Search code examples
pythondiscorddiscord.py

Discord.py 2.0 - Getting context for a HybridCommand


I am trying to write a slash command version of my help command, and I want to check if a user can run a command.
This is my code here:

@app_commands.command(name="help")
async def help(self, interaction: discord.Interaction, input: str = None):
    embed = discord.Embed(title="Test")

    cmd = self.bot.get_command(input)

    ctx: commands.Context = await self.bot.get_context(interaction)

    try:
        await cmd.can_run(ctx)
        embed.add_field(name="Usable by you:", value="Yes")
    except commands.CommandError as exc:
        embed.add_field(name="Usable by you:", value=f"No:\n{exc}")

    await ctx.send(embed=embed)

Now this works fine if I want to check for a normal command (commands.Command), however this does not work with Hybrid Commands (commands.HybridCommand). I read the docs for bot.get_context where it says:
In order for the custom context to be used inside an interaction-based context (such as HybridCommand) then this method must be overridden to return that class.
However I am not sure what this means exactly. How do I override it and what class do I need to return?

For completions sake, here is the error I get when I try to use this on a HybridCommand:

discord.app_commands.errors.CommandInvokeError: Command 'help' raised an exception: AttributeError: '_MissingSentinel' object has no attribute 'guild'

Again, when I use this on a normal command it works fine.

I would greatly appreciate any pointer!

Edit: An example of what I mean:

    @commands.command()
    @commands.has_permissions(administrator=True)
    async def test1(self, ctx):
        print("Test1")

    @commands.hybrid_command()
    @commands.has_permissions(administrator=True)
    async def test2(self, ctx):
        print("Test 2")

If you input /help test1, it will work fine, but error out on /help test2. Without the permission check it seems to also work fine.


Solution

  • Current Answer

    This can be resolved by updating discord.py to at least commit 36f039a1bffb835a555be8a43976397ba6eb9c76, by using pip install git+https://github.com/rapptz/discord.py in terminal.

    Outdated Answer

    Explanation

    It appears as though there is a bug with discord.py which causes this error. In _check_can_run for hybrid commands, it uses interaction._baton as the command context to check. interaction._baton is set to a missing sentinel value, so it runs into errors when discord.py tries to use it like a regular Context object.

    This can be patched pretty simply, although I'm not sure of any unintended side effects. Simply set interaction._baton to be the context you've just fetched. I've tested it with both test cases and they work:

    Code

    @bot.tree.command(name="help", guild=discord.Object(id=703732969160048731))
    async def help_command(interaction: discord.Interaction, parameter: str = None):
        embed = discord.Embed(title="Test")
    
        cmd = bot.get_command(parameter)
    
        ctx: commands.Context = await bot.get_context(interaction)
    
        interaction._baton = ctx  # sketchy, but it works
    
        try:
            await cmd.can_run(ctx)
            embed.add_field(name="Usable by you:", value="Yes")
        except commands.CommandError as exc:
            embed.add_field(name="Usable by you:", value=f"No:\n{exc}")
    
        await ctx.send(embed=embed)