diff --git a/cogs/commands/delete.py b/cogs/commands/delete.py index 44d3622..2c7af4d 100644 --- a/cogs/commands/delete.py +++ b/cogs/commands/delete.py @@ -19,6 +19,8 @@ from utilities.utils import ( send_highscore_message, ) +from .views.delete_confirm import DeleteConfirmView, create_delete_id + class DeleteCog(commands.Cog, name="Table Management"): """ @@ -39,7 +41,7 @@ class DeleteCog(commands.Cog, name="Table Management"): @delete.subcommand() async def all(self, interaction: nextcord.Interaction): """ - Subcommand parent for deleting everything in a group. 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.") @@ -124,6 +126,40 @@ class DeleteCog(commands.Cog, name="Table Management"): await interaction.send(f'User "{user_name}" was removed from all tables.', ephemeral=True) + @delete.subcommand(description="Delete a category from a group. Deletion is perminant.") + async def category( + self, + interaction: nextcord.Interaction, + group_id: str = nextcord.SlashOption(name="group", description="Group to delete from.", required=True), + category_id: str = nextcord.SlashOption(name="category", description="Category to delete.", required=True), + ) -> None: + """ + Calls the delete confirmation view for the given group and category. + + Args: + interaction (nextcord.Interaction): Interaction of command. + group_id (str): Group to delete from. Is SlashOption. + category_id (str): Category to delete. Is SlashOption. + """ + await interaction.response.send_modal( + DeleteConfirmView(custom_id=create_delete_id(int(group_id), int(category_id))) + ) + + @delete.subcommand(description="Delete a group. Deletion is perminant.") + async def group( + self, + interaction: nextcord.Interaction, + group_id: str = nextcord.SlashOption(name="group", description="Group to delete.", required=True), + ) -> None: + """ + Calls the delete confirmation view for the given group. + + Args: + interaction (nextcord.Interaction): Interaction of command. + group_id (str): Group to delete from. Is SlashOption. + """ + await interaction.response.send_modal(DeleteConfirmView(custom_id=create_delete_id(int(group_id)))) + @delete.subcommand() async def user(self, interaction: nextcord.Interaction): """ @@ -201,6 +237,8 @@ class DeleteCog(commands.Cog, name="Table Management"): @from_category.on_autocomplete("group_id") @from_group.on_autocomplete("group_id") + @category.on_autocomplete("group_id") + @group.on_autocomplete("group_id") async def group_autocomplete(self, interaction: nextcord.Interaction, group_id: str): """ Autocomplete function for table names. @@ -212,6 +250,7 @@ class DeleteCog(commands.Cog, name="Table Management"): await interaction.response.send_autocomplete(autofill_groups(interaction.guild_id, group_id)) @from_category.on_autocomplete("category_id") + @category.on_autocomplete("category_id") async def category_autocomplete(self, interaction: nextcord.Interaction, category_id: str, group_id: str): """ Autocomplete function for category names. diff --git a/cogs/commands/views/delete_confirm.py b/cogs/commands/views/delete_confirm.py new file mode 100644 index 0000000..0296013 --- /dev/null +++ b/cogs/commands/views/delete_confirm.py @@ -0,0 +1,97 @@ +""" +Contains view and helper functions for confirming and processing deletes. +""" + +from typing import Optional + +import nextcord +from sqlalchemy import delete, select +from sqlalchemy.orm import Session + +# pylint: disable=import-error +from utilities.sql_data_classes import Category, CategoryGroup, engine +from utilities.utils import send_highscore_message + + +class DeleteConfirmView(nextcord.ui.Modal): + """ + View modal popup for confirming deletions. + """ + + def __init__(self, custom_id: str): + super().__init__(title="Warning, deletion is perminant.", custom_id=custom_id) + + self.checker = nextcord.ui.TextInput( + label='Type "delete" to confirm deletion.', + required=True, + placeholder="delete", + style=nextcord.TextInputStyle.short, + ) + self.add_item(self.checker) + + async def callback(self, interaction: nextcord.Interaction): + if self.checker.value.lower() != "delete": + await interaction.response.send_message('Response was not "delete". Canceling delete.') + return + + del_vals = parse_delete_id(self.custom_id) + + with Session(engine) as session: + stmt = select(CategoryGroup).where(CategoryGroup.GroupId == del_vals["group_id"]) + group = session.scalar(stmt) + if del_vals["category_id"] is None: + # delete entire group + session.delete(group) + # delete the message related to group + channel: nextcord.TextChannel = interaction.guild.get_channel(group.ChannelId) + message: nextcord.Message = await channel.fetch_message(group.MessageId) + await message.delete() + + else: + stmt = ( + delete(Category) + .where(Category.GroupId == del_vals["group_id"]) + .where(Category.CategoryId == del_vals["category_id"]) + ) + session.execute(stmt) + # update the message + await send_highscore_message(session, interaction.guild, group) + session.commit() + + await interaction.response.send_message("Deletion complete.", ephemeral=True) + + +def create_delete_id(group_id: int, category_id: Optional[int] = None) -> str: + """ + Creates a delete id string to tell the view what to delete. + + Args: + group_id (int): group id to delete + category_id (Optional[int]): category id to delete. Can be None. + + Returns: + str: string representation of the delete id. + """ + return f"delete-{group_id}-{category_id}" + + +def parse_delete_id(delete_id: str) -> dict: + """ + Returns a dict containing the values of a delete id. + + Args: + id (str): the delete id + + Returns: + dict: dict containing values from a delete id. + """ + vals = {} + group_id = delete_id.split("-")[1] + category_id = delete_id.split("-")[2] + vals["group_id"] = group_id + if category_id == "None": + vals["category_id"] = None + else: + vals["category_id"] = category_id + + return vals