Compare commits

..
12 Commits
Author SHA1 Message Date
imcompiling 0c0997624f Remove guild ids.
build_develop / build (push) Successful in 39s
build_release / build (push) Successful in 21s
2025-02-20 20:29:16 -05:00
imcompiling 67090e0d6f Remove submission from table once handled by admins.
build_develop / build (push) Successful in 32s
build_release / build (push) Successful in 21s
2025-02-20 17:53:14 -05:00
imcompiling 385129c434 Fix smells in view.py. 2025-02-20 17:31:03 -05:00
imcompiling 327126307d Remove all references to table. Remove a dead function. Fix some code smells. 2025-02-20 17:23:57 -05:00
imcompiling 36dad683d8 Remove reference to boss_name. Fix a few smells. 2025-02-20 17:13:19 -05:00
imcompiling 9be9279239 Update the help setup command.
build_develop / build (push) Successful in 37s
build_release / build (push) Successful in 21s
2025-02-20 16:57:56 -05:00
imcompiling ffc5c20fec Add deprecation notes to SubmissionChannel. Keeping around just in case. 2025-02-20 16:50:10 -05:00
imcompiling 65453eb7d6 Edit verification view to use the database for information. 2025-02-20 16:48:18 -05:00
imcompiling 2ad1c808c2 Add a todo to rework how approval interacts with information. 2025-02-20 11:46:55 -05:00
imcompiling 3898e5e494 Fix types in database, add UserId, fix file usage in submission command. 2025-02-20 11:46:22 -05:00
imcompiling b16132d61b Add submission command to init cog. Clean up init cog. 2025-02-20 11:27:20 -05:00
imcompiling d3aaf642bd Add isinstance check for text channel. 2025-02-20 11:08:03 -05:00
12 changed files with 128 additions and 150 deletions
+1 -1
View File
@@ -6,6 +6,6 @@
"python.analysis.diagnosticSeverityOverrides": { "python.analysis.diagnosticSeverityOverrides": {
"reportOptionalMemberAccess": false, "reportOptionalMemberAccess": false,
"reportArgumentType": "information", "reportArgumentType": "information",
"reportAttributeAccessIssue": "information" "reportAttributeAccessIssue": "warning"
} }
} }
+5 -7
View File
@@ -1,6 +1,4 @@
""" """Cog loader. Adds all command cogs to the bot."""
Cog loader. Adds all command cogs to the bot.
"""
from nextcord.ext import commands from nextcord.ext import commands
@@ -9,20 +7,20 @@ from .delete import DeleteCog
from .help import HelpCog from .help import HelpCog
from .refresh import RefreshCog from .refresh import RefreshCog
from .set import SetCog from .set import SetCog
from .submission import SubmissionCog
from .update import UpdateCog from .update import UpdateCog
from .view import ViewCog from .view import ViewCog
# This function will be called when this extension is loaded. # This function will be called when this extension is loaded.
# It is necessary to add these functions to the bot. # It is necessary to add these functions to the bot.
def setup(bot: commands.Bot): def setup(bot: commands.Bot) -> None:
""" """Adds all command classes to bot."""
Adds all command classes to bot.
"""
bot.add_cog(CreateCog(bot)) bot.add_cog(CreateCog(bot))
bot.add_cog(DeleteCog()) bot.add_cog(DeleteCog())
bot.add_cog(HelpCog()) bot.add_cog(HelpCog())
bot.add_cog(RefreshCog(bot)) bot.add_cog(RefreshCog(bot))
bot.add_cog(SetCog()) bot.add_cog(SetCog())
bot.add_cog(SubmissionCog())
bot.add_cog(UpdateCog(bot)) bot.add_cog(UpdateCog(bot))
bot.add_cog(ViewCog()) bot.add_cog(ViewCog())
+1 -1
View File
@@ -147,7 +147,7 @@ class CreateCog(commands.Cog, name="Create Cog"):
@category.on_autocomplete("group_id") @category.on_autocomplete("group_id")
async def group_autocomplete(self, interaction: nextcord.Interaction, group_id: str) -> None: async def group_autocomplete(self, interaction: nextcord.Interaction, group_id: str) -> None:
""" """
Collects list of table names and compares to input table name for autocomplete assistance. Collects list of group names and compares to input group name for autocomplete assistance.
Args: Args:
interaction (nextcord.Interaction): Interaction with command. interaction (nextcord.Interaction): Interaction with command.
+10 -10
View File
@@ -18,7 +18,7 @@ from utilities.utils import (
from .views.delete_confirm import DeleteConfirmView, create_delete_id from .views.delete_confirm import DeleteConfirmView, create_delete_id
class DeleteCog(commands.Cog, name="Table Management"): class DeleteCog(commands.Cog, name="Delete Cog"):
"""Class holds commands for deleting.""" """Class holds commands for deleting."""
def __init__(self) -> None: def __init__(self) -> None:
@@ -32,7 +32,7 @@ class DeleteCog(commands.Cog, name="Table Management"):
async def all(self, interaction: nextcord.Interaction) -> None: async def all(self, interaction: nextcord.Interaction) -> None:
"""Subcommand parent for deleting all of a thing. Never called.""" """Subcommand parent for deleting all of a thing. Never called."""
@all.subcommand(description="Clears highscores channel of all table messages. Does not change data.") @all.subcommand(description="Clears highscores channel of all highscore messages. Does not change data.")
async def channel_messages( async def channel_messages(
self, self,
interaction: nextcord.Interaction, interaction: nextcord.Interaction,
@@ -69,16 +69,16 @@ class DeleteCog(commands.Cog, name="Table Management"):
for group in groups: for group in groups:
try: try:
table_message: nextcord.Message = await channel.fetch_message(group.MessageId) message: nextcord.Message = await channel.fetch_message(group.MessageId)
await table_message.delete() await message.delete()
except nextcord.NotFound: except nextcord.NotFound:
# silently ignore that messages are not found. They got deleted already. # silently ignore that messages are not found. They got deleted already.
pass pass
await interaction.send("Table messages deleted.", ephemeral=True) await interaction.send("Highscore messages deleted.", ephemeral=True)
@all.subcommand( @all.subcommand(
description="Removes all of a user's scores. Warning: data is destroyed, use with case.", description="Removes all of a user's scores. Warning: data is destroyed, use with care.",
) )
async def users( async def users(
self, self,
@@ -90,7 +90,7 @@ class DeleteCog(commands.Cog, name="Table Management"):
""" """
Searches all of guild's scores data for any score from the user and removes. Searches all of guild's scores data for any score from the user and removes.
Updates table message is user was removed from that table. Updates highscore message if user was removed from that Category Group.
Args: Args:
interaction (nextcord.Interaction): Interaction that invoked the command. interaction (nextcord.Interaction): Interaction that invoked the command.
@@ -112,7 +112,7 @@ class DeleteCog(commands.Cog, name="Table Management"):
for group in session.scalars(stmt): for group in session.scalars(stmt):
await send_highscore_message(session, interaction.guild, group) await send_highscore_message(session, interaction.guild, group)
await interaction.send(f'User "{user_name}" was removed from all tables.', ephemeral=True) await interaction.send(f'User "{user_name}" was removed from all Category Groups.', ephemeral=True)
@delete.subcommand(description="Delete a category from a group. Deletion is perminant.") @delete.subcommand(description="Delete a category from a group. Deletion is perminant.")
async def category( async def category(
@@ -225,7 +225,7 @@ class DeleteCog(commands.Cog, name="Table Management"):
@group.on_autocomplete("group_id") @group.on_autocomplete("group_id")
async def group_autocomplete(self, interaction: nextcord.Interaction, group_id: str) -> None: async def group_autocomplete(self, interaction: nextcord.Interaction, group_id: str) -> None:
""" """
Autocomplete function for table names. Autocomplete function for group names.
Args: Args:
interaction (nextcord.Interaction): Interaction that invoked the command. interaction (nextcord.Interaction): Interaction that invoked the command.
@@ -264,7 +264,7 @@ class DeleteCog(commands.Cog, name="Table Management"):
""" """
await interaction.response.send_autocomplete( await interaction.response.send_autocomplete(
autofill_users_in_group_category( autofill_users_in_group_category(
interaction.guild_id, int(group_id), category_id if category_id else 0, user_name interaction.guild_id, int(group_id), int(category_id) if category_id else 0, user_name
) )
) )
+12 -22
View File
@@ -1,29 +1,21 @@
""" """Commands for helping a user."""
Commands for helping a user.
"""
import nextcord import nextcord
from nextcord.ext import commands from nextcord.ext import commands
class HelpCog(commands.Cog, name="Help commands."): class HelpCog(commands.Cog, name="Help commands."):
""" """Class holds commands for getting help."""
Class holds commands for getting help.
"""
def __init__(self) -> None: def __init__(self) -> None:
""" """Default constructor for HelpCog class. Does nothing."""
Default constructor for HelpCog class. Does nothing.
"""
@nextcord.slash_command() @nextcord.slash_command()
async def help(self, interaction: nextcord.Interaction): async def help(self, interaction: nextcord.Interaction):
""" """Parent slash command for help. Never called."""
Parent slash command for help. Never called.
"""
@help.subcommand(description="Describes how to get started with Agon.") @help.subcommand(description="Describes how to get started with Agon.")
async def setup(self, interaction: nextcord.Interaction): async def setup(self, interaction: nextcord.Interaction) -> None:
""" """
Sends a message describing how to get started with Agon. Sends a message describing how to get started with Agon.
@@ -31,15 +23,13 @@ class HelpCog(commands.Cog, name="Help commands."):
interaction (nextcord.Interaction): Interaction that invoked the command. interaction (nextcord.Interaction): Interaction that invoked the command.
""" """
# I hate this but don't want indents in the string. # I hate this but don't want indents in the string.
# pylint: disable=line-too-long
help_str: str = """Welcome to Agon! Three simple steps are needed to get started.
1.) Set a channel to recieve submission verifications with "/set verify_channel."
This channel is used to allow moderators to check if a submission is real/valid.
2.) Set a channel for submissions to be posted in with "/set submission_channel." help_str: str = "Welcome to Agon! Three simple steps are needed to get started."
This channel will be where all user submission messages are sent. help_str += '1.) Set a channel to receive submission verifications with "/set verify_channel."\n'
help_str += "This channel is used to allow moderators to check if a submission is real/valid.\n\n"
3.) Set up your highscore tables! Use "/create group" to first create a group message in the channel you would like and then use "/create category" to create a category in that group message!""" help_str += '2.) Set up your highscores! Use "/create group" to first create a group message in the'
# pylint: enable=line-too-long help_str += 'channel you would like and then use "/create category" to create a category in that group message!'
help_str += "\n\n"
help_str += '3.) Users can then submit scores with the "/submit" command.'
await interaction.response.send_message(content=help_str) await interaction.response.send_message(content=help_str)
+13 -20
View File
@@ -14,13 +14,11 @@ from utilities.utils import autofill_groups, send_highscore_message
class RefreshCog(commands.Cog, name="Refresh Cog"): class RefreshCog(commands.Cog, name="Refresh Cog"):
""" """Class holds commands for refreshing."""
Class holds commands for refreshing.
"""
def __init__(self, bot: commands.Bot) -> None: def __init__(self, bot: commands.Bot) -> None:
""" """
Default constructor for RefreshCog class Default constructor for RefreshCog class.
Args: Args:
bot (commands.Bot): Bot object. Required to register submission button view. bot (commands.Bot): Bot object. Required to register submission button view.
@@ -28,19 +26,17 @@ class RefreshCog(commands.Cog, name="Refresh Cog"):
self._bot = bot self._bot = bot
@nextcord.slash_command(default_member_permissions=nextcord.Permissions(administrator=True)) @nextcord.slash_command(default_member_permissions=nextcord.Permissions(administrator=True))
async def refresh(self, interaction: nextcord.Interaction): async def refresh(self, interaction: nextcord.Interaction) -> None:
""" """Parent slash command for refreshing things. Never called."""
Parent slash command for refreshing things. Never called.
"""
@refresh.subcommand(description="Manually refresh a table. Use when changing settings or data manually changed.") @refresh.subcommand(description="Manually refresh a group. Use when changing settings or data manually changed.")
async def group( async def group(
self, self,
interaction: nextcord.Interaction, interaction: nextcord.Interaction,
group_id: str = nextcord.SlashOption(name="group", description="Name of Group.", required=True), group_id: str = nextcord.SlashOption(name="group", description="Name of Group.", required=True),
) -> None: ) -> None:
""" """
Refreshes a table given by user. Refreshes a group given by user.
Args: Args:
interaction (nextcord.Interaction): Interaction that invoked the command. interaction (nextcord.Interaction): Interaction that invoked the command.
@@ -57,22 +53,19 @@ class RefreshCog(commands.Cog, name="Refresh Cog"):
message: nextcord.Message = await send_highscore_message(session, interaction.guild, group) message: nextcord.Message = await send_highscore_message(session, interaction.guild, group)
await message.edit(view=SubmissionCreateButton(self._bot)) await message.edit(view=SubmissionCreateButton(self._bot))
await interaction.response.send_message(f"Updated table {group.GroupName}!", ephemeral=True) await interaction.response.send_message(f"Updated group {group.GroupName}!", ephemeral=True)
@refresh.subcommand() @refresh.subcommand()
async def all(self, interaction: nextcord.Interaction): async def all(self, interaction: nextcord.Interaction) -> None:
""" """Subcommand parent for refreshing all things. Never called."""
Subcommand parent for refreshing all things. Never called.
"""
@all.subcommand(description="Triggers post/update of highscores to registered highscore channels.") @all.subcommand(description="Triggers post/update of highscores to registered highscore channels.")
async def groups(self, interaction: nextcord.Interaction) -> None: async def groups(self, interaction: nextcord.Interaction) -> None:
""" """
A command which posts the highscore in the registered channel. A command which posts the highscore in the registered channel.
Sends warning if no channel has been registered.
Either creates new messages for each table if one doesn't exist Sends warning if no channel has been registered. Either creates new messages for each group if one doesn't
or edits the existing message. exist or edits the existing message.
Args: Args:
interaction (nextcord.Interaction): Interaction that invoked the command. interaction (nextcord.Interaction): Interaction that invoked the command.
@@ -90,9 +83,9 @@ class RefreshCog(commands.Cog, name="Refresh Cog"):
await interaction.send(f"Posted all groups in {interaction.guild.name}'s highscores.", ephemeral=True) await interaction.send(f"Posted all groups in {interaction.guild.name}'s highscores.", ephemeral=True)
@group.on_autocomplete("group_id") @group.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:
""" """
Collects list of table names and compares to input table name for autocomplete assistance. Collects list of group names and compares to input group name for autocomplete assistance.
Args: Args:
interaction (nextcord.Interaction): Interaction with command. interaction (nextcord.Interaction): Interaction with command.
+3 -1
View File
@@ -103,7 +103,9 @@ class SetCog(commands.Cog, name="Set command."):
f"Category pad length set to {length} for {group.GroupName}", ephemeral=True f"Category pad length set to {length} for {group.GroupName}", ephemeral=True
) )
@set.subcommand(description="Registers the channel for submission messages.") @set.subcommand(
description="Registers the channel for submission messages. Currently this information is deprecated."
)
async def submission_channel( async def submission_channel(
self, self,
interaction: nextcord.Interaction, interaction: nextcord.Interaction,
+22 -8
View File
@@ -9,7 +9,8 @@ from sqlalchemy.orm import Session
from cogs.submission.verification_view import VerificationView from cogs.submission.verification_view import VerificationView
from database_setup.sql_data_classes import Category, CategoryGroup, Guild, Submission, engine from database_setup.sql_data_classes import Category, CategoryGroup, Guild, Submission, engine
from utilities.utils import GUILDS, autofill_categories, autofill_groups, submit_score from main import logger
from utilities.utils import autofill_categories, autofill_groups, submit_score
class SubmissionCog(commands.Cog): class SubmissionCog(commands.Cog):
@@ -18,15 +19,18 @@ class SubmissionCog(commands.Cog):
def __init__(self) -> None: def __init__(self) -> None:
return return
@nextcord.slash_command(guild_ids=GUILDS) @nextcord.slash_command()
async def submit( async def submit( # noqa: PLR0913
self, self,
interaction: nextcord.Interaction, interaction: nextcord.Interaction,
group_id: str = nextcord.SlashOption(name="Group", required=True, description="Group to submit score to."), group_id: str = nextcord.SlashOption(name="group", required=True, description="Group to submit score to."),
category_id: str = nextcord.SlashOption( category_id: str = nextcord.SlashOption(
name="Category", required=True, description="Category to submit score to." name="category", required=True, description="Category to submit score to."
), ),
score: str = nextcord.SlashOption(required=True, description="Score to submit"), score: str = nextcord.SlashOption(required=True, description="Score to submit"),
proof: nextcord.Attachment = nextcord.SlashOption(
required=False, description="Proof for submission. Only needed if score requires verification."
),
username: str = nextcord.SlashOption( username: str = nextcord.SlashOption(
required=False, description="Modified username if submitting under not your discord name." required=False, description="Modified username if submitting under not your discord name."
), ),
@@ -43,6 +47,7 @@ class SubmissionCog(commands.Cog):
group_id (str): Is SlashOption. Group that the score category belongs in. group_id (str): Is SlashOption. Group that the score category belongs in.
category_id (str): Is SlashOption. Category to submit the score in. category_id (str): Is SlashOption. Category to submit the score in.
score (str): Is SlashOption. The score. 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. username (str, optional): Is SlashOption. Username to associate with the score.
""" """
# fill out username if not passed in # fill out username if not passed in
@@ -57,7 +62,7 @@ class SubmissionCog(commands.Cog):
category: Category = session.scalar(stmt) category: Category = session.scalar(stmt)
# check if verification is required. if no picture, deny # check if verification is required. if no picture, deny
if category.IsValidated and not len(interaction.message.attachments) > 0: if category.IsValidated and proof is None:
await interaction.response.send_message( await interaction.response.send_message(
"This category required verification. Please attach a picture and re-submit.", ephemeral=True "This category required verification. Please attach a picture and re-submit.", ephemeral=True
) )
@@ -86,10 +91,18 @@ class SubmissionCog(commands.Cog):
"Registered Verification channel does not exist or was never registered. Contact server admins." "Registered Verification channel does not exist or was never registered. Contact server admins."
) )
await interaction.response.send_message(error_response, ephemeral=True) await interaction.response.send_message(error_response, ephemeral=True)
return
if not isinstance(channel, nextcord.TextChannel):
await interaction.response.send_message("An error happened with submission.")
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 # Send the verification message to admins
verification_message = await channel.send( verification_message = await channel.send(
files=[await attach.to_file() for attach in interaction.message.attachments], file=await proof.to_file(),
view=VerificationView(), view=VerificationView(),
embeds=[self.create_embed(interaction, group_id, category_id, verified_score, username, session)], embeds=[self.create_embed(interaction, group_id, category_id, verified_score, username, session)],
content=f"Submission from <@{interaction.user.id}>.", content=f"Submission from <@{interaction.user.id}>.",
@@ -103,6 +116,7 @@ class SubmissionCog(commands.Cog):
GuildId=interaction.guild_id, GuildId=interaction.guild_id,
MessageId=verification_message.id, MessageId=verification_message.id,
Score=verified_score, Score=verified_score,
UserId=interaction.user.id,
UserName=username, UserName=username,
) )
@@ -195,7 +209,7 @@ class SubmissionCog(commands.Cog):
@submit.on_autocomplete("group_id") @submit.on_autocomplete("group_id")
async def group_autocomplete(self, interaction: nextcord.Interaction, group_id: str) -> None: async def group_autocomplete(self, interaction: nextcord.Interaction, group_id: str) -> None:
""" """
Autocomplete function for table names. Autocomplete function for group names.
Args: Args:
interaction (nextcord.Interaction): Interaction that invoked the command. interaction (nextcord.Interaction): Interaction that invoked the command.
+19 -20
View File
@@ -1,6 +1,4 @@
""" """View command hierarchy."""
View command heirarchy.
"""
import nextcord import nextcord
import pandas as pd import pandas as pd
@@ -14,20 +12,14 @@ from utilities.utils import MAX_LINE_LENGTH, autofill_categories, autofill_group
class ViewCog(commands.Cog, name="View Command"): class ViewCog(commands.Cog, name="View Command"):
""" """Class holds commands for viewing data."""
Class holds commands for viewing data.
"""
def __init__(self) -> None: def __init__(self) -> None:
""" """Default constructor for ViewCog class. Does nothing."""
Default constructor for ViewCog class. Does nothing.
"""
@nextcord.slash_command() @nextcord.slash_command()
async def view(self, interaction: nextcord.Interaction): async def view(self, interaction: nextcord.Interaction) -> None:
""" """Parent slash command for view. Never called."""
Parent slash command for view. Never called.
"""
@view.subcommand(description="Lists all scores for a given category.") @view.subcommand(description="Lists all scores for a given category.")
async def leaderboard( async def leaderboard(
@@ -39,10 +31,12 @@ class ViewCog(commands.Cog, name="View Command"):
category_id: str = nextcord.SlashOption( category_id: str = nextcord.SlashOption(
name="category", description="Category to show leaderboard.", required=True name="category", description="Category to show leaderboard.", required=True
), ),
): ) -> None:
""" """
Gathers all users that have a submission in the group/category combo and returns a message with them Create message with leaderboard of a group and category.
in ranked order.
Gathers all users that have a submission in the group/category combo and returns a
message with them in ranked order.
Args: Args:
interaction (nextcord.Interaction): Interaction that invoked the command. interaction (nextcord.Interaction): Interaction that invoked the command.
@@ -71,7 +65,12 @@ class ViewCog(commands.Cog, name="View Command"):
for i, row in scores.iterrows(): for i, row in scores.iterrows():
str_list.append( str_list.append(
format_score_string( format_score_string(
row["UserName"], row["Score"], longest_str, category.IsTimeRecord, i + 1, MAX_LINE_LENGTH row["UserName"],
row["Score"],
longest_str,
category.IsTimeRecord,
i + 1, # type: ignore[reportOperatorIssue]
MAX_LINE_LENGTH,
) )
) )
@@ -87,9 +86,9 @@ class ViewCog(commands.Cog, name="View Command"):
await interaction.response.send_message(content=message_str) await interaction.response.send_message(content=message_str)
@leaderboard.on_autocomplete("group_id") @leaderboard.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:
""" """
Autocomplete function for table names. Autocomplete function for group names.
Args: Args:
interaction (nextcord.Interaction): Interaction that invoked the command. interaction (nextcord.Interaction): Interaction that invoked the command.
@@ -98,7 +97,7 @@ class ViewCog(commands.Cog, name="View Command"):
await interaction.response.send_autocomplete(autofill_groups(interaction.guild_id, group_id)) await interaction.response.send_autocomplete(autofill_groups(interaction.guild_id, group_id))
@leaderboard.on_autocomplete("category_id") @leaderboard.on_autocomplete("category_id")
async def category_autocomplete(self, interaction: nextcord.Interaction, category_id: str, group_id: str): async def category_autocomplete(self, interaction: nextcord.Interaction, category_id: str, group_id: str) -> None:
""" """
Autocomplete function for category names. Autocomplete function for category names.
+30 -17
View File
@@ -1,6 +1,10 @@
"""View for verification buttons.""" """View for verification buttons."""
import nextcord import nextcord
from sqlalchemy import delete, select
from sqlalchemy.orm import Session
from database_setup.sql_data_classes import Submission, engine
# pylint: disable=import-error # pylint: disable=import-error
from utilities.utils import submit_score from utilities.utils import submit_score
@@ -15,30 +19,39 @@ class VerificationView(nextcord.ui.View):
@nextcord.ui.button(label="Deny", style=nextcord.ButtonStyle.red, custom_id="deny-button-submission") @nextcord.ui.button(label="Deny", style=nextcord.ButtonStyle.red, custom_id="deny-button-submission")
async def deny_button(self, button: nextcord.ui.Button, interaction: nextcord.Interaction) -> None: # noqa: ARG002 async def deny_button(self, button: nextcord.ui.Button, interaction: nextcord.Interaction) -> None: # noqa: ARG002
"""Button for denying score submission.""" """Button for denying score submission."""
with Session(engine) as session:
stmt = (
delete(Submission)
.where(Submission.GuildId == interaction.guild_id)
.where(Submission.MessageId == interaction.message.id)
)
session.execute(stmt)
session.commit()
await interaction.message.delete() await interaction.message.delete()
@nextcord.ui.button(label="Approve", style=nextcord.ButtonStyle.green, custom_id="approve-button-submission") @nextcord.ui.button(label="Approve", style=nextcord.ButtonStyle.green, custom_id="approve-button-submission")
async def approve_button(self, button: nextcord.ui.Button, interaction: nextcord.Interaction) -> None: # noqa: ARG002 async def approve_button(self, button: nextcord.ui.Button, interaction: nextcord.Interaction) -> None: # noqa: ARG002
"""Button for approving score submission.""" """Button for approving score submission."""
for embed in interaction.message.embeds: with Session(engine) as session:
content = embed.to_dict() stmt = (
select(Submission)
.where(Submission.GuildId == interaction.guild_id)
.where(Submission.MessageId == interaction.message.id)
)
for field in content["fields"]: submission = session.scalar(stmt)
if field["name"] == "GroupId":
group_id = int(field["value"])
elif field["name"] == "CategoryId":
category_id = int(field["value"])
elif field["name"] == "Score":
score = field["value"]
elif field["name"] == "User Name":
user = field["value"]
try: if submission is None:
await submit_score(interaction, group_id, category_id, user, score) await interaction.response.send_message("Could not find submission data in database.", ephemeral=True)
await interaction.message.delete()
return
await submit_score(
interaction, submission.GroupId, submission.CategoryId, submission.UserName, submission.Score
)
session.delete(submission)
session.commit()
content = interaction.message.content content = interaction.message.content
content += f"\nApproved by <@{interaction.user.id}>." content += f"\nApproved by <@{interaction.user.id}>."
# remove the bot information embed. it should always be the first one. # remove the bot information embed. it should always be the first one.
await interaction.message.edit(content=content, view=None, embed=interaction.message.embeds[0]) await interaction.message.edit(content=content, view=None)
except UnboundLocalError:
await interaction.send("Instruct user to submit with all fields.")
await interaction.message.edit(view=None)
+4 -3
View File
@@ -4,7 +4,7 @@ use highscores;
CREATE TABLE Guilds( CREATE TABLE Guilds(
GuildId bigint unsigned not null unique, GuildId bigint unsigned not null unique,
GuildName varchar(95) not null, GuildName varchar(95) not null,
SubmissionChannel bigint unsigned, SubmissionChannel bigint unsigned, -- deprecated, is not used anymore.
VerificationChannel bigint unsigned, VerificationChannel bigint unsigned,
Active boolean DEFAULT true, Active boolean DEFAULT true,
PRIMARY KEY (GuildId) PRIMARY KEY (GuildId)
@@ -47,9 +47,10 @@ CREATE TABLE Scores(
CREATE TABLE Submissions( CREATE TABLE Submissions(
CategoryId int unsigned not null, CategoryId int unsigned not null,
GroupId int unsigned not null, GroupId int unsigned not null,
GuildId int unsigned not null, GuildId bigint unsigned not null,
MessageId int unsigned not null, MessageId bigint unsigned not null,
Score varchar(30) not null, -- Score is stored as a string until converted during submission Score varchar(30) not null, -- Score is stored as a string until converted during submission
UserId bigint unsigned not null,
UserName varchar(30) not null, UserName varchar(30) not null,
PRIMARY KEY (CategoryId, UserName) -- one submission a category per user. PRIMARY KEY (CategoryId, UserName) -- one submission a category per user.
); );
+7 -39
View File
@@ -1,8 +1,5 @@
""" """Contains various utility functions for highscores bot."""
Contains various utlity functions for highscores bot
"""
import json
import time import time
import nextcord import nextcord
@@ -15,12 +12,11 @@ from database_setup.sql_data_classes import Category, CategoryGroup, Guild, Scor
MAX_LINE_LENGTH = 142 MAX_LINE_LENGTH = 142
GUILDS = [1000247361639030804]
def autofill_categories(guild_id: int, group_id: int, category_begin: str) -> "dict[str, str]": def autofill_categories(guild_id: int, group_id: int, category_begin: str) -> "dict[str, str]":
""" """
Returns all category names that start with category_begin. Returns all category names that start with category_begin.
If category_begin is empty, returns list of all category names. If category_begin is empty, returns list of all category names.
Args: Args:
@@ -164,9 +160,12 @@ def autofill_users_in_guild(guild_id: int, user_begin: str) -> "list[str]":
return users[:25] return users[:25]
# TODO(imcompiling): #73 Address complexity warning.
# 073
def format_highscore_message(session: Session, group: CategoryGroup) -> str: def format_highscore_message(session: Session, group: CategoryGroup) -> str:
""" """
Creates a formatted string for a boss's highscore discord message. Creates a formatted string for a category's highscore discord message.
This formatted string is in discord's code block to ensure monospace text. This formatted string is in discord's code block to ensure monospace text.
Loops through each category to build the message one place at a time. Loops through each category to build the message one place at a time.
@@ -213,7 +212,6 @@ def format_highscore_message(session: Session, group: CategoryGroup) -> str:
str_list: list[str] = ["" for i in range(max_cat_size + 1)] str_list: list[str] = ["" for i in range(max_cat_size + 1)]
groups_df = d_f.groupby("CategoryId") groups_df = d_f.groupby("CategoryId")
# TODO figure out how to have less nests.
for category in categories: for category in categories:
# category name string # category name string
str_list[0] += f"{category.CategoryName}".ljust(pad_length, " ") str_list[0] += f"{category.CategoryName}".ljust(pad_length, " ")
@@ -269,9 +267,7 @@ def format_highscore_message(session: Session, group: CategoryGroup) -> str:
ret_string += string.rstrip() + "\n" ret_string += string.rstrip() + "\n"
# add extra newline at category split unless there is already a space. # add extra newline at category split unless there is already a space.
if ((ind + 1) % (max_cat_size + 1)) == 0: if ((ind + 1) % (max_cat_size + 1)) == 0 and not ret_string.endswith("\n\n"):
# checking inside the other if to ensure called as little as possible.
if not ret_string.endswith("\n\n"):
ret_string += "\n" ret_string += "\n"
return ret_string + "```" return ret_string + "```"
@@ -400,31 +396,3 @@ async def submit_score(
group = session.scalar(stmt) group = session.scalar(stmt)
await send_highscore_message(session, interaction.guild, group) await send_highscore_message(session, interaction.guild, group)
def add_preset_configuration(guild_id: int, config_str: str) -> None:
"""
Adds an example configuration to a Guild. Uses guild id to know which guild and
config name to open a file in example_config.
Args:
guild_id (int): Guild id to add config to
config_str (str): Config name to add
"""
with Session(engine) as session:
with open(f"example_config/{config_str}.json", encoding="utf-8") as file:
config = json.load(file)
for group_name, categorys in config["tables"].items():
group_obj = CategoryGroup(GroupName=group_name, GuildId=guild_id)
session.add(group_obj)
for category_name in categorys:
cat_obj = Category(
CategoryName=category_name,
Size=config["table_size"],
IsTimeRecord=config["categories"][category_name]["is_time_record"],
IsAscending=config["categories"][category_name]["ascending"],
)
session.add(cat_obj)
session.commit()