diff --git a/agon/cogs/commands/submission.py b/agon/cogs/commands/submission.py index 24f3ad3..45ff971 100644 --- a/agon/cogs/commands/submission.py +++ b/agon/cogs/commands/submission.py @@ -18,13 +18,112 @@ from agon.utils import autofill_categories, autofill_groups, submit_score class SubmissionCog(commands.Cog): """Class holds command for submitting a score.""" - 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: PLR0911, PLR0913 + async def submit(self, interaction: nextcord.Interaction) -> None: + """Parent slash command for submitting scores. Never called.""" + + @submit.subcommand(name="int", description="Submit an integer type score.") + async def integer( + self, + interaction: nextcord.Interaction, + group_id: str = nextcord.SlashOption(name="group", required=True, description="Group to submit score to."), + category_id: str = nextcord.SlashOption( + name="category", required=True, description="Category to submit score to." + ), + score: int = nextcord.SlashOption(required=True, description="Score to submit", min_value=0), + proof: nextcord.Attachment = nextcord.SlashOption( + required=False, description="Proof for submission. Only needed if score requires verification." + ), + username: str = nextcord.SlashOption( + required=False, description="Modified username if submitting under not your discord name." + ), + ) -> None: + """ + Submit a highscore. + + Sets the username if one isn't passed in. Checks if a submission needs verified. Ensures that the score is + following the correct format. Then either creates the verification request or submits score if verification + isn't required. + + Args: + interaction (nextcord.Interaction): Interaction of the command. + group_id (str): Is SlashOption. Group that the score category belongs in. + category_id (str): Is SlashOption. Category to submit the score in. + score (str): Is SlashOption. The score. + proof (nextcord.Attachment): Is SlashOption. Picture to provide proof of score. + username (str, optional): Is SlashOption. Username to associate with the score. + """ + # fill out username if not passed in + if username is None: + username = interaction.user.display_name + + with Session(engine) as session: + stmt = ( + select(Category) + .where(col(Category.group_id) == int(group_id)) + .where(col(Category.id) == int(category_id)) + ) + 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.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 + + # Create submission verification request or submit score if verification not needed. + if not category.is_validated: + await submit_score(interaction, int(group_id), int(category_id), username, score) + await interaction.response.send_message("Score has been submitted!", ephemeral=True) + return + + stmt = select(col(Guild.verification_channel)).where(col(Guild.id) == interaction.guild_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 := 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 + + if not isinstance(channel, nextcord.TextChannel | nextcord.Thread): + await interaction.response.send_message("An error happened with submission.") + agon_logger.error( + "Channel id %s in guild %s is does not correspond to a TextChannel.", channel.id, channel.guild.id + ) + return + + # Send the verification message to admins + verification_message = await channel.send( + file=await proof.to_file(), + view=VerificationView(), + embeds=[self.create_embed(interaction, group_id, category_id, score, username, session)], + content=f"Submission from <@{interaction.user.id}>.", + ) + await interaction.response.send_message(content="Score submitted!", ephemeral=True) + + # Save the submission data for later use. + submission: Submission = Submission( + category_id=int(category_id), + group_id=int(group_id), + guild_id=interaction.guild_id, + message_id=verification_message.id, + score=score, + user_id=interaction.user.id, + username=username, + ) + + session.add(submission) + session.commit() + + @nextcord.slash_command() + async def old_submit( # noqa: PLR0911, PLR0913 self, interaction: nextcord.Interaction, group_id: str = nextcord.SlashOption(name="group", required=True, description="Group to submit score to."), @@ -136,7 +235,7 @@ class SubmissionCog(commands.Cog): interaction: nextcord.Interaction, group_id: str, category_id: str, - score: str, + score: str | float, username: str, session: Session, ) -> nextcord.Embed: @@ -147,7 +246,7 @@ class SubmissionCog(commands.Cog): interaction (nextcord.Interaction): Discord interaction of the command. group_id (str): Used to get group name. category_id (str): Used to get category name. - score (str): Score to set. + score (str | float): Score to set. username (str): Username to set. session (Session): Open connection to database. @@ -170,6 +269,8 @@ class SubmissionCog(commands.Cog): 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) + if not isinstance(score, str): + score = str(score) embed.add_field(name="Score", value=score, inline=False) return embed