""" Commands for managing the tables/categories/scores for a Guild. """ from typing import Optional import nextcord from nextcord.ext import commands from highscores import highscores_data # pylint: disable=import-error from utilities.utils import ( # pylint: disable=import-error autofill_categories, autofill_tables, autofill_users, send_highscore_message, ) from .views.update_username_verify import UpdateUsernameVerifyView class DataManagementCog(commands.Cog, name="Table Management"): """ Class holds commands for managing the data storage of the highscores. """ def __init__(self, bot: commands.Bot): """ Default constructor for DataManagement class. Sets internal bot variable. """ self._bot: commands.Bot = bot @commands.Cog.listener() async def on_ready(self): """ Called when cog is loaded. Adds views required for DataManagement commands. """ self._bot.add_view(UpdateUsernameVerifyView()) @nextcord.slash_command(default_member_permissions=nextcord.Permissions(administrator=True)) async def delete(self, interaction: nextcord.Interaction): """ Parent slash command for deleting things. Never called. """ @delete.subcommand(description="Deletes all scores for this guild. Used for testing. Do Not Use.") async def scores(self, interaction: nextcord.Interaction): """ Warning: This command only works for developers. A command which deletes all scores in all tables for this guild. Command limits to admins to prevent devs from usings on non-test servers. """ # only allow AlwaysCompiling to use. # If you are a developer and want to use command, can add name to list if interaction.user.id == 206602713511886849: for table in highscores_data.get_guild(interaction.guild_id).tables: for category in table.categories: category.clear() highscores_data.save_guild_data(highscores_data.get_guild(interaction.guild_id)) await interaction.response.send_message(f"Cleared score data for {interaction.guild.name}.", ephemeral=True) else: await interaction.response.send_message( "You shouldn't be using this command. Deletes everything.", ephemeral=True ) @delete.subcommand() async def user(self, interaction: nextcord.Interaction): """ Subcommand parent for deleting user information. Never called. """ @user.subcommand( description="Removes all of a user's scores. Warning: data is destroyed, use with case.", ) async def from_all( self, interaction: nextcord.Interaction, username: str = nextcord.SlashOption( description="User to remove scores for. Note: This is not always a user's discord name." ), ): """ Searches all of guild's scores data for any score from the user. Removes. Updates table message is user was removed from that table. Args: interaction (nextcord.Interaction): Interaction that invoked the command. username (str): User to remove from highscores """ guild = highscores_data.get_guild(interaction.guild_id) user_was_found: bool = False # remove user from all tables and categories for table in guild.tables: table_updated: bool = False for category in table.categories: # user has a score in category if category.remove_score(username): user_was_found = True table_updated = True if table_updated: await send_highscore_message(interaction.guild.get_channel(guild.highscore_channel), guild, table) if not user_was_found: await interaction.send(f'User "{username}" was not found.', ephemeral=True) else: await interaction.send(f'User "{username}" was removed from all tables.') highscores_data.save_guild_data(guild) @user.subcommand(description="Deletes a user's scores from a category.") async def from_category( self, interaction: nextcord.Interaction, table_name: str = nextcord.SlashOption(description="Table to delete user from.", required=True), category_name: str = nextcord.SlashOption(description="Category to delete user from.", required=True), user_name: str = nextcord.SlashOption(description="User to delete.", required=True), ): """ Removes a user from a category. Uses autofill functions to fill out table_name, category_name, and user_name. Args: interaction (nextcord.Interaction): Interaction that invoked the command. table_name (str): Table to remove user from. Is SlashOption. category_name (str): Category to remove user from. Is SlashOption. user_name (str): The user to remove. Is SlashOption. """ guild = highscores_data.get_guild(interaction.guild_id) try: guild.get_table(table_name).get_category(category_name).remove_score(user_name) await send_highscore_message( interaction.guild.get_channel(guild.highscore_channel), guild, guild.get_table(table_name) ) await interaction.response.send_message( f'User "{user_name}" removed from "{category_name}".', ephemeral=True ) except nextcord.errors.ApplicationInvokeError: await interaction.response.send_message("Invalid parameter(s).", ephemeral=True) @user.subcommand(description="Deletes a user's scores from entire table.") async def from_table( self, interaction: nextcord.Interaction, table_name: str = nextcord.SlashOption(description="Table to delete user from.", required=True), user_name: str = nextcord.SlashOption(description="User to delete.", required=True), ): """ Removes user from a table. Uses autofill functions to fill out table_name and user_name. Args: interaction (nextcord.Interaction): Interaction that invoked the command. table_name (str): Table to remove user from. Is SlashOption. user_name (str): The user to remove. Is SlashOption. """ guild = highscores_data.get_guild(interaction.guild_id) try: for category in guild.get_table(table_name).categories: category.remove_score(user_name) await send_highscore_message( interaction.guild.get_channel(guild.highscore_channel), guild, guild.get_table(table_name) ) await interaction.response.send_message(f'User "{user_name}" removed from all categories.', ephemeral=True) except nextcord.errors.ApplicationInvokeError: await interaction.response.send_message("Invalid parameter(s).", ephemeral=True) @from_category.on_autocomplete("table_name") @from_table.on_autocomplete("table_name") async def autocomplete_table(self, interaction: nextcord.Interaction, table_name: str): """ Autocomplete function for table names. Args: interaction (nextcord.Interaction): Interaction that invoked the command. table_name (str): String to be autocompleted with Table name. """ await interaction.response.send_autocomplete(autofill_tables(interaction.guild_id, table_name)) @from_category.on_autocomplete("category_name") async def autocomplete_category(self, interaction: nextcord.Interaction, category_name: str, table_name: str): """ Autocomplete function for category names. Args: interaction (nextcord.Interaction): Interaction that invoked the command. category_name (str): String to be autocompleted with Category name. table_name (str): Table to find Category from. """ await interaction.response.send_autocomplete( autofill_categories(interaction.guild_id, table_name, category_name) ) @from_category.on_autocomplete("user_name") @from_table.on_autocomplete("user_name") async def autocomplete_user( self, interaction: nextcord.Interaction, user_name: str, table_name: str, category_name: Optional[str] ): """ Autocomplete functions for user names. Args: interaction (nextcord.Interaction): Interaction that invoked the command. user_name (str): String to be autocompleted with User name. table_name (str): Table to find category from. category_name (Optional[str]): Category to find User from, can be None. """ await interaction.response.send_autocomplete( autofill_users(interaction.guild_id, table_name, category_name, user_name) ) @nextcord.slash_command(description="Request a username update. Admins must approve.") async def update_username( self, 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), ): """ 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 old_username (str): Username to replace. new_username (str): Username to replace old with. """ guild = highscores_data.get_guild(interaction.guild_id) verify_channel = interaction.guild.get_channel(guild.verification_channel) 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) await verify_channel.send( content=f"Username change request from <@{interaction.user.id}>.", embed=embed, view=UpdateUsernameVerifyView(), ) await interaction.send("Request sent to admins for verification.", ephemeral=True)