pythondiscorddiscord.py

Registering hybrid command group trees for discord.py


I've run into a problem with the following hybrid command group I'm trying to create in discord.py:

    @commands.hybrid_group(name='event', description='Lists events attended by character')
    @commands.guild_only()
    async def character_events(self, ctx, event_type: str = None):
        if event_type == None:
            character_name = str(ctx.author.nick) or str(ctx.author.name)
            await self.send_message_chunks(ctx, character_name.lower().title())
            return
        elif event_type is not None and str(event_type.lower()) not in ['start', 'end', 'add', 'delete', 'master']:
            await self.send_message_chunks(ctx, event_type.lower().title())
            return

    @character_events.command(name='add', description='Adds a character event from a master record event, or manually adds raid ticks>')
    @commands.has_permissions(administrator=True)
    async def character_add_event(self, ctx, event_name: str = None, event_value: str = None, event_class: str = None):
        if event_class is not None and event_class.lower() == 'channel' and ctx.author.guild_permissions.administrator:
            await self.insert_event_channel(ctx, event_name, event_value, 'channel')
            return
        elif event_class is not None and event_class.lower() == 'split' and ctx.author.guild_permissions.administrator:
            await self.insert_event_channel(ctx, event_name, event_value, 'split')
            return
        elif event_value.lower() == 'all':
            await self.insert_event(ctx, event_name, event_value)
            return
        else:
            await self.insert_event(ctx, event_name, event_value)
            return

    @character_events.command(name='delete', description='Deletes a character event: <character_name/all> <event_id>')
    @commands.has_permissions(administrator=True)
    async def character_delete_event(self, ctx, event_name: str = None, event_value: str = None):
        if event_name.lower() == 'all':
            await self.delete_event(ctx, event_name, event_value)
            return
        else:
            print('delete detected')
            await self.delete_event(ctx, event_name, event_value)
            return

    @character_events.command(name='start', description='Starts an event: <event_name> <event_value> <event_duration> <event_ticks>')
    @commands.has_permissions(administrator=True)
    async def start_timed_event(self, ctx, event_name: str = None, event_value: int = None, event_class: int = None, event_status: str = None, event_ticks: int = None):
            print('start detected')
            await self.event_channel_timer(ctx, event_name, event_value, event_class, event_status, event_ticks)

    @character_events.command(name='master', description='Display or alter the master record: <add/delete> <event_name/event_id>')
    async def master_modify(self, ctx, event_name: str = None, event_value: str = None):
        if event_name == None:
            await self.send_message_chunks_master(ctx, event_name)
        elif event_name.lower() == 'add' and ctx.author.guild_permissions.administrator:
            await self.master_record_modify(ctx, event_name, event_value, 'add')
        elif event_name.lower() == 'delete' and ctx.author.guild_permissions.administrator:
            await self.master_record_modify(ctx, event_name, event_value, 'delete')

async def setup(bot):
    await bot.add_cog(Events(bot))

Basically 'event' is the parent command and add, delete, start, and master are the subcommands that I want to implement (example: /event add "New Event", etc). The prefix command works fine with the code as is, but I can't get it to register them as slash commands. In my main.py this is what I use to load all the cogs:

@bot.event
async def on_ready():
    loaded_cogs = []  # Create a list to track loaded cogs
    activity = discord.Game(name="EverQuest")

    for filename in os.listdir("./cogs"):
        if filename.endswith(".py"):
            cog_name = "cogs.{0}".format(filename[:-3])
            if cog_name not in loaded_cogs:  # Check if the cog hasn't been loaded yet
                try:
                    await bot.load_extension(cog_name)
                    loaded_cogs.append(cog_name)  # Add the loaded cog to the list
                    print(f'"{cog_name}" Cog loaded')
                except Exception as e:
                    print(f'Failed to load "{cog_name}" Cog: {e}')

I feel like I'm missing something obvious on how to register these (or maybe it's not possible in a hybrid command with nested commands?) From the discord.py documentation they say:

The following are currently not supported by hybrid commands:

Variable number of arguments. e.g. *arg: int

Group commands with a depth greater than 1.

But my commands are within these parameters right?


Solution

  • To use slash commands you need to register them with Discord. It is not recommended that you do this in the on_ready event as you only need to sync your slash commands when you create a new one, remove one, or edit one. It's best practice to use a prefixed command that only you can use to sync these.

    You can do something that looks like this:

    @bot.command(name="Sync", description="Sync slash commands")
    @commands.is_owner()
    @commands.guild_only()
    async def sync(ctx: Context):
        ctx.bot.tree.clear_commands(guild=ctx.guild)
        ctx.bot.tree.copy_global_to(guild=ctx.guild)
        synced = await ctx.bot.tree.sync(guild=ctx.guild)
        print(synced)
        await ctx.reply(f"Synced {len(synced)} commands", mention_author=False)