diff --git a/cogs/commands/__init__.py b/cogs/commands/__init__.py index 38bffbc..85407e7 100644 --- a/cogs/commands/__init__.py +++ b/cogs/commands/__init__.py @@ -2,14 +2,15 @@ from nextcord.ext import commands -from .create import CreateCog -from .delete import DeleteCog -from .help import HelpCog -from .refresh import RefreshCog -from .set import SetCog -from .submission import SubmissionCog -from .update import UpdateCog -from .view import ViewCog +from cogs.commands.create import CreateCog +from cogs.commands.delete import DeleteCog +from cogs.commands.get import GetCog +from cogs.commands.help import HelpCog +from cogs.commands.refresh import RefreshCog +from cogs.commands.set import SetCog +from cogs.commands.submission import SubmissionCog +from cogs.commands.update import UpdateCog +from cogs.commands.view import ViewCog # This function will be called when this extension is loaded. @@ -18,6 +19,7 @@ def setup(bot: commands.Bot) -> None: """Adds all command classes to bot.""" bot.add_cog(CreateCog(bot)) bot.add_cog(DeleteCog()) + bot.add_cog(GetCog()) bot.add_cog(HelpCog()) bot.add_cog(RefreshCog(bot)) bot.add_cog(SetCog()) diff --git a/cogs/commands/get.py b/cogs/commands/get.py new file mode 100644 index 0000000..e05e72f --- /dev/null +++ b/cogs/commands/get.py @@ -0,0 +1,199 @@ +"""Get command hierarchy.""" + +import nextcord +from nextcord.ext import commands +from sqlmodel import Session, col, select + +from database.db import engine +from database.models import Category, CategoryGroup +from utilities.utils import autofill_categories, autofill_groups + + +class GetCog(commands.Cog, name="Get command."): + """Class holds commands for setting variables in guild's database.""" + + def __init__(self) -> None: + """Default constructor for SetCog class. Does nothing.""" + + @nextcord.slash_command(default_member_permissions=nextcord.Permissions(administrator=True)) + async def get(self, interaction: nextcord.Interaction) -> None: + """Parent slash command for getting. Never called.""" + + @get.subcommand(description="Commands for getting category information.") + async def category(self, interaction: nextcord.Interaction) -> None: + """Parent slash subcommand for getting category information. Never called.""" + + @category.subcommand(description="Get whether a category is sorted in ascending or descending order.") + async def is_ascending( + self, + interaction: nextcord.Interaction, + group_id: str = nextcord.SlashOption(name="group", description="Group that Category is in.", required=True), + category_id: str = nextcord.SlashOption( + name="category", description="Category to information from.", required=True + ), + ) -> None: + """ + Command to view how a category is sorted. + + Args: + interaction (nextcord.Interaction): Interaction that invoked the command. + group_id (str): String representation of Group id get information from. Is SlashOption. + category_id (str): String representation of Category id to get information from. Is SlashOption. + """ + stmt = ( + select(Category).where(col(Category.group_id) == int(group_id)).where(col(Category.id) == int(category_id)) + ) + + with Session(engine) as session: + if (category := session.scalar(stmt)) is None: + await interaction.response.send_message("Group or Category was not found.", ephemeral=True) + return + + await interaction.response.send_message( + f"**{category.name}** is sorted in **{'ascending' if category.is_ascending else 'descending'}** order.", + ephemeral=True, + ) + + @category.subcommand(description="Get whether a category required validation.") + async def is_validated( + self, + interaction: nextcord.Interaction, + group_id: str = nextcord.SlashOption(name="group", description="Group that Category is in.", required=True), + category_id: str = nextcord.SlashOption( + name="category", description="Category to information from.", required=True + ), + ) -> None: + """ + Command to view if a category required photo validation. + + Args: + interaction (nextcord.Interaction): Interaction that invoked the command. + group_id (str): String representation of Group id get information from. Is SlashOption. + category_id (str): String representation of Category id to get information from. Is SlashOption. + """ + stmt = ( + select(Category).where(col(Category.group_id) == int(group_id)).where(col(Category.id) == int(category_id)) + ) + + with Session(engine) as session: + if (category := session.scalar(stmt)) is None: + await interaction.response.send_message("Group or Category was not found.", ephemeral=True) + return + + await interaction.response.send_message( + f"{category.name} does {'not ' if not category.is_validated else ''} require validation.", + ephemeral=True, + ) + + @category.subcommand(description="Get the size for a category.") + async def size( + self, + interaction: nextcord.Interaction, + group_id: str = nextcord.SlashOption(name="group", description="Group that Category is in.", required=True), + category_id: str = nextcord.SlashOption(name="category", description="Category to get size of.", required=True), + ) -> None: + """ + Command to view a category's size. + + Args: + interaction (nextcord.Interaction): Interaction that invoked the command. + group_id (str): String representation of Group id get information from. Is SlashOption. + category_id (str): String representation of Category id to get information from. Is SlashOption. + """ + stmt = ( + select(Category).where(col(Category.group_id) == int(group_id)).where(col(Category.id) == int(category_id)) + ) + + with Session(engine) as session: + if (category := session.scalar(stmt)) is None: + await interaction.response.send_message("Group or Category was not found.", ephemeral=True) + return + + await interaction.response.send_message( + f"**{category.name}** has a size of **{category.size}**.", ephemeral=True + ) + + @get.subcommand(description="Commands for getting group information.") + async def group(self, interaction: nextcord.Interaction) -> None: + """Parent slash subcommand for getting group information. Never called.""" + + @group.subcommand(description="Get the pad length of a group.") + async def pad_length( + self, + interaction: nextcord.Interaction, + group_id: str = nextcord.SlashOption(name="group", description="Group to get pad length of.", required=True), + ) -> None: + """ + Command to view a group's pad length. + + Args: + interaction (nextcord.Interaction): Interaction that invoked the command. + group_id (str): String representation of Group id get information from. Is SlashOption. + """ + stmt = select(CategoryGroup).where(col(CategoryGroup.id) == int(group_id)) + + with Session(engine) as session: + if (group := session.scalar(stmt)) is None: + await interaction.response.send_message("Group was not found.", ephemeral=True) + return + + await interaction.response.send_message( + f"**{group.name}** has a pad length of **{group.pad_length}**.", ephemeral=True + ) + + @group.subcommand(description="Get the username length of a group.") + async def username_length( + self, + interaction: nextcord.Interaction, + group_id: str = nextcord.SlashOption( + name="group", description="Group to get username length of.", required=True + ), + ) -> None: + """ + Command to view a group's username length. + + Args: + interaction (nextcord.Interaction): Interaction that invoked the command. + group_id (str): String representation of Group id get information from. Is SlashOption. + """ + stmt = select(CategoryGroup).where(col(CategoryGroup.id) == int(group_id)) + + with Session(engine) as session: + if (group := session.scalar(stmt)) is None: + await interaction.response.send_message("Group was not found.", ephemeral=True) + return + + await interaction.response.send_message( + f"**{group.name}** has a username length of **{group.username_length}**.", ephemeral=True + ) + + @is_ascending.on_autocomplete("category_id") + @is_validated.on_autocomplete("category_id") + @size.on_autocomplete("category_id") + async def category_autocomplete(self, interaction: nextcord.Interaction, category_id: str, group_id: str) -> None: + """ + Autocomplete function for category names. + + Args: + interaction (nextcord.Interaction): Interaction that invoked the command. + category_id (str): String to be autocompleted with Category name. + group_id (str): Group to find Category from. + """ + await interaction.response.send_autocomplete( + autofill_categories(interaction.guild_id, int(group_id), category_id) + ) + + @is_ascending.on_autocomplete("group_id") + @is_validated.on_autocomplete("group_id") + @pad_length.on_autocomplete("group_id") + @size.on_autocomplete("group_id") + @username_length.on_autocomplete("group_id") + 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. + + Args: + interaction (nextcord.Interaction): Interaction with command. + group_id (str): String to run autocompletion against. + """ + await interaction.response.send_autocomplete(autofill_groups(interaction.guild_id, group_id)) diff --git a/cogs/commands/set.py b/cogs/commands/set.py index 47e8d26..780c147 100644 --- a/cogs/commands/set.py +++ b/cogs/commands/set.py @@ -7,8 +7,9 @@ from sqlalchemy.orm import Session from sqlmodel import col from database.db import engine -from database.models import CategoryGroup, Guild -from utilities.utils import MAX_LINE_LENGTH, autofill_groups +from database.models import Category, CategoryGroup, Guild +from utilities.logger import agon_logger +from utilities.utils import MAX_LINE_LENGTH, autofill_categories, autofill_groups, send_highscore_message class SetCog(commands.Cog, name="Set command."): @@ -21,13 +22,219 @@ class SetCog(commands.Cog, name="Set command."): 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( + @set.subcommand(description="Commands for editing a category.") + async def category(self, interaction: nextcord.Interaction) -> None: + """Parent slash subcommand for setting category settings. Never called.""" + + @category.subcommand(description="Update the number of entries to show for a category.", name="size") + async def category_size( self, interaction: nextcord.Interaction, - group_id: str = nextcord.SlashOption( - name="group", description="Category Group to assign to channel.", required=True + group_id: str = nextcord.SlashOption(name="group", description="Group that Category is in.", required=True), + category_id: str = nextcord.SlashOption( + name="category", description="Category to update size of.", required=True ), + size: int = nextcord.SlashOption(description="Number of entries to show for the category.", required=True), + ) -> None: + """ + Command to update a category's size. + + Args: + interaction (nextcord.Interaction): Interaction that invoked the command. + group_id (str): String representation of Group id to register channel to. Is SlashOption. + category_id (str): String representation of Category id to register channel to. Is SlashOption. + size (int): Size to set. Is SlashOption. + """ + stmt = ( + select(Category).where(col(Category.group_id) == int(group_id)).where(col(Category.id) == int(category_id)) + ) + + with Session(engine) as session: + if (category := session.scalar(stmt)) is None: + await interaction.response.send_message("Category or Group was not found.", ephemeral=True) + return + category.size = size + session.commit() + + # Use the relationship to get group category is in. + if (group := category.category_group) is None: + agon_logger.warning(f"{category.name}:{category.id} group relationship was None.") + + stmt = select(CategoryGroup).where(col(CategoryGroup.id) == group_id) + if (group := session.scalar(stmt)) is None: + await interaction.response.send_message("Group was not found.", ephemeral=True) + return + + await send_highscore_message(session, interaction.guild, group) + await interaction.response.send_message( + f"**{category.name}** size has been updated to **{size}**.", ephemeral=True + ) + + @category.subcommand(description="Update the is_ascending value for a category.") + async def is_ascending( + self, + interaction: nextcord.Interaction, + group_id: str = nextcord.SlashOption(name="group", description="Group that Category is in.", required=True), + category_id: str = nextcord.SlashOption( + name="category", description="Category to update is_ascending of.", required=True + ), + ascending: bool = nextcord.SlashOption( # noqa: FBT001 + description="Whether the scores are sorted ascending or not.", required=True + ), + ) -> None: + """ + Command to update a category's is_ascending boolean. + + Args: + interaction (nextcord.Interaction): Interaction that invoked the command. + group_id (str): String representation of Group id to register channel to. Is SlashOption. + category_id (str): String representation of Category id to register channel to. Is SlashOption. + ascending (bool): Boolean to set. Determines whether scores are sorted ascending or not. Is SlashOption. + """ + stmt = ( + select(Category).where(col(Category.group_id) == int(group_id)).where(col(Category.id) == int(category_id)) + ) + + with Session(engine) as session: + if (category := session.scalar(stmt)) is None: + await interaction.response.send_message("Category or Group was not found.", ephemeral=True) + return + category.is_ascending = ascending + session.commit() + + # Use the relationship to get group category is in. + if (group := category.category_group) is None: + agon_logger.warning(f"{category.name}:{category.id} group relationship was None.") + + stmt = select(CategoryGroup).where(col(CategoryGroup.id) == group_id) + if (group := session.scalar(stmt)) is None: + await interaction.response.send_message("Group was not found.", ephemeral=True) + return + + await send_highscore_message(session, interaction.guild, group) + await interaction.response.send_message( + f"**{category.name}** is_ascending has been updated to **{ascending}**.", ephemeral=True + ) + + @category.subcommand(description="Update the is_validated value for a category.") + async def is_validated( + self, + interaction: nextcord.Interaction, + group_id: str = nextcord.SlashOption(name="group", description="Group that Category is in.", required=True), + category_id: str = nextcord.SlashOption( + name="category", description="Category to update is_ascending of.", required=True + ), + validated: bool = nextcord.SlashOption( # noqa: FBT001 + description="Whether the scores need to be validated.", required=True + ), + ) -> None: + """ + Command to update a category's is_validated boolean. + + Args: + interaction (nextcord.Interaction): Interaction that invoked the command. + group_id (str): String representation of Group id to register channel to. Is SlashOption. + category_id (str): String representation of Category id to register channel to. Is SlashOption. + validated (bool): Boolean to set. Determines whether scores are validated or not. Is SlashOption. + """ + stmt = ( + select(Category).where(col(Category.group_id) == int(group_id)).where(col(Category.id) == int(category_id)) + ) + + with Session(engine) as session: + if (category := session.scalar(stmt)) is None: + await interaction.response.send_message("Category or Group was not found.", ephemeral=True) + return + category.is_validated = validated + session.commit() + + await interaction.response.send_message( + f"**{category.name}** is_validated has been updated to **{validated}**.", ephemeral=True + ) + + @category.subcommand(name="name", description="Change the name of a category.") + async def category_name( + self, + interaction: nextcord.Interaction, + group_id: str = nextcord.SlashOption(name="group", description="Group that Category is in.", required=True), + category_id: str = nextcord.SlashOption( + name="category", description="Category to update the name of.", required=True + ), + name: str = nextcord.SlashOption(description="Name to update to.", required=True, min_length=1, max_length=30), + ) -> None: + """ + Command to update a category's name. + + Args: + interaction (nextcord.Interaction): Interaction that invoked the command. + group_id (str): String representation of Group id to register channel to. Is SlashOption. + category_id (str): String representation of Category id to register channel to. Is SlashOption. + name (str): The name to update the category's name to. Is SlashOption. + """ + stmt = ( + select(Category).where(col(Category.group_id) == int(group_id)).where(col(Category.id) == int(category_id)) + ) + + with Session(engine) as session: + if (category := session.scalar(stmt)) is None: + await interaction.response.send_message("Category or Group was not found.", ephemeral=True) + return + old_name = category.name + category.name = name + session.commit() + + # Use the relationship to get group category is in. + if (group := category.category_group) is None: + agon_logger.warning(f"{category.name}:{category.id} group relationship was None.") + + stmt = select(CategoryGroup).where(col(CategoryGroup.id) == group_id) + if (group := session.scalar(stmt)) is None: + await interaction.response.send_message("Group was not found.", ephemeral=True) + return + + await send_highscore_message(session, interaction.guild, group) + await interaction.response.send_message( + f"**{old_name}** has been updated to **{category.name}**.", ephemeral=True + ) + + @set.subcommand(description="Commands for editing a group.") + async def group(self, interaction: nextcord.Interaction) -> None: + """Parent slash subcommand for setting group settings. Never called.""" + + @group.subcommand(name="name", description="Change the name of a Group.") + async def group_name( + self, + interaction: nextcord.Interaction, + group_id: str = nextcord.SlashOption(name="group", description="Group that Category is in.", required=True), + name: str = nextcord.SlashOption(description="Name to update to.", required=True, min_length=1, max_length=50), + ) -> None: + """ + Command to update a group's name. + + Args: + interaction (nextcord.Interaction): Interaction that invoked the command. + group_id (str): String representation of Group id to register channel to. Is SlashOption. + name (str): The name to update the group's name to. Is SlashOption. + """ + stmt = select(CategoryGroup).where(col(CategoryGroup.id) == int(group_id)) + + with Session(engine) as session: + if (group := session.scalar(stmt)) is None: + await interaction.response.send_message("Group was not found.", ephemeral=True) + return + old_name = group.name + group.name = name + # session.add(group) test if this is needed. SQLAlchemy can do the update without adding. + session.commit() + + await send_highscore_message(session, interaction.guild, group) + await interaction.response.send_message(f"**{old_name}** has been updated to **{group.name}**.", ephemeral=True) + + @group.subcommand(description="Registers the channel for a Group's messages.") + async def output_channel( + self, + interaction: nextcord.Interaction, + group_id: str = nextcord.SlashOption(name="group", description="Group to assign to channel.", required=True), channel: nextcord.abc.GuildChannel | None = nextcord.SlashOption( description="Channel to register", required=False, @@ -41,7 +248,7 @@ 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 register channel to. Is SlashOption. - channel (nextcord.abc.GuildChannel, optional): Channel to register + channel (nextcord.abc.GuildChannel, optional): Channel to register. Is a SlashOption. Limited to text channels, Defaults to None. """ channel_id = interaction.channel.id if not channel else channel.id @@ -57,17 +264,18 @@ class SetCog(commands.Cog, name="Set command."): return group.channel_id = channel_id session.commit() + # TODO(imcompiling): delete the message and call send_highscore_message to send a new one. Bug needs fixed + # first. + # 082 await interaction.send(f"Registered <#{channel_id}>.", ephemeral=True) - @set.subcommand( + @group.subcommand( description="Set character length for a category. Can cut off username/score. Affects all categories." ) async def pad_length( self, interaction: nextcord.Interaction, - group_id: str = nextcord.SlashOption( - name="group", description="Category Group to assign pad length to.", required=True - ), + group_id: str = nextcord.SlashOption(name="group", description="Group to assign pad length to.", required=True), length: int = nextcord.SlashOption( description="Number of characters alloted to a category.", required=True, @@ -91,16 +299,17 @@ class SetCog(commands.Cog, name="Set command."): .where(col(CategoryGroup.id) == int(group_id)) ) if (group := session.scalar(stmt)) is None: - await interaction.response.send_message("Group or Category was not found.", ephemeral=True) + await interaction.response.send_message("Group 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.name}", ephemeral=True + f"Category pad length set to **{length}** for **{group.name}**.", ephemeral=True ) + await send_highscore_message(session, interaction.guild, group) - @set.subcommand( + @group.subcommand( description="Registers the channel for submission messages. Currently this information is deprecated." ) async def submission_channel( @@ -132,7 +341,7 @@ class SetCog(commands.Cog, name="Set command."): session.commit() await interaction.send(f"Registered <#{channel_id}>.", ephemeral=True) - @set.subcommand(description="Set username length for a Group of Categories.") + @group.subcommand(description="Set username length for a Group of Categories.") async def username_length( self, interaction: nextcord.Interaction, @@ -163,7 +372,9 @@ class SetCog(commands.Cog, name="Set command."): group.username_length = length session.commit() - await interaction.response.send_message(f"Username length set to {length}.", ephemeral=True) + await interaction.response.send_message( + f"Username length for **{group.name}** set to **{length}**.", ephemeral=True + ) @set.subcommand(description="Registers the channel for verification messages.") async def verify_channel( @@ -195,7 +406,29 @@ class SetCog(commands.Cog, name="Set command."): session.commit() await interaction.send(f"Registered <#{channel_id}>.", ephemeral=True) - @group_output_channel.on_autocomplete("group_id") + @category_name.on_autocomplete("category_id") + @category_size.on_autocomplete("category_id") + @is_ascending.on_autocomplete("category_id") + @is_validated.on_autocomplete("category_id") + async def category_autocomplete(self, interaction: nextcord.Interaction, category_id: str, group_id: str) -> None: + """ + Autocomplete function for category names. + + Args: + interaction (nextcord.Interaction): Interaction that invoked the command. + category_id (str): String to be autocompleted with Category name. + group_id (str): Group to find Category from. + """ + await interaction.response.send_autocomplete( + autofill_categories(interaction.guild_id, int(group_id), category_id) + ) + + @category_name.on_autocomplete("group_id") + @category_size.on_autocomplete("group_id") + @group_name.on_autocomplete("group_id") + @is_ascending.on_autocomplete("group_id") + @is_validated.on_autocomplete("group_id") + @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) -> None: diff --git a/cogs/commands/submission.py b/cogs/commands/submission.py index caa011d..ad53caa 100644 --- a/cogs/commands/submission.py +++ b/cogs/commands/submission.py @@ -11,7 +11,7 @@ from sqlmodel import col from cogs.submission.verification_view import VerificationView from database.db import engine from database.models import Category, CategoryGroup, Guild, Submission -from main import logger +from main import agon_logger from utilities.utils import autofill_categories, autofill_groups, submit_score @@ -105,7 +105,7 @@ class SubmissionCog(commands.Cog): if not isinstance(channel, nextcord.TextChannel): await interaction.response.send_message("An error happened with submission.") - logger.error( + agon_logger.error( "Channel id %s in guild %s is does not correspond to a TextChannel.", channel.id, channel.guild.id ) return diff --git a/database/db.py b/database/db.py index d86cd41..7a55afc 100644 --- a/database/db.py +++ b/database/db.py @@ -2,13 +2,16 @@ from sqlmodel import SQLModel, create_engine from utilities import config +from utilities.logger import agon_logger from . import models # noqa: F401 - Need this import for creating the models. if config.ENVIRONMENT == "prod": url = f"mysql+pymysql://{config.SQL_USER}:{config.SQL_PASS}@{config.SQL_HOST}/{config.SQL_DB}" + agon_logger.info("Connected to prod database.") else: url = "sqlite:///database.db" + agon_logger.info("Connected to local dev database.") engine = create_engine(url) # Don't need to create all when connected to prod. diff --git a/main.py b/main.py index 639d6c8..4dcb589 100644 --- a/main.py +++ b/main.py @@ -8,6 +8,7 @@ import nextcord from nextcord.ext import commands from utilities import config +from utilities.logger import agon_logger nextcord_logger = logging.getLogger("nextcord") nextcord_logger.setLevel(logging.DEBUG) @@ -15,18 +16,12 @@ nextcord_handler = logging.FileHandler(filename="nextcord.log", encoding="utf-8" nextcord_handler.setFormatter(logging.Formatter("%(asctime)s:%(levelname)s:%(name)s: %(message)s")) nextcord_logger.addHandler(nextcord_handler) -logger = logging.getLogger("default") -logger.setLevel(logging.INFO) -handler = logging.StreamHandler(sys.stdout) -handler.setFormatter(logging.Formatter("%(asctime)s:%(levelname)s:%(name)s: %(message)s")) -logger.addHandler(handler) - def main() -> None: """Main function for bot.""" min_python_version = (3, 12) if sys.version_info < min_python_version: - logger.error("ERROR: Python version must be >= 3.12") + agon_logger.error("ERROR: Python version must be >= 3.12") sys.exit() # Allows privileged intents for monitoring members joining, roles editing, and role assignments @@ -46,9 +41,9 @@ def main() -> None: async def on_ready() -> None: """When Discord is connected.""" if bot.user is None: - logger.error("Discord connection failed.") + agon_logger.error("Discord connection failed.") return - logger.info("%s has connected to Discord!", bot.user.name) + agon_logger.info("%s has connected to Discord!", bot.user.name) # Run Discord bot bot.run(config.DISCORD_TOKEN) diff --git a/utilities/logger.py b/utilities/logger.py new file mode 100644 index 0000000..b8fb2a6 --- /dev/null +++ b/utilities/logger.py @@ -0,0 +1,10 @@ +"""Creates logger for Agon bot.""" + +import logging +import sys + +agon_logger = logging.getLogger("default") +agon_logger.setLevel(logging.INFO) +handler = logging.StreamHandler(sys.stdout) +handler.setFormatter(logging.Formatter("%(asctime)s:%(levelname)s:%(name)s: %(message)s")) +agon_logger.addHandler(handler) diff --git a/utilities/utils.py b/utilities/utils.py index 8681a6b..caaecc1 100644 --- a/utilities/utils.py +++ b/utilities/utils.py @@ -10,6 +10,7 @@ from sqlmodel import col from database.db import engine from database.models import Category, CategoryGroup, Guild, Score +from main import agon_logger MAX_LINE_LENGTH = 142 @@ -275,6 +276,8 @@ def format_highscore_message(session: Session, group: CategoryGroup) -> str: return ret_string + "```" +# TODO(imcompiling): rework into two functions to remove the boolean and reduce function arguments. +# 084 def format_score_string( username: str, score: float, @@ -327,7 +330,7 @@ def get_time_str(time_in: float) -> str: return time.strftime(f"%-M:%S.{milliseconds}", time.gmtime(time_in)) -async def send_highscore_message(session: Session, guild: nextcord.Guild, group: CategoryGroup) -> nextcord.Message: +async def send_highscore_message(session: Session, guild: nextcord.Guild, group: CategoryGroup) -> None: """ Function takes care of checking if a message exists for a group. @@ -337,23 +340,28 @@ async def send_highscore_message(session: Session, guild: nextcord.Guild, group: session (Session): The sqlalchemy session being used for this function call. guild (nextcord.Guild): the nextcord.Guild object requesting this message. group (CategoryGroup): group to post to the message. - - Returns: - nextcord.Message: Message object that was posted with group contents. """ highscore_string = format_highscore_message(session, group) channel = guild.get_channel(group.channel_id) + if not isinstance(channel, nextcord.TextChannel): + agon_logger.error( + "Channel id %s in guild %s is does not correspond to a TextChannel.", channel.id, channel.guild.id + ) + return + if group.message_id is None: # message doesn't exist. message: nextcord.Message = await channel.send(highscore_string) group.message_id = message.id session.commit() else: + # TODO(imcompiling): check what happens when message is deleted. + # https://gitea.cheezers.org/imcompiling/agon/issues/83 message: nextcord.Message = await channel.fetch_message(group.message_id) await message.edit(content=highscore_string) - return message + return async def submit_score(