Skip to content

Hybrid commands only accept Discord flag decorators in certain orders #10058

@Flame442

Description

@Flame442

Summary

The ordering of @app_commands.allowed_installs and @commands.hybrid_command affects whether the allows_installs decorator will apply.

Reproduction Steps

  1. Load the minimal reproduction cog below
  2. bot.tree.get_command("beforecom").allowed_installs is the expected AppInstallationType object with the flags applied
  3. bot.tree.get_command("aftercom").allowed_installs is None

Minimal Reproducible Code

import discord
from discord.ext import commands
from discord import app_commands

class Test(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
    
    @commands.hybrid_command()
    @app_commands.allowed_installs(guilds=True, users=True)
    async def beforecom(self, ctx: commands.Context) -> None:
        await ctx.send("Example")
    
    @app_commands.allowed_installs(guilds=True, users=True)
    @commands.hybrid_command()
    async def aftercom(self, ctx: commands.Context) -> None:
        await ctx.send("Example")

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

Expected Results

Either orientation of the decorator should apply the allowed_installs setting to the hybrid command.

Actual Results

Only cases where the allowed_installs decorator runs first (ie, is lower in the decorator list) actually cause the decorator to work.

Intents

discord.Intents.all()

System Information

  • Python v3.8.5-final
  • discord.py v2.4.0-final
  • aiohttp v3.9.5
  • system info: Windows 10 10.0.19041

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

Also tested with app_commands.guild_only, which has the same issue. I do not know the full scope of decorators affected by this issue, but I would imagine it is all non-check app command decorators.


if isinstance(f, (Command, Group, ContextMenu)):
allowed_installs = f.allowed_installs or AppInstallationType()
f.allowed_installs = allowed_installs
else:
allowed_installs = getattr(f, '__discord_app_commands_installation_types__', None) or AppInstallationType()
f.__discord_app_commands_installation_types__ = allowed_installs # type: ignore # Runtime attribute assignment

In the decorator logic, the else case handles the currently working case, which is when the decorator is used while the object is still a function. Later, when the app command is initialized, it properly includes the allowed_installs setting.

For hybrid commands specifically, the object that is returned from @commands.hybrid_command is a commands.HybridCommand, which does not inherit from app_commands.Command. Only commands.HybridAppCommand inherits from app_commands.Command, which would require accessing the .app_command attribute of the HybridCommand.

A potential fix is to add a 3rd case to these decorators, which checks for HybridCommands and pulls out the .app_command. I am not familiar enough with the flow of information for app command internals to know if that would be a satisfactory solution.

Metadata

Metadata

Assignees

No one assigned

    Labels

    unconfirmed bugA bug report that needs triaging

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions