"""Commands for creating items in bot.""" import nextcord from nextcord.ext import commands from sqlalchemy import exc, select from sqlalchemy.orm import Session from sqlmodel import col from database.db import engine from database.models import Category, CategoryGroup from utilities.utils import MAX_LINE_LENGTH, autofill_groups, send_highscore_message class CreateCog(commands.Cog, name="Create Cog"): """Class holds commands for creating things.""" def __init__(self, bot: commands.Bot) -> None: """Default constructor for CreateCog class.""" self._bot = bot @nextcord.slash_command(default_member_permissions=nextcord.Permissions(administrator=True)) async def create(self, interaction: nextcord.Interaction) -> None: """Parent slash command for creating things. Never called.""" @create.subcommand(description="Create a new Category. Must not exist already in Group.") async def category( # noqa: PLR0913 need all these arguments because discord command. self, interaction: nextcord.Interaction, group_id: str = nextcord.SlashOption(name="group", description="Group to add Category to.", required=True), category_name: str = nextcord.SlashOption(description="Name of new Category.", required=True), is_ascending: bool = nextcord.SlashOption(description="If lower value is a better score.", required=True), # noqa: FBT001 is_time_record: bool = nextcord.SlashOption( # noqa: FBT001 description="If the score should be shown in time format. Cannot be changed.", required=True ), is_validated: bool = nextcord.SlashOption( # noqa: FBT001 description="If scores need to be validated before posted.", required=True ), size: int = nextcord.SlashOption( description="How many scores to post in Category. Default is 3.", required=False, default=3, min_value=1 ), ) -> None: """ Creates a new category to be placed into group_id group. Fails if the category name already exists within the group. Then calls send_highscore_message(). Args: interaction (nextcord.Interaction): Interaction for the command. group_id (str): Id of group to add category to. Is SlashOption. category_name (str): Name of new category. Is SlashOption. is_ascending (bool): If the scores are sorted ascending or not. Is SlashOption. is_time_record (bool): If the scores are a time record or not. Is SlashOption. is_validated (bool): If the scores should be validated or not. Is SlashOption. size (int): How many sores to show in the category message. Is SlashOption. """ new_category = Category( name=category_name, group_id=int(group_id), size=size, is_ascending=is_ascending, is_time_record=is_time_record, is_validated=is_validated, ) # Open connection to engine and add new category. with Session(engine) as session: stmt = select(CategoryGroup).where(col(CategoryGroup.id) == int(group_id)) group = session.scalar(stmt) try: session.add(new_category) session.commit() await send_highscore_message(session, interaction.guild, group) await interaction.response.send_message( f'"{category_name}" has been added to "{group.name}"!', ephemeral=True ) except exc.IntegrityError: # Category name already exists in group session.rollback() await interaction.response.send_message( f'"{category_name}" already exists in "{group.name}". Please rename.', ephemeral=True ) @create.subcommand(description="Create a new Category Group. Must not exist already for Guild.") async def group( self, interaction: nextcord.Interaction, group_name: str = nextcord.SlashOption(description="Name of new Group.", required=True), pad_length: int = nextcord.SlashOption( description="Number of characters wide a category is printed. Default is 26.", required=False, default=26, min_value=7, max_value=MAX_LINE_LENGTH, ), username_length: int = nextcord.SlashOption( description="Number of characters of a username to display. Default is 12.", required=False, default=12, min_value=1, max_value=30, ), channel: nextcord.TextChannel | None = nextcord.SlashOption( description="Channel to post Group in. If left empty, defaults to current channel.", required=False, default=None, ), ) -> None: """ Creates a new category group to be placed into the database with the guild's id. Fails if the category group name already exists within the guild. Then calls send_highscore_message(). Args: interaction (nextcord.Interaction): Interaction for the command. group_name (str): Name of new Group. Is SlashOption. pad_length (int): Number of characters wide a category is printed. Is SlashOption. username_length (int): Number of characters of a username to display. Is SlashOption. channel (Optional[nextcord.abc.GuildChannel]): Channel to post group in. If None, defaults to current channel. Is SlashOption. """ # if no channel provided, use current channel. if channel is None: channel_id: int = interaction.channel.id else: channel_id: int = channel.id new_group = CategoryGroup( name=group_name, channel_id=channel_id, guild_id=interaction.guild_id, pad_length=pad_length, username_length=username_length, ) with Session(engine) as session: try: session.add(new_group) session.commit() await send_highscore_message(session, interaction.guild, new_group) await interaction.response.send_message(f'"{group_name}" has been created!', ephemeral=True) except exc.IntegrityError: # Category Group already exists in guild session.rollback() await interaction.response.send_message( f'"{group_name}" already exists in "{interaction.guild.name}". Please rename.', ephemeral=True ) @category.on_autocomplete("group_id") async def group_autocomplete(self, interaction: nextcord.Interaction, group_id: str) -> None: """ Collects list of group names and compares to input group name for autocomplete assistance. Args: interaction (nextcord.Interaction): Interaction with command. group_id (str): String to run autocomplete compared to. """ await interaction.response.send_autocomplete(autofill_groups(interaction.guild_id, group_id))