Compare commits

...
13 Commits
11 changed files with 132 additions and 120 deletions
+2 -1
View File
@@ -5,11 +5,12 @@
"highscore",
"highscores",
"imcompiling",
"iterrows",
"nextcord",
"ondelete"
],
"python.analysis.diagnosticSeverityOverrides": {
"reportOptionalMemberAccess": false,
"reportOptionalMemberAccess": "warning",
"reportArgumentType": "information",
"reportAttributeAccessIssue": "warning"
}
+1 -1
View File
@@ -11,7 +11,7 @@ class HelpCog(commands.Cog, name="Help commands."):
"""Default constructor for HelpCog class. Does nothing."""
@nextcord.slash_command()
async def help(self, interaction: nextcord.Interaction):
async def help(self, interaction: nextcord.Interaction) -> None:
"""Parent slash command for help. Never called."""
@help.subcommand(description="Describes how to get started with Agon.")
+8 -8
View File
@@ -1,13 +1,13 @@
"""
Commands for refreshing highscore messages.
"""
"""Commands for refreshing highscore messages."""
import nextcord
from nextcord.ext import commands
from sqlalchemy import select
from sqlalchemy.orm import Session
from sqlmodel import col
from database.db import CategoryGroup, engine
from database.db import engine
from database.models import CategoryGroup
from utilities.utils import autofill_groups, send_highscore_message
@@ -43,13 +43,13 @@ class RefreshCog(commands.Cog, name="Refresh Cog"):
with Session(engine) as session:
stmt = (
select(CategoryGroup)
.where(CategoryGroup.GuildId == interaction.guild_id)
.where(CategoryGroup.GroupId == int(group_id))
.where(col(CategoryGroup.guild_id) == interaction.guild_id)
.where(col(CategoryGroup.id) == int(group_id))
)
group = session.scalar(stmt)
await send_highscore_message(session, interaction.guild, group)
await interaction.response.send_message(f"Updated group {group.GroupName}!", ephemeral=True)
await interaction.response.send_message(f"Updated group {group.name}!", ephemeral=True)
@refresh.subcommand()
async def all(self, interaction: nextcord.Interaction) -> None:
@@ -67,7 +67,7 @@ class RefreshCog(commands.Cog, name="Refresh Cog"):
interaction (nextcord.Interaction): Interaction that invoked the command.
"""
with Session(engine) as session:
stmt = select(CategoryGroup).where(CategoryGroup.GuildId == interaction.guild_id)
stmt = select(CategoryGroup).where(col(CategoryGroup.guild_id) == interaction.guild_id)
# awaiting response because posting scores takes a while
await interaction.response.defer(ephemeral=True)
+47 -44
View File
@@ -1,31 +1,25 @@
"""
Set command heirarchy.
"""
"""Set command hierarchy."""
import nextcord
from nextcord.ext import commands
from sqlalchemy import select
from sqlalchemy.orm import Session
from sqlmodel import col
from database.db import CategoryGroup, Guild, engine
from database.db import engine
from database.models import CategoryGroup, Guild
from utilities.utils import MAX_LINE_LENGTH, autofill_groups
class SetCog(commands.Cog, name="Set command."):
"""
Class holds commands for setting variables in guild's database.
"""
"""Class holds commands for setting variables in guild's database."""
def __init__(self) -> None:
"""
Default constructor for SetCog class. Does nothing.
"""
"""Default constructor for SetCog class. Does nothing."""
@nextcord.slash_command(default_member_permissions=nextcord.Permissions(administrator=True))
async def set(self, interaction: nextcord.Interaction):
"""
Parent slash command for setting settings. Never called.
"""
async def set(self, interaction: nextcord.Interaction) -> None:
"""Parent slash command for setting settings. Never called."""
@set.subcommand(description="Registers the channel for a Category Group's messages.")
async def group_output_channel(
@@ -50,17 +44,18 @@ class SetCog(commands.Cog, name="Set command."):
channel (nextcord.abc.GuildChannel, optional): Channel to register
Is a SlashOption. Limited to text channels, Defaults to None.
"""
if not channel:
channel = interaction.channel
channel_id = channel.id
channel_id = interaction.channel.id if not channel else channel.id
with Session(engine) as session:
stmt = (
select(CategoryGroup)
.where(CategoryGroup.GuildId == interaction.guild_id)
.where(CategoryGroup.GroupId == int(group_id))
.where(col(CategoryGroup.guild_id) == interaction.guild_id)
.where(col(CategoryGroup.id) == int(group_id))
)
group = session.scalar(stmt)
group.ChannelId = channel_id
if (group := session.scalar(stmt)) is None:
await interaction.response.send_message("Group or Category was not found.", ephemeral=True)
return
group.channel_id = channel_id
session.commit()
await interaction.send(f"Registered <#{channel_id}>.", ephemeral=True)
@@ -92,14 +87,17 @@ class SetCog(commands.Cog, name="Set command."):
with Session(engine) as session:
stmt = (
select(CategoryGroup)
.where(CategoryGroup.GuildId == interaction.guild_id)
.where(CategoryGroup.GroupId == int(group_id))
.where(col(CategoryGroup.guild_id) == interaction.guild_id)
.where(col(CategoryGroup.id) == int(group_id))
)
group = session.scalar(stmt)
group.PadLength = length
if (group := session.scalar(stmt)) is None:
await interaction.response.send_message("Group or Category was not found.", ephemeral=True)
return
group.pad_length = length
session.commit()
await interaction.response.send_message(
f"Category pad length set to {length} for {group.GroupName}", ephemeral=True
f"Category pad length set to {length} for {group.name}", ephemeral=True
)
@set.subcommand(
@@ -120,17 +118,17 @@ class SetCog(commands.Cog, name="Set command."):
Args:
interaction (nextcord.Interaction): Interaction that invoked the command
channel Optional[nextcord.abc.GuildChannel], optional): Channel to register
channel (nextcord.abc.GuildChannel): Channel to register
Is a SlashOption. Limited to text channels, Defaults to None.
"""
if not channel:
channel = interaction.channel
channel_id = interaction.channel.id if not channel else channel.id
with Session(engine) as session:
channel_id = channel.id
stmt = select(Guild).where(Guild.GuildId == interaction.guild_id)
guild = session.scalar(stmt)
guild.SubmissionChannel = channel_id
stmt = select(Guild).where(col(Guild.id) == interaction.guild_id)
if (guild := session.scalar(stmt)) is None:
await interaction.response.send_message("Guild was not found.", ephemeral=True)
return
guild.submission_channel = channel_id
session.commit()
await interaction.send(f"Registered <#{channel_id}>.", ephemeral=True)
@@ -150,16 +148,20 @@ class SetCog(commands.Cog, name="Set command."):
Args:
interaction (nextcord.Interaction): Interaction that invoked the command
group_id (str): String representation of Group id to set username length in. Is SlashOption.
length (int): Number of characters. Is SlashOption. Defaults to 12.
"""
with Session(engine) as session:
stmt = (
select(CategoryGroup)
.where(CategoryGroup.GuildId == interaction.guild_id)
.where(CategoryGroup.GroupId == int(group_id))
.where(col(CategoryGroup.guild_id) == interaction.guild_id)
.where(col(CategoryGroup.id) == int(group_id))
)
group = session.scalar(stmt)
group.UsernameLength = length
if (group := session.scalar(stmt)) is None:
await interaction.response.send_message("Group or Category was not found.", ephemeral=True)
return
group.username_length = length
session.commit()
await interaction.response.send_message(f"Username length set to {length}.", ephemeral=True)
@@ -182,20 +184,21 @@ class SetCog(commands.Cog, name="Set command."):
channel (Optional[nextcord.abc.GuildChannel]): Channel to register
Is a SlashOption. Limited to text channels, Defaults to None.
"""
if not channel:
channel = interaction.channel
channel_id = channel.id
channel_id = interaction.channel.id if not channel else channel.id
with Session(engine) as session:
stmt = select(Guild).where(Guild.GuildId == interaction.guild_id)
guild = session.scalar(stmt)
guild.VerificationChannel = channel_id
stmt = select(Guild).where(col(Guild.id) == interaction.guild_id)
if (guild := session.scalar(stmt)) is None:
await interaction.response.send_message("Guild was not found.", ephemeral=True)
return
guild.verification_channel = channel_id
session.commit()
await interaction.send(f"Registered <#{channel_id}>.", ephemeral=True)
@group_output_channel.on_autocomplete("group_id")
@pad_length.on_autocomplete("group_id")
@username_length.on_autocomplete("group_id")
async def group_autocomplete(self, interaction: nextcord.Interaction, group_id: str):
async def group_autocomplete(self, interaction: nextcord.Interaction, group_id: str) -> None:
"""
Sends results from autofill_groups back to group_output_channel for autocompletion of group_id.
+38 -28
View File
@@ -6,9 +6,11 @@ import nextcord
from nextcord.ext import commands
from sqlalchemy import select
from sqlalchemy.orm import Session
from sqlmodel import col
from cogs.submission.verification_view import VerificationView
from database.db import Category, CategoryGroup, Guild, Submission, engine
from database.db import engine
from database.models import Category, CategoryGroup, Guild, Submission
from main import logger
from utilities.utils import autofill_categories, autofill_groups, submit_score
@@ -19,8 +21,10 @@ class SubmissionCog(commands.Cog):
def __init__(self) -> None:
return
# TODO(imcompiling): fix too many return statements when reworking command.
# https://gitea.cheezers.org/imcompiling/agon/issues/76
@nextcord.slash_command()
async def submit( # noqa: PLR0913
async def submit( # noqa: PLR0911, PLR0913
self,
interaction: nextcord.Interaction,
group_id: str = nextcord.SlashOption(name="group", required=True, description="Group to submit score to."),
@@ -56,40 +60,46 @@ class SubmissionCog(commands.Cog):
with Session(engine) as session:
stmt = (
select(Category).where(Category.GroupId == int(group_id)).where(Category.CategoryId == int(category_id))
select(Category)
.where(col(Category.group_id) == int(group_id))
.where(col(Category.id) == int(category_id))
)
category: Category = session.scalar(stmt)
if (category := session.scalar(stmt)) is None:
await interaction.response.send_message("Category not found.", ephemeral=True)
return
# check if verification is required. if no picture, deny
if category.IsValidated and proof is None:
if category.is_validated and proof is None:
await interaction.response.send_message(
"This category required verification. Please attach a picture and re-submit.", ephemeral=True
)
return
# check with regex if score is in correct format
verified_score = await self.verify_score(interaction, score, is_time_record=category.IsTimeRecord)
# check if verify_score was successful. It replies with corrections required itself.
if verified_score is None:
if (
verified_score := await self.verify_score(interaction, score, is_time_record=category.is_time_record)
) is None:
# This response message will be filled out better once command is split up to
# know which type it expects...
await interaction.response.send_message("Format was incorrect.", ephemeral=True)
return
# Create submission verification request or submit score if verification not needed.
if not category.IsValidated:
if not category.is_validated:
await submit_score(interaction, int(group_id), int(category_id), username, verified_score)
await interaction.response.send_message("Score has been submitted!", ephemeral=True)
return
stmt = select(Guild).where(Guild.GuildId == interaction.guild_id)
verification_channel_id: int = session.scalar(stmt).VerificationChannel
stmt = select(col(Guild.verification_channel)).where(col(Guild.id) == interaction.guild_id)
channel = interaction.guild.get_channel(verification_channel_id)
if (verification_channel_id := session.scalar(stmt)) is None:
error_response = "No verification channel was registered. Contact server admins."
await interaction.response.send_message(error_response, ephemeral=True)
return
if channel is None:
error_response = (
"Registered Verification channel does not exist or was never registered. Contact server admins."
)
if (channel := interaction.guild.get_channel(verification_channel_id)) is None:
error_response = "Registered Verification channel does not exist. Contact server admins."
await interaction.response.send_message(error_response, ephemeral=True)
return
@@ -111,13 +121,13 @@ class SubmissionCog(commands.Cog):
# Save the submission data for later use.
submission: Submission = Submission(
CategoryId=category_id,
GroupId=group_id,
GuildId=interaction.guild_id,
MessageId=verification_message.id,
Score=verified_score,
UserId=interaction.user.id,
UserName=username,
category_id=int(category_id),
group_id=int(group_id),
guild_id=interaction.guild_id,
message_id=verification_message.id,
score=verified_score,
user_id=interaction.user.id,
username=username,
)
session.add(submission)
@@ -151,15 +161,15 @@ class SubmissionCog(commands.Cog):
# Get group name from database
stmt = (
select(CategoryGroup.GroupName)
.where(CategoryGroup.GuildId == interaction.guild_id)
.where(CategoryGroup.GroupId == group_id)
select(col(CategoryGroup.name))
.where(col(CategoryGroup.guild_id) == interaction.guild_id)
.where(col(CategoryGroup.id) == group_id)
)
embed.add_field(name="Group", value=session.scalar(stmt), inline=False)
# Get category name from database
stmt = (
select(Category.CategoryName).where(Category.GroupId == group_id).where(Category.CategoryId == category_id)
select(col(Category.name)).where(col(Category.group_id) == group_id).where(col(Category.id) == category_id)
)
embed.add_field(name="Category", value=session.scalar(stmt), inline=False)
embed.add_field(name="Score", value=score, inline=False)
+20 -21
View File
@@ -1,22 +1,20 @@
"""
Commands for updating information/variables held in database.
"""
"""Commands for updating information/variables held in database."""
import nextcord
from nextcord.ext import commands
from sqlalchemy import select
from sqlalchemy.orm import Session
from sqlmodel import col
from database.db import Guild, engine
from database.db import engine
from database.models import Guild
from utilities.utils import autofill_users_in_guild
from .views.update_username_verify import UpdateUsernameVerifyView
class UpdateCog(commands.Cog, name="Update commands."):
"""
Class holds commands and views needed for updating things in bot.
"""
"""Class holds commands and views needed for updating things in bot."""
def __init__(self, bot: commands.Bot):
"""
@@ -28,17 +26,13 @@ class UpdateCog(commands.Cog, name="Update commands."):
self._bot: commands.Bot = bot
@commands.Cog.listener()
async def on_ready(self):
"""
Called when cog is loaded. Adds views required for DataManagement commands.
"""
async def on_ready(self) -> None:
"""Called when cog is loaded. Adds views required for DataManagement commands."""
self._bot.add_view(UpdateUsernameVerifyView())
@nextcord.slash_command()
async def update(self, interaction: nextcord.Interaction):
"""
Parent slash command for updating things. Never called.
"""
async def update(self, interaction: nextcord.Interaction) -> None:
"""Parent slash command for updating things. Never called."""
@update.subcommand(description="Request a username update. Admins must approve.")
async def username(
@@ -46,10 +40,11 @@ class UpdateCog(commands.Cog, name="Update commands."):
interaction: nextcord.Interaction,
old_username: str = nextcord.SlashOption(description="Username currently stored in highscores.", required=True),
new_username: str = nextcord.SlashOption(description="Username to update to.", required=True),
):
) -> None:
"""
Creates a request to send to admins for changing a username. Uses
UpdateUsernameVerifyView to do the work.
Creates a request to send to admins for changing a username.
Uses UpdateUsernameVerifyView to do the work.
Args:
interaction (nextcord.Interaction): Interaction that invoked the command
@@ -57,9 +52,13 @@ class UpdateCog(commands.Cog, name="Update commands."):
new_username (str): Username to replace old with.
"""
with Session(engine) as session:
stmt = select(Guild.VerificationChannel).where(Guild.GuildId == interaction.guild_id)
stmt = select(col(Guild.verification_channel)).where(col(Guild.id) == interaction.guild_id)
verify_channel = interaction.guild.get_channel(session.scalar(stmt))
if not isinstance(verify_channel, nextcord.TextChannel):
await interaction.send("Verification channel not set. Cannot process request.", ephemeral=True)
return
embed: nextcord.Embed = nextcord.Embed(title="Username change request.")
embed.add_field(name="Old Username", value=old_username)
embed.add_field(name="New Username", value=new_username)
@@ -71,12 +70,12 @@ class UpdateCog(commands.Cog, name="Update commands."):
await interaction.send("Request sent to admins for verification.", ephemeral=True)
@username.on_autocomplete("old_username")
async def all_users_autocomplete(self, interaction: nextcord.Interaction, username: str):
async def all_users_autocomplete(self, interaction: nextcord.Interaction, username: str) -> None:
"""
Sends response from autofill_users_in_guild back to update_username for autocompletion of old_username.
Args:
interaction (nextcord.Interaction): Interaction with command.
old_username (str): String to run autocompletion against.
username (str): String to run autocompletion against.
"""
await interaction.response.send_autocomplete(autofill_users_in_guild(interaction.guild_id, username))
+9 -7
View File
@@ -5,8 +5,10 @@ import pandas as pd
from nextcord.ext import commands
from sqlalchemy import select
from sqlalchemy.orm import Session
from sqlmodel import col
from database.db import Category, CategoryGroup, Score, engine
from database.db import engine
from database.models import Category, CategoryGroup, Score
from utilities.utils import MAX_LINE_LENGTH, autofill_categories, autofill_groups, format_score_string
@@ -46,17 +48,17 @@ class ViewCog(commands.Cog, name="View Command"):
# get group, category, and all scores sorted correctly.
with Session(engine) as session:
stmt = select(CategoryGroup).where(CategoryGroup.GroupId == int(group_id))
stmt = select(CategoryGroup).where(col(CategoryGroup.id) == int(group_id))
group = session.scalar(stmt)
stmt = select(Category).where(Category.CategoryId == int(category_id))
stmt = select(Category).where(col(Category.id) == int(category_id))
category = session.scalar(stmt)
d_f = pd.read_sql_query(
sql=session.query(Score).where(Score.CategoryId == category_id).statement, con=engine
sql=session.query(Score).where(col(Score.category_id) == category_id).statement, con=engine
)
scores = d_f.sort_values("Score", ascending=category.IsAscending).reset_index()
scores = d_f.sort_values("Score", ascending=category.is_ascending).reset_index()
# get the longest string
longest_str: int = scores.UserName.map(len).max()
@@ -67,13 +69,13 @@ class ViewCog(commands.Cog, name="View Command"):
row["UserName"],
row["Score"],
longest_str,
category.IsTimeRecord,
category.is_time_record,
i + 1, # type: ignore[reportOperatorIssue]
MAX_LINE_LENGTH,
)
)
message_str: str = f"**{group.GroupName} {category.CategoryName} Leaderboard!**\n```"
message_str: str = f"**{group.name} {category.name} Leaderboard!**\n```"
if len(str_list) == 0:
message_str += "No Submissions!"
else:
+1 -2
View File
@@ -26,10 +26,9 @@ class GuildListener(commands.Cog, name="Guild Listener"):
"""
with Session(engine) as session:
stmt = select(Guild).where(col(Guild.id == guild.id))
guild_data = session.scalar(stmt)
# just in case check if the guild already exists
if guild_data is None:
if (guild_data := session.scalar(stmt)) is None:
new_guild = Guild(id=guild.id, name=guild.name)
session.add(new_guild)
else:
+2 -2
View File
@@ -31,7 +31,7 @@ class GuildData(commands.Cog, name="Guild Data"):
with Session(engine) as session:
for guild in self.bot.guilds:
# update given guild name for guild id
stmt = select(Guild).where(col(Guild.id == guild.id))
stmt = select(Guild).where(col(Guild.id) == guild.id)
guild_data = session.scalar(stmt)
# just in case checking if the guild doesn't exist.
@@ -39,6 +39,6 @@ class GuildData(commands.Cog, name="Guild Data"):
new_guild = Guild(id=guild.id, name=guild.name)
session.add(new_guild)
else:
guild_data.GuildName = guild.name
guild_data.name = guild.name
session.commit()
+1 -3
View File
@@ -39,9 +39,7 @@ class VerificationView(nextcord.ui.View):
.where(col(Submission.message_id) == interaction.message.id)
)
submission = session.scalar(stmt)
if submission is None:
if (submission := session.scalar(stmt)) is None:
await interaction.response.send_message("Could not find submission data in database.", ephemeral=True)
await interaction.message.delete()
return
+3 -3
View File
@@ -64,7 +64,7 @@ def autofill_groups(guild_id: int, group_begin: str) -> "dict[str, str]":
"""
with Session(engine) as session:
# Select all groups in guild.
stmt = select(CategoryGroup).wherecol((CategoryGroup.guild_id) == guild_id)
stmt = select(CategoryGroup).where(col(CategoryGroup.guild_id) == guild_id)
# dict has to have key values of same type for discord.
group_dict: dict[str, str] = {group.name: str(group.id) for group in session.scalars(stmt)}
@@ -347,7 +347,7 @@ async def send_highscore_message(session: Session, guild: nextcord.Guild, group:
if group.message_id is None:
# message doesn't exist.
message: nextcord.Message = await channel.send(highscore_string)
group.MessageId = message.id
group.message_id = message.id
session.commit()
else:
message: nextcord.Message = await channel.fetch_message(group.message_id)
@@ -390,7 +390,7 @@ async def submit_score(
existing_score = session.scalar(stmt)
if existing_score:
existing_score.Score = score_f
existing_score.score = score_f
else:
score_obj = Score(username=username, score=score_f, category_id=category_id)
session.add(score_obj)