Pycord-Development / pycord

Pycord is a modern, easy to use, feature-rich, and async ready API wrapper for Discord written in Python
https://docs.pycord.dev
MIT License
2.74k stars 462 forks source link

SlashCommandGroup does not register correctly #1649

Closed M1N0RM1N3R closed 2 years ago

M1N0RM1N3R commented 2 years ago

Summary

SlashCommandGroups don't appear to be registering properly--when I start up my bot, the commands in SlashCommandGroups appear broken, whereas commands not in a group work fine.

Reproduction Steps

  1. Create a SlashCommandGroup via foo_cmd = discord.SlashCommandGroup('foo')
  2. Add commands to it via @foo_cmd.command()
  3. Add the group to the bot via bot.add_application_command(foo_cmd)
  4. Add a regular command via @bot.slash_command() to compare and contrast for good measure

Minimal Reproducible Code

@bot.slash_command()
async def ping(
    ctx: discord.ApplicationContext,
):
    await ctx.send_response(f"πŸ“ Pong! Latency: {bot.latency} secs. Active since <t:{int(login_time)}>. Version: {VERSION}", ephemeral=True)

profile_cmd = discord.SlashCommandGroup('profile')

@profile_cmd.command(name='create')
async def profile_create(
    ctx: discord.ApplicationContext,
    name: str
):
    profile = Profile(ctx.author.id, name)
    db.add(profile)
    await ctx.send_response('βœ… Profile created:', embed=profile.profile_embed(), ephemeral=True)

@profile_cmd.command(name='get')
async def profile_get(
    ctx: discord.ApplicationContext,
    user: discord.Option(
        discord.Member, description='Select another user to look up their profile, or leave alone to get your own. (#bars)', default=None)
):
    user: discord.Member = user or ctx.author

    profile: Profile = db.get(lambda x: x.discord_id == user.id)
    if profile:
        await ctx.send_response(embed=profile.profile_embed(), ephemeral=True)
    else:
        await ctx.send_response('❌ Profile not found.', ephemeral=True)

bot.add_application_command(profile_cmd)

Expected Results

Command shows correct args in Discord client, command runs as programmed

Actual Results

Commands created via @bot.slash_command() such as the /ping command in the MPC example function as expected. The commands in the SlashCommandGroup appear blank, with only the name. No arguments appear at all. When attempting to run one of the commands in the group, Discord shows a "The application did not respond" error and the terminal shows the following traceback. When I change the @profile_cmd.command() to @bot.slash_command(), the command works perfectly.

Ignoring exception in on_interaction
Traceback (most recent call last):
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/commands/core.py", line 127, in wrapped
    ret = await coro(arg)
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/commands/core.py", line 1201, in _invoke
    await command.invoke(ctx)
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/commands/core.py", line 356, in invoke
    await self.prepare(ctx)
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/commands/core.py", line 278, in prepare
    if not await self.can_run(ctx):
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/commands/core.py", line 373, in can_run
    local_check = cog._get_overridden_method(cog.cog_check)
AttributeError: '_MissingSentinel' object has no attribute '_get_overridden_method'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/bot.py", line 1009, in invoke_application_command
    await ctx.command.invoke(ctx)
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/commands/core.py", line 359, in invoke
    await injected(ctx)
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/commands/core.py", line 135, in wrapped
    raise ApplicationCommandInvokeError(exc) from exc
discord.errors.ApplicationCommandInvokeError: Application Command raised an exception: AttributeError: '_MissingSentinel' object has no attribute '_get_overridden_method'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/client.py", line 382, in _run_event
    await coro(*args, **kwargs)
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/bot.py", line 1058, in on_interaction
    await self.process_application_commands(interaction)
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/bot.py", line 756, in process_application_commands
    await self.invoke_application_command(ctx)
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/bot.py", line 1013, in invoke_application_command
    await ctx.command.dispatch_error(ctx, exc)
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/commands/core.py", line 401, in dispatch_error
    local = cog.__class__._get_overridden_method(cog.cog_command_error)
AttributeError: type object '_MissingSentinel' has no attribute '_get_overridden_method'
Ignoring exception in command profile create:
Traceback (most recent call last):
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/commands/core.py", line 127, in wrapped
    ret = await coro(arg)
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/commands/core.py", line 1201, in _invoke
    await command.invoke(ctx)
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/commands/core.py", line 356, in invoke
    await self.prepare(ctx)
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/commands/core.py", line 278, in prepare
    if not await self.can_run(ctx):
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/commands/core.py", line 373, in can_run
    local_check = cog._get_overridden_method(cog.cog_check)
AttributeError: '_MissingSentinel' object has no attribute '_get_overridden_method'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/bot.py", line 1009, in invoke_application_command
    await ctx.command.invoke(ctx)
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/commands/core.py", line 359, in invoke
    await injected(ctx)
  File "/home/<username>/.local/lib/python3.10/site-packages/discord/commands/core.py", line 135, in wrapped
    raise ApplicationCommandInvokeError(exc) from exc
discord.errors.ApplicationCommandInvokeError: Application Command raised an exception: AttributeError: '_MissingSentinel' object has no attribute '_get_overridden_method'

Intents

None at the moment

System Information

- Python v3.10.6-final
- py-cord v2.1.3-final
- aiohttp v3.8.3
- system info: Linux 5.15.0-48-generic #54-Ubuntu SMP Fri Aug 26 13:26:29 UTC 2022

Checklist

Additional Context

I believe this has worked properly in the past when I was running on Windows. It could just be me, but it suddenly stopped working after I switched over to Ubuntu 22.04 last weekend.

JustaSqu1d commented 2 years ago

Does the workaround of using bot.create_group not solve this issue?

M1N0RM1N3R commented 2 years ago

Does the workaround of using bot.create_group not solve this issue?

Hmm, lemme try that... Edit: Nada. IG to get it working rn I can just comment out @profile_cmd.command('create') and replace it with @bot.slash_command('profile_create') just to weather the storm till a fix can be found.

voidus commented 2 years ago

As a workaround, you can add my_command.cog = None after the command creation like so:

g = bot.create_group("g")

@b.command()
async def foo(ctx):
  ctx.respond("heya")

foo.cog = None

No idea if this has any side effects though.

EDIT: nevermind that breaks everything.

EDIT2: Here's the horrible, horrible workaround that seems to actually work. I'm pretty sure it will horribly break on updating anything and could have horrible side effects such as setting your computer on fire or posting your credit card data on linkedin. Please don't use it if you don't know what you're doing.

At the top of the file:

# OH THE HORRORS https://github.com/Pycord-Development/pycord/issues/1649
import discord.utils

discord.utils._MissingSentinel._get_overridden_method = lambda *args, **kwargs: None
discord.utils._MissingSentinel.cog_check = lambda *args, **kwargs: True
discord.utils._MissingSentinel.cog_before_invoke = lambda *args, **kwargs: None
discord.utils._MissingSentinel.cog_after_invoke = lambda *args, **kwargs: None
discord.utils._MissingSentinel.cog_command_error = lambda *args, **kwargs: None

When definint your command:

g = bot.create_group(g)

@g.command
async def cmd(_cog, ctx: ApplicationContext): ...

Note the added but unused _cog parameter.

This works because it makes _MissingSentinel a kind of null object, which isn't the developers intention afaict.

The proper fix would maybe be to examine all places where cog is checked for is not None and add or isinstance(the_thing.cog, _MissingSentinel) but I don't really understand the code enough to gauge the impact.

JustaSqu1d commented 2 years ago

1662