Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get an error when use context_menu in cogs #7823

Closed
3 tasks done
ZebcoWeb opened this issue Apr 1, 2022 · 9 comments
Closed
3 tasks done

Get an error when use context_menu in cogs #7823

ZebcoWeb opened this issue Apr 1, 2022 · 9 comments
Labels
question This is a question about the library

Comments

@ZebcoWeb
Copy link

ZebcoWeb commented Apr 1, 2022

Summary

error for context menu

Reproduction Steps

I get this error when I use the context_menu in Cogs.

Minimal Reproducible Code

@app_commands.context_menu(name='my menu')
    async def qanda_choose_answer(self, interaction: discord.Interaction, message: discord.Message):
        if message.channel.type == discord.ChannelType.public_thread and message.channel.parent_id == Channel.QA_CHANNEL_ID:
  ...

Expected Results

I expected the menu to work properly and get to its parameters.

Actual Results

i get this error:
Extension 'cogs.qanda' raised an error: TypeError: context menu callbacks require 2 parameters, the first one being the annotation and the other one explicitly annotated with either discord.Message, discord.User, discord.Member, or a typing.Union of discord.Member and discord.User

Intents

all of them

System Information

  • Python v3.9.8-final
  • discord.py v2.0.0-alpha
    • discord.py pkg_resources: v2.0.0a4020+g3d914e0
  • aiohttp v3.7.4.post0
  • system info: Windows 10 10.0.22000

Checklist

  • I have searched the open issues for duplicates.
  • I have shown the entire traceback, if possible.
  • I have removed my token from display, if visible.

Additional Context

I looked for a solution on the library's Discord server but not found answer.

@ZebcoWeb ZebcoWeb added the unconfirmed bug A bug report that needs triaging label Apr 1, 2022
@AbstractUmbra
Copy link
Contributor

AbstractUmbra commented Apr 1, 2022

It is not supported to define these within a class, they must be free standing functions.

You can call bot.tree.add_command(...) in the extension setup and whatnot if you want the hot-reload functionality.

@Rapptz Rapptz added question This is a question about the library and removed unconfirmed bug A bug report that needs triaging labels Apr 3, 2022
@Rapptz
Copy link
Owner

Rapptz commented Apr 3, 2022

Context menus do not support group contexts (e.g. Cog or Group). This is for a few reasons.

  1. Within Discord itself, they cannot be bound to a group. There is no such thing as a "sub context menu" right now, and if it exists in the future it probably will not be in the same category as a Group would be.
  2. If added, the Group and the Cog would need to handle and maintain a mapping of context menus to eventually add back and remove from the tree. This complication and extra space doesn't seem worth it to me, especially considering you can only have 5 context menu commands anyway.
  3. The context menu would have to store bindings (the self parameter) which is an incredibly complicated thing to do that has been a common source of bugs within the library.

This is kind of ironic but this is one of those things that is actually significantly easier to do yourself rather than the library generalising it to fit to everyone's needs. For example, in a cog you can do the following:

class MyCog(commands.Cog):
    def __init__(self, bot: commands.Bot) -> None:
        self.bot = bot
        self.ctx_menu = app_commands.ContextMenu(
            name='Cool Command Name',
            callback=self.my_cool_context_menu,
        )
        self.bot.tree.add_command(self.ctx_menu)

    async def cog_unload(self) -> None:
        self.bot.tree.remove_command(self.ctx_menu.name, type=self.ctx_menu.type)

    # You can add checks too
    @app_commands.checks.has_permissions(ban_members=True)
    # @app_commads.guilds(12345)
    async def my_cool_context_menu(self, interaction: discord.Interaction, message: discord.Message) -> None:
        await interaction.response.send_message('hello...')

This lets you manually add the context menu to the bot tree when a cog is created. The Python runtime handles the self binding for you as well, rather than the library.

@Rapptz Rapptz closed this as completed Apr 3, 2022
@ZebcoWeb
Copy link
Author

ZebcoWeb commented Apr 3, 2022

Thanks for the explanation

asthomp added a commit to asthomp/Stoplight-Bot that referenced this issue Apr 1, 2023
…ure how I got it working before. Regardless, removed all decorators and registered the command manually in the cog init function. Since I'm not using decorators, also included a function to remove the commands when the cog is unloaded. Credit: solution based on code by Rapptz that can be found here: Rapptz/discord.py#7823 (comment)
@vaz1306011
Copy link

Context menus do not support group contexts (e.g. Cog or Group). This is for a few reasons.

  1. Within Discord itself, they cannot be bound to a group. There is no such thing as a "sub context menu" right now, and if it exists in the future it probably will not be in the same category as a Group would be.
  2. If added, the Group and the Cog would need to handle and maintain a mapping of context menus to eventually add back and remove from the tree. This complication and extra space doesn't seem worth it to me, especially considering you can only have 5 context menu commands anyway.
  3. The context menu would have to store bindings (the self parameter) which is an incredibly complicated thing to do that has been a common source of bugs within the library.

This is kind of ironic but this is one of those things that is actually significantly easier to do yourself rather than the library generalising it to fit to everyone's needs. For example, in a cog you can do the following:

class MyCog(commands.Cog):
    def __init__(self, bot: commands.Bot) -> None:
        self.bot = bot
        self.ctx_menu = app_commands.ContextMenu(
            name='Cool Command Name',
            callback=self.my_cool_context_menu,
        )
        self.bot.tree.add_command(self.ctx_menu)

    async def cog_unload(self) -> None:
        self.bot.tree.remove_command(self.ctx_menu.name, type=self.ctx_menu.type)

    # You can add checks too
    @app_commands.checks.has_permissions(ban_members=True)
    # @app_commads.guilds(12345)
    async def my_cool_context_menu(self, interaction: discord.Interaction, message: discord.Message) -> None:
        await interaction.response.send_message('hello...')

This lets you manually add the context menu to the bot tree when a cog is created. The Python runtime handles the self binding for you as well, rather than the library.

But how do I handle the error?
The general command is like this

class MyCog(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

    @commands.command()
    async def echo(self, ctx, arg):
        await ctx.send(arg)

    @echo.error
    async def echo_error(self, ctx, error):
        await ctx.send("There was an error")
        if isinstance(error, command.MissingRequiredArgument):
            await ctx.send("MIssing Required Argument")
        else:
            raise error

in this case

class MyCog(commands.Cog):
    def __init__(self, bot: commands.Bot) -> None:
        self.bot = bot
        self.ctx_menu = app_commands.ContextMenu(
            name='Cool Command Name',
            callback=self.my_cool_context_menu,
        )
        self.bot.tree.add_command(self.ctx_menu)

    @app_commands.checks.has_permissions(ban_members=True)
    async def my_cool_context_menu(self, interaction: discord.Interaction, message: discord.Message) -> None:
        await interaction.response.send_message('hello...')
        
    @my_cool_context_menu.error
    async def my_cool_context_menu_error(self, interaction: discord.Interaction, error: Exception) -> None:
        await interaction.response.send_message('error...')

This will report the error AttributeError: 'function' object has no attribute 'error'

@Rapptz
Copy link
Owner

Rapptz commented Oct 26, 2023

You would do self.ctx_menu.error(self.my_cool_context_menu_error) inside __init__ instead.

@vaz1306011
Copy link

You would do self.ctx_menu.error(self.my_cool_context_menu_error) inside __init__ instead.

oh,thanks

@glass-ships
Copy link

Sorry - I know this issue is closed and the discussion is somewhat old, but I couldn't find much info in doing research.

How would one define multiple context menu interactions in one single cog?
Or would the current implementation require separate cogs for each ContextMenu?

@AbstractUmbra
Copy link
Contributor

You can definitely do more than one per Cog class.

A small example:

class MyCog(commands.Cog):
  def __init__(self, bot: commands.Bot, /) -> None:
    self.bot: commands.Bot = bot
    self.ctx_one = app_commands.ContextMenu(name="Context Menu 1", callback=self.ctx_one_callback)
    self.bot.tree.add_command(self.ctx_one)
    self.ctx_two = app_commands.ContextMenu(name="Context Menu 2", callback=self.ctx_two_callback)
    self.bot.tree.add_command(self.ctx_two)

  def cog_unload(self) -> None:
    # remove both on cog unload to not clog up the tree incorrectly
    self.bot.tree.remove_command(self.ctx_one.name, type=self.ctx_one.type)
    self.bot.tree.remove_command(self.ctx_two.name, type=self.ctx_two.type)

Just define the callbacks as normal.

@glass-ships
Copy link

Awesome, thanks Umbra!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question This is a question about the library
Projects
None yet
Development

No branches or pull requests

5 participants