mirror of
				https://github.com/Rapptz/discord.py.git
				synced 2025-11-03 23:12:56 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			968 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			968 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""
 | 
						|
The MIT License (MIT)
 | 
						|
 | 
						|
Copyright (c) 2015-present Rapptz
 | 
						|
 | 
						|
Permission is hereby granted, free of charge, to any person obtaining a
 | 
						|
copy of this software and associated documentation files (the "Software"),
 | 
						|
to deal in the Software without restriction, including without limitation
 | 
						|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
						|
and/or sell copies of the Software, and to permit persons to whom the
 | 
						|
Software is furnished to do so, subject to the following conditions:
 | 
						|
 | 
						|
The above copyright notice and this permission notice shall be included in
 | 
						|
all copies or substantial portions of the Software.
 | 
						|
 | 
						|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 | 
						|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
						|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
						|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
						|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
						|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
						|
DEALINGS IN THE SOFTWARE.
 | 
						|
"""
 | 
						|
 | 
						|
from __future__ import annotations
 | 
						|
 | 
						|
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Dict, List, Tuple, Type, TypeVar, Union, Optional
 | 
						|
 | 
						|
import discord
 | 
						|
import inspect
 | 
						|
from discord import app_commands
 | 
						|
from discord.utils import MISSING, maybe_coroutine, async_all
 | 
						|
from .core import Command, Group
 | 
						|
from .errors import BadArgument, CommandRegistrationError, CommandError, HybridCommandError, ConversionError, DisabledCommand
 | 
						|
from .converter import Converter, Range, Greedy, run_converters, CONVERTER_MAPPING
 | 
						|
from .parameters import Parameter
 | 
						|
from .flags import is_flag, FlagConverter
 | 
						|
from .cog import Cog
 | 
						|
from .view import StringView
 | 
						|
 | 
						|
if TYPE_CHECKING:
 | 
						|
    from typing_extensions import Self, ParamSpec, Concatenate, Unpack
 | 
						|
    from ._types import ContextT, Coro, BotT
 | 
						|
    from .bot import Bot
 | 
						|
    from .context import Context
 | 
						|
    from discord.app_commands.commands import (
 | 
						|
        Check as AppCommandCheck,
 | 
						|
        AutocompleteCallback,
 | 
						|
        ChoiceT,
 | 
						|
    )
 | 
						|
    from .core import _CommandKwargs
 | 
						|
 | 
						|
    class _HybridCommandKwargs(_CommandKwargs, total=False):
 | 
						|
        guild_ids: list[int]
 | 
						|
        guild_only: bool
 | 
						|
        default_permissions: bool
 | 
						|
        nsfw: bool
 | 
						|
        with_app_command: bool
 | 
						|
 | 
						|
    class _HybridCommandDecoratorKwargs(_HybridCommandKwargs, total=False):
 | 
						|
        description: Union[str, app_commands.locale_str]
 | 
						|
 | 
						|
    class _HybridGroupKwargs(_HybridCommandDecoratorKwargs, total=False):
 | 
						|
        with_app_command: bool
 | 
						|
        guild_ids: list[int]
 | 
						|
        guild_only: bool
 | 
						|
        default_permissions: bool
 | 
						|
        nsfw: bool
 | 
						|
        description: str
 | 
						|
        case_insensitive: bool
 | 
						|
 | 
						|
    class _HybridGroupDecoratorKwargs(_HybridGroupKwargs, total=False):
 | 
						|
        description: Union[str, app_commands.locale_str]
 | 
						|
        fallback: Optional[str]
 | 
						|
        fallback_locale: Optional[app_commands.locale_str]
 | 
						|
 | 
						|
 | 
						|
__all__ = (
 | 
						|
    'HybridCommand',
 | 
						|
    'HybridGroup',
 | 
						|
    'hybrid_command',
 | 
						|
    'hybrid_group',
 | 
						|
)
 | 
						|
 | 
						|
T = TypeVar('T')
 | 
						|
U = TypeVar('U')
 | 
						|
CogT = TypeVar('CogT', bound='Cog')
 | 
						|
CommandT = TypeVar('CommandT', bound='Command[Any, ..., Any]')
 | 
						|
# CHT = TypeVar('CHT', bound='Check')
 | 
						|
GroupT = TypeVar('GroupT', bound='Group[Any, ..., Any]')
 | 
						|
_NoneType = type(None)
 | 
						|
 | 
						|
if TYPE_CHECKING:
 | 
						|
    P = ParamSpec('P')
 | 
						|
    P2 = ParamSpec('P2')
 | 
						|
 | 
						|
    CommandCallback = Union[
 | 
						|
        Callable[Concatenate[CogT, ContextT, P], Coro[T]],
 | 
						|
        Callable[Concatenate[ContextT, P], Coro[T]],
 | 
						|
    ]
 | 
						|
else:
 | 
						|
    P = TypeVar('P')
 | 
						|
    P2 = TypeVar('P2')
 | 
						|
 | 
						|
 | 
						|
class _CallableDefault:
 | 
						|
    __slots__ = ('func',)
 | 
						|
 | 
						|
    def __init__(self, func: Callable[[Context], Any]) -> None:
 | 
						|
        self.func: Callable[[Context], Any] = func
 | 
						|
 | 
						|
    @property
 | 
						|
    def __class__(self) -> Any:
 | 
						|
        return _NoneType
 | 
						|
 | 
						|
 | 
						|
def is_converter(converter: Any) -> bool:
 | 
						|
    return (inspect.isclass(converter) and issubclass(converter, Converter)) or isinstance(converter, Converter)
 | 
						|
 | 
						|
 | 
						|
def is_transformer(converter: Any) -> bool:
 | 
						|
    return hasattr(converter, '__discord_app_commands_transformer__') or hasattr(
 | 
						|
        converter, '__discord_app_commands_transform__'
 | 
						|
    )
 | 
						|
 | 
						|
 | 
						|
def required_pos_arguments(func: Callable[..., Any]) -> int:
 | 
						|
    sig = inspect.signature(func)
 | 
						|
    return sum(p.default is p.empty for p in sig.parameters.values())
 | 
						|
 | 
						|
 | 
						|
class ConverterTransformer(app_commands.Transformer):
 | 
						|
    def __init__(self, converter: Any, parameter: Parameter) -> None:
 | 
						|
        super().__init__()
 | 
						|
        self.converter: Any = converter
 | 
						|
        self.parameter: Parameter = parameter
 | 
						|
 | 
						|
        try:
 | 
						|
            module = converter.__module__
 | 
						|
        except AttributeError:
 | 
						|
            pass
 | 
						|
        else:
 | 
						|
            if module is not None and (module.startswith('discord.') and not module.endswith('converter')):
 | 
						|
                self.converter = CONVERTER_MAPPING.get(converter, converter)
 | 
						|
 | 
						|
    async def transform(self, interaction: discord.Interaction, value: str, /) -> Any:
 | 
						|
        ctx = interaction._baton
 | 
						|
        converter = self.converter
 | 
						|
        ctx.current_parameter = self.parameter
 | 
						|
        ctx.current_argument = value
 | 
						|
        try:
 | 
						|
            if inspect.isclass(converter) and issubclass(converter, Converter):
 | 
						|
                if inspect.ismethod(converter.convert):
 | 
						|
                    return await converter.convert(ctx, value)
 | 
						|
                else:
 | 
						|
                    return await converter().convert(ctx, value)
 | 
						|
            elif isinstance(converter, Converter):
 | 
						|
                return await converter.convert(ctx, value)
 | 
						|
        except CommandError:
 | 
						|
            raise
 | 
						|
        except Exception as exc:
 | 
						|
            raise ConversionError(converter, exc) from exc  # type: ignore
 | 
						|
 | 
						|
 | 
						|
class CallableTransformer(app_commands.Transformer):
 | 
						|
    def __init__(self, func: Callable[[str], Any]) -> None:
 | 
						|
        super().__init__()
 | 
						|
        self.func: Callable[[str], Any] = func
 | 
						|
 | 
						|
    async def transform(self, interaction: discord.Interaction, value: str, /) -> Any:
 | 
						|
        try:
 | 
						|
            return self.func(value)
 | 
						|
        except CommandError:
 | 
						|
            raise
 | 
						|
        except Exception as exc:
 | 
						|
            raise BadArgument(f'Converting to "{self.func.__name__}" failed') from exc
 | 
						|
 | 
						|
 | 
						|
class GreedyTransformer(app_commands.Transformer):
 | 
						|
    def __init__(self, converter: Any, parameter: Parameter) -> None:
 | 
						|
        super().__init__()
 | 
						|
        self.converter: Any = converter
 | 
						|
        self.parameter: Parameter = parameter
 | 
						|
 | 
						|
    async def transform(self, interaction: discord.Interaction, value: str, /) -> Any:
 | 
						|
        view = StringView(value)
 | 
						|
        result = []
 | 
						|
        ctx = interaction._baton
 | 
						|
        ctx.current_parameter = parameter = self.parameter
 | 
						|
        converter = self.converter
 | 
						|
 | 
						|
        while True:
 | 
						|
            view.skip_ws()
 | 
						|
            ctx.current_argument = arg = view.get_quoted_word()
 | 
						|
            if arg is None:
 | 
						|
                break
 | 
						|
 | 
						|
            # This propagates the exception
 | 
						|
            converted = await run_converters(ctx, converter, arg, parameter)
 | 
						|
            result.append(converted)
 | 
						|
 | 
						|
        return result
 | 
						|
 | 
						|
 | 
						|
def replace_parameter(
 | 
						|
    param: inspect.Parameter,
 | 
						|
    converter: Any,
 | 
						|
    callback: Callable[..., Any],
 | 
						|
    original: Parameter,
 | 
						|
    mapping: Dict[str, inspect.Parameter],
 | 
						|
) -> inspect.Parameter:
 | 
						|
    try:
 | 
						|
        # If it's a supported annotation (i.e. a transformer) just let it pass as-is.
 | 
						|
        app_commands.transformers.get_supported_annotation(converter)
 | 
						|
    except TypeError:
 | 
						|
        # Fallback to see if the behaviour needs changing
 | 
						|
        origin = getattr(converter, '__origin__', None)
 | 
						|
        args = getattr(converter, '__args__', [])
 | 
						|
        if isinstance(converter, Range):  # type: ignore # Range is not an Annotation at runtime
 | 
						|
            r = converter
 | 
						|
            param = param.replace(annotation=app_commands.Range[r.annotation, r.min, r.max])  # type: ignore
 | 
						|
        elif isinstance(converter, Greedy):
 | 
						|
            # Greedy is "optional" in ext.commands
 | 
						|
            # However, in here, it probably makes sense to make it required.
 | 
						|
            # I'm unsure how to allow the user to choose right now.
 | 
						|
            inner = converter.converter
 | 
						|
            if inner is discord.Attachment:
 | 
						|
                raise TypeError('discord.Attachment with Greedy is not supported in hybrid commands')
 | 
						|
 | 
						|
            param = param.replace(annotation=GreedyTransformer(inner, original))
 | 
						|
        elif is_flag(converter):
 | 
						|
            callback.__hybrid_command_flag__ = (param.name, converter)
 | 
						|
            descriptions = {}
 | 
						|
            renames = {}
 | 
						|
            for flag in converter.__commands_flags__.values():
 | 
						|
                name = flag.attribute
 | 
						|
                flag_param = inspect.Parameter(
 | 
						|
                    name=name,
 | 
						|
                    kind=param.kind,
 | 
						|
                    default=flag.default if flag.default is not MISSING else inspect.Parameter.empty,
 | 
						|
                    annotation=flag.annotation,
 | 
						|
                )
 | 
						|
                pseudo = replace_parameter(flag_param, flag.annotation, callback, original, mapping)
 | 
						|
                if name in mapping:
 | 
						|
                    raise TypeError(f'{name!r} flag would shadow a pre-existing parameter')
 | 
						|
                if flag.description is not MISSING:
 | 
						|
                    descriptions[name] = flag.description
 | 
						|
                if flag.name != flag.attribute:
 | 
						|
                    renames[name] = flag.name
 | 
						|
                if pseudo.default is not pseudo.empty:
 | 
						|
                    # This ensures the default is wrapped around _CallableDefault if callable
 | 
						|
                    # else leaves it as-is.
 | 
						|
                    pseudo = pseudo.replace(
 | 
						|
                        default=_CallableDefault(flag.default) if callable(flag.default) else flag.default
 | 
						|
                    )
 | 
						|
 | 
						|
                mapping[name] = pseudo
 | 
						|
 | 
						|
            # Manually call the decorators
 | 
						|
            if descriptions:
 | 
						|
                app_commands.describe(**descriptions)(callback)
 | 
						|
            if renames:
 | 
						|
                app_commands.rename(**renames)(callback)
 | 
						|
 | 
						|
        elif is_converter(converter) or converter in CONVERTER_MAPPING:
 | 
						|
            param = param.replace(annotation=ConverterTransformer(converter, original))
 | 
						|
        elif origin is Union:
 | 
						|
            if len(args) == 2 and args[-1] is _NoneType:
 | 
						|
                # Special case Optional[X] where X is a single type that can optionally be a converter
 | 
						|
                inner = args[0]
 | 
						|
                is_inner_transformer = is_transformer(inner)
 | 
						|
                if (is_converter(inner) or inner in CONVERTER_MAPPING) and not is_inner_transformer:
 | 
						|
                    param = param.replace(annotation=Optional[ConverterTransformer(inner, original)])
 | 
						|
            else:
 | 
						|
                raise
 | 
						|
        elif origin:
 | 
						|
            # Unsupported typing.X annotation e.g. typing.Dict, typing.Tuple, typing.List, etc.
 | 
						|
            raise
 | 
						|
        elif callable(converter) and not inspect.isclass(converter):
 | 
						|
            param_count = required_pos_arguments(converter)
 | 
						|
            if param_count != 1:
 | 
						|
                raise
 | 
						|
            param = param.replace(annotation=CallableTransformer(converter))
 | 
						|
 | 
						|
    return param
 | 
						|
 | 
						|
 | 
						|
def replace_parameters(
 | 
						|
    parameters: Dict[str, Parameter], callback: Callable[..., Any], signature: inspect.Signature
 | 
						|
) -> List[inspect.Parameter]:
 | 
						|
    # Need to convert commands.Parameter back to inspect.Parameter so this will be a bit ugly
 | 
						|
    params = signature.parameters.copy()
 | 
						|
    for name, parameter in parameters.items():
 | 
						|
        converter = parameter.converter
 | 
						|
        # Parameter.converter properly infers from the default and has a str default
 | 
						|
        # This allows the actual signature to inherit this property
 | 
						|
        param = params[name].replace(annotation=converter)
 | 
						|
        param = replace_parameter(param, converter, callback, parameter, params)
 | 
						|
 | 
						|
        if parameter.default is not parameter.empty:
 | 
						|
            default = _CallableDefault(parameter.default) if callable(parameter.default) else parameter.default
 | 
						|
            param = param.replace(default=default)
 | 
						|
 | 
						|
        if isinstance(param.default, Parameter):
 | 
						|
            # If we're here, then it hasn't been handled yet so it should be removed completely
 | 
						|
            param = param.replace(default=parameter.empty)
 | 
						|
 | 
						|
        # Flags are flattened out and thus don't get their parameter in the actual mapping
 | 
						|
        if hasattr(converter, '__commands_is_flag__'):
 | 
						|
            del params[name]
 | 
						|
            continue
 | 
						|
 | 
						|
        params[name] = param
 | 
						|
 | 
						|
    return list(params.values())
 | 
						|
 | 
						|
 | 
						|
class HybridAppCommand(discord.app_commands.Command[CogT, P, T]):
 | 
						|
    __commands_is_hybrid_app_command__: ClassVar[bool] = True
 | 
						|
 | 
						|
    def __init__(
 | 
						|
        self,
 | 
						|
        wrapped: Union[HybridCommand[CogT, ..., T], HybridGroup[CogT, ..., T]],
 | 
						|
        name: Optional[Union[str, app_commands.locale_str]] = None,
 | 
						|
    ) -> None:
 | 
						|
        signature = inspect.signature(wrapped.callback)
 | 
						|
        params = replace_parameters(wrapped.params, wrapped.callback, signature)
 | 
						|
        wrapped.callback.__signature__ = signature.replace(parameters=params)
 | 
						|
        nsfw = getattr(wrapped.callback, '__discord_app_commands_is_nsfw__', False)
 | 
						|
        try:
 | 
						|
            super().__init__(
 | 
						|
                name=name or wrapped._locale_name or wrapped.name,
 | 
						|
                callback=wrapped.callback,  # type: ignore # Signature doesn't match but we're overriding the invoke
 | 
						|
                description=wrapped._locale_description or wrapped.description or wrapped.short_doc or '…',
 | 
						|
                nsfw=nsfw,
 | 
						|
            )
 | 
						|
        finally:
 | 
						|
            del wrapped.callback.__signature__
 | 
						|
 | 
						|
        self.wrapped: Union[HybridCommand[CogT, ..., T], HybridGroup[CogT, ..., T]] = wrapped
 | 
						|
        self.binding: Optional[CogT] = wrapped.cog
 | 
						|
        # This technically means only one flag converter is supported
 | 
						|
        self.flag_converter: Optional[Tuple[str, Type[FlagConverter]]] = getattr(
 | 
						|
            wrapped.callback, '__hybrid_command_flag__', None
 | 
						|
        )
 | 
						|
        self.module = wrapped.module
 | 
						|
 | 
						|
    def _copy_with(self, **kwargs) -> Self:
 | 
						|
        copy: Self = super()._copy_with(**kwargs)  # type: ignore
 | 
						|
        copy.wrapped = self.wrapped
 | 
						|
        copy.flag_converter = self.flag_converter
 | 
						|
        return copy
 | 
						|
 | 
						|
    def copy(self) -> Self:
 | 
						|
        bindings = {
 | 
						|
            self.binding: self.binding,
 | 
						|
        }
 | 
						|
        return self._copy_with(parent=self.parent, binding=self.binding, bindings=bindings)
 | 
						|
 | 
						|
    async def _transform_arguments(
 | 
						|
        self, interaction: discord.Interaction, namespace: app_commands.Namespace
 | 
						|
    ) -> Dict[str, Any]:
 | 
						|
        values = namespace.__dict__
 | 
						|
        transformed_values = {}
 | 
						|
 | 
						|
        for param in self._params.values():
 | 
						|
            try:
 | 
						|
                value = values[param.display_name]
 | 
						|
            except KeyError:
 | 
						|
                if not param.required:
 | 
						|
                    if isinstance(param.default, _CallableDefault):
 | 
						|
                        transformed_values[param.name] = await maybe_coroutine(param.default.func, interaction._baton)
 | 
						|
                    else:
 | 
						|
                        transformed_values[param.name] = param.default
 | 
						|
                else:
 | 
						|
                    raise app_commands.CommandSignatureMismatch(self) from None
 | 
						|
            else:
 | 
						|
                transformed_values[param.name] = await param.transform(interaction, value)
 | 
						|
 | 
						|
        if self.flag_converter is not None:
 | 
						|
            param_name, flag_cls = self.flag_converter
 | 
						|
            flag = flag_cls.__new__(flag_cls)
 | 
						|
            for f in flag_cls.__commands_flags__.values():
 | 
						|
                try:
 | 
						|
                    value = transformed_values.pop(f.attribute)
 | 
						|
                except KeyError:
 | 
						|
                    raise app_commands.CommandSignatureMismatch(self) from None
 | 
						|
                else:
 | 
						|
                    setattr(flag, f.attribute, value)
 | 
						|
 | 
						|
            transformed_values[param_name] = flag
 | 
						|
 | 
						|
        return transformed_values
 | 
						|
 | 
						|
    async def _check_can_run(self, interaction: discord.Interaction) -> bool:
 | 
						|
        # Hybrid checks must run like so:
 | 
						|
        # - Bot global check once
 | 
						|
        # - Bot global check
 | 
						|
        # - Parent interaction check
 | 
						|
        # - Cog/group interaction check
 | 
						|
        # - Cog check
 | 
						|
        # - Local interaction checks
 | 
						|
        # - Local command checks
 | 
						|
 | 
						|
        bot: Bot = interaction.client  # type: ignore
 | 
						|
        ctx: Context[Bot] = interaction._baton
 | 
						|
 | 
						|
        if not await bot.can_run(ctx, call_once=True):
 | 
						|
            return False
 | 
						|
 | 
						|
        if not await bot.can_run(ctx):
 | 
						|
            return False
 | 
						|
 | 
						|
        if self.parent is not None and self.parent is not self.binding:
 | 
						|
            # For commands with a parent which isn't the binding, i.e.
 | 
						|
            # <binding>
 | 
						|
            #     <parent>
 | 
						|
            #         <command>
 | 
						|
            # The parent check needs to be called first
 | 
						|
            if not await maybe_coroutine(self.parent.interaction_check, interaction):
 | 
						|
                return False
 | 
						|
 | 
						|
        if self.binding is not None:
 | 
						|
            try:
 | 
						|
                # Type checker does not like runtime attribute retrieval
 | 
						|
                check: AppCommandCheck = self.binding.interaction_check
 | 
						|
            except AttributeError:
 | 
						|
                pass
 | 
						|
            else:
 | 
						|
                ret = await maybe_coroutine(check, interaction)
 | 
						|
                if not ret:
 | 
						|
                    return False
 | 
						|
 | 
						|
            local_check = Cog._get_overridden_method(self.binding.cog_check)
 | 
						|
            if local_check is not None:
 | 
						|
                ret = await maybe_coroutine(local_check, ctx)
 | 
						|
                if not ret:
 | 
						|
                    return False
 | 
						|
 | 
						|
        if self.checks and not await async_all(f(interaction) for f in self.checks):  # type: ignore
 | 
						|
            return False
 | 
						|
 | 
						|
        if self.wrapped.checks and not await async_all(f(ctx) for f in self.wrapped.checks):  # type: ignore
 | 
						|
            return False
 | 
						|
 | 
						|
        return True
 | 
						|
 | 
						|
    async def _invoke_with_namespace(self, interaction: discord.Interaction, namespace: app_commands.Namespace) -> Any:
 | 
						|
        # Wrap the interaction into a Context
 | 
						|
        bot: Bot = interaction.client  # type: ignore
 | 
						|
 | 
						|
        # Unfortunately, `get_context` has to be called for this to work.
 | 
						|
        # If someone doesn't inherit this to replace it with their custom class
 | 
						|
        # then this doesn't work.
 | 
						|
        interaction._baton = ctx = await bot.get_context(interaction)
 | 
						|
        command = self.wrapped
 | 
						|
        bot.dispatch('command', ctx)
 | 
						|
        value = None
 | 
						|
        callback_completed = False
 | 
						|
        try:
 | 
						|
            await command.prepare(ctx)
 | 
						|
            # This lies and just always passes a Context instead of an Interaction.
 | 
						|
            value = await self._do_call(ctx, ctx.kwargs)  # type: ignore
 | 
						|
            callback_completed = True
 | 
						|
        except app_commands.CommandSignatureMismatch:
 | 
						|
            raise
 | 
						|
        except (app_commands.TransformerError, app_commands.CommandInvokeError) as e:
 | 
						|
            if isinstance(e.__cause__, CommandError):
 | 
						|
                exc = e.__cause__
 | 
						|
            else:
 | 
						|
                exc = HybridCommandError(e)
 | 
						|
                exc.__cause__ = e
 | 
						|
            await command.dispatch_error(ctx, exc.with_traceback(e.__traceback__))
 | 
						|
        except app_commands.AppCommandError as e:
 | 
						|
            exc = HybridCommandError(e)
 | 
						|
            exc.__cause__ = e
 | 
						|
            await command.dispatch_error(ctx, exc.with_traceback(e.__traceback__))
 | 
						|
        except CommandError as e:
 | 
						|
            await command.dispatch_error(ctx, e)
 | 
						|
        finally:
 | 
						|
            if command._max_concurrency is not None:
 | 
						|
                await command._max_concurrency.release(ctx.message)
 | 
						|
 | 
						|
            if callback_completed:
 | 
						|
                await command.call_after_hooks(ctx)
 | 
						|
 | 
						|
        if not ctx.command_failed:
 | 
						|
            bot.dispatch('command_completion', ctx)
 | 
						|
 | 
						|
        interaction.command_failed = ctx.command_failed
 | 
						|
        return value
 | 
						|
 | 
						|
 | 
						|
class HybridCommand(Command[CogT, P, T]):
 | 
						|
    r"""A class that is both an application command and a regular text command.
 | 
						|
 | 
						|
    This has the same parameters and attributes as a regular :class:`~discord.ext.commands.Command`.
 | 
						|
    However, it also doubles as an :class:`application command <discord.app_commands.Command>`. In order
 | 
						|
    for this to work, the callbacks must have the same subset that is supported by application
 | 
						|
    commands.
 | 
						|
 | 
						|
    These are not created manually, instead they are created via the
 | 
						|
    decorator or functional interface.
 | 
						|
 | 
						|
    .. versionadded:: 2.0
 | 
						|
    """
 | 
						|
 | 
						|
    __commands_is_hybrid__: ClassVar[bool] = True
 | 
						|
 | 
						|
    def __init__(
 | 
						|
        self,
 | 
						|
        func: CommandCallback[CogT, Context[Any], P, T],
 | 
						|
        /,
 | 
						|
        *,
 | 
						|
        name: Union[str, app_commands.locale_str] = MISSING,
 | 
						|
        description: Union[str, app_commands.locale_str] = MISSING,
 | 
						|
        **kwargs: Unpack[_HybridCommandKwargs],  # type: ignore # name, description
 | 
						|
    ) -> None:
 | 
						|
        name, name_locale = (name.message, name) if isinstance(name, app_commands.locale_str) else (name, None)
 | 
						|
        if name is not MISSING:
 | 
						|
            kwargs['name'] = name
 | 
						|
        description, description_locale = (
 | 
						|
            (description.message, description) if isinstance(description, app_commands.locale_str) else (description, None)
 | 
						|
        )
 | 
						|
        if description is not MISSING:
 | 
						|
            kwargs['description'] = description
 | 
						|
 | 
						|
        super().__init__(func, **kwargs)
 | 
						|
        self.with_app_command: bool = kwargs.pop('with_app_command', True)
 | 
						|
        self._locale_name: Optional[app_commands.locale_str] = name_locale
 | 
						|
        self._locale_description: Optional[app_commands.locale_str] = description_locale
 | 
						|
 | 
						|
        self.app_command: Optional[HybridAppCommand[CogT, Any, T]] = (
 | 
						|
            HybridAppCommand(self) if self.with_app_command else None
 | 
						|
        )
 | 
						|
 | 
						|
    @property
 | 
						|
    def cog(self) -> CogT:
 | 
						|
        return self._cog
 | 
						|
 | 
						|
    @cog.setter
 | 
						|
    def cog(self, value: CogT) -> None:
 | 
						|
        self._cog = value
 | 
						|
        if self.app_command is not None:
 | 
						|
            self.app_command.binding = value
 | 
						|
 | 
						|
    async def can_run(self, ctx: Context[BotT], /) -> bool:
 | 
						|
        if not self.enabled:
 | 
						|
            raise DisabledCommand(f'{self.name} command is disabled')
 | 
						|
 | 
						|
        if ctx.interaction is not None and self.app_command:
 | 
						|
            return await self.app_command._check_can_run(ctx.interaction)
 | 
						|
        else:
 | 
						|
            return await super().can_run(ctx)
 | 
						|
 | 
						|
    async def _parse_arguments(self, ctx: Context[BotT]) -> None:
 | 
						|
        interaction = ctx.interaction
 | 
						|
        if interaction is None:
 | 
						|
            return await super()._parse_arguments(ctx)
 | 
						|
        elif self.app_command:
 | 
						|
            ctx.kwargs = await self.app_command._transform_arguments(interaction, interaction.namespace)
 | 
						|
 | 
						|
    def _ensure_assignment_on_copy(self, other: Self) -> Self:
 | 
						|
        copy = super()._ensure_assignment_on_copy(other)
 | 
						|
        if self.app_command is None:
 | 
						|
            copy.app_command = None
 | 
						|
        else:
 | 
						|
            copy.app_command = self.app_command.copy()
 | 
						|
            copy.app_command.wrapped = copy
 | 
						|
        return copy
 | 
						|
 | 
						|
    def autocomplete(
 | 
						|
        self, name: str
 | 
						|
    ) -> Callable[[AutocompleteCallback[CogT, ChoiceT]], AutocompleteCallback[CogT, ChoiceT]]:
 | 
						|
        """A decorator that registers a coroutine as an autocomplete prompt for a parameter.
 | 
						|
 | 
						|
        This is the same as :meth:`~discord.app_commands.Command.autocomplete`. It is only
 | 
						|
        applicable for the application command and doesn't do anything if the command is
 | 
						|
        a regular command.
 | 
						|
 | 
						|
        .. note::
 | 
						|
 | 
						|
            Similar to the :meth:`~discord.app_commands.Command.autocomplete` method, this
 | 
						|
            takes :class:`~discord.Interaction` as a parameter rather than a :class:`Context`.
 | 
						|
 | 
						|
        Parameters
 | 
						|
        -----------
 | 
						|
        name: :class:`str`
 | 
						|
            The parameter name to register as autocomplete.
 | 
						|
 | 
						|
        Raises
 | 
						|
        -------
 | 
						|
        TypeError
 | 
						|
            The coroutine passed is not actually a coroutine or
 | 
						|
            the parameter is not found or of an invalid type.
 | 
						|
        """
 | 
						|
        if self.app_command is None:
 | 
						|
            raise TypeError('This command does not have a registered application command')
 | 
						|
 | 
						|
        return self.app_command.autocomplete(name)
 | 
						|
 | 
						|
 | 
						|
class HybridGroup(Group[CogT, P, T]):
 | 
						|
    r"""A class that is both an application command group and a regular text group.
 | 
						|
 | 
						|
    This has the same parameters and attributes as a regular :class:`~discord.ext.commands.Group`.
 | 
						|
    However, it also doubles as an :class:`application command group <discord.app_commands.Group>`.
 | 
						|
    Note that application commands groups cannot have callbacks associated with them, so the callback
 | 
						|
    is only called if it's not invoked as an application command.
 | 
						|
 | 
						|
    Hybrid groups will always have :attr:`Group.invoke_without_command` set to ``True``.
 | 
						|
 | 
						|
    These are not created manually, instead they are created via the
 | 
						|
    decorator or functional interface.
 | 
						|
 | 
						|
    .. versionadded:: 2.0
 | 
						|
 | 
						|
    Attributes
 | 
						|
    -----------
 | 
						|
    fallback: Optional[:class:`str`]
 | 
						|
        The command name to use as a fallback for the application command. Since
 | 
						|
        application command groups cannot be invoked, this creates a subcommand within
 | 
						|
        the group that can be invoked with the given group callback. If ``None``
 | 
						|
        then no fallback command is given. Defaults to ``None``.
 | 
						|
    fallback_locale: Optional[:class:`~discord.app_commands.locale_str`]
 | 
						|
        The fallback command name's locale string, if available.
 | 
						|
    """
 | 
						|
 | 
						|
    __commands_is_hybrid__: ClassVar[bool] = True
 | 
						|
 | 
						|
    def __init__(
 | 
						|
        self,
 | 
						|
        *args: Any,
 | 
						|
        name: Union[str, app_commands.locale_str] = MISSING,
 | 
						|
        description: Union[str, app_commands.locale_str] = MISSING,
 | 
						|
        fallback: Optional[Union[str, app_commands.locale_str]] = None,
 | 
						|
        **attrs: Unpack[_HybridGroupKwargs],  # type: ignore # name, description
 | 
						|
    ) -> None:
 | 
						|
        name, name_locale = (name.message, name) if isinstance(name, app_commands.locale_str) else (name, None)
 | 
						|
        if name is not MISSING:
 | 
						|
            attrs['name'] = name
 | 
						|
        description, description_locale = (
 | 
						|
            (description.message, description) if isinstance(description, app_commands.locale_str) else (description, None)
 | 
						|
        )
 | 
						|
        if description is not MISSING:
 | 
						|
            attrs['description'] = description
 | 
						|
        super().__init__(*args, **attrs)
 | 
						|
        self.invoke_without_command = True
 | 
						|
        self.with_app_command: bool = attrs.pop('with_app_command', True)
 | 
						|
        self._locale_name: Optional[app_commands.locale_str] = name_locale
 | 
						|
        self._locale_description: Optional[app_commands.locale_str] = description_locale
 | 
						|
 | 
						|
        parent = None
 | 
						|
        if self.parent is not None:
 | 
						|
            if isinstance(self.parent, HybridGroup):
 | 
						|
                parent = self.parent.app_command
 | 
						|
            else:
 | 
						|
                raise TypeError(f'HybridGroup parent must be HybridGroup not {self.parent.__class__}')
 | 
						|
 | 
						|
        # I would love for this to be Optional[app_commands.Group]
 | 
						|
        # However, Python does not have conditional typing so it's very hard to
 | 
						|
        # make this type depend on the with_app_command bool without a lot of needless repetition
 | 
						|
        self.app_command: app_commands.Group = MISSING
 | 
						|
 | 
						|
        fallback, fallback_locale = (
 | 
						|
            (fallback.message, fallback) if isinstance(fallback, app_commands.locale_str) else (fallback, None)
 | 
						|
        )
 | 
						|
        self.fallback: Optional[str] = fallback
 | 
						|
        self.fallback_locale: Optional[app_commands.locale_str] = fallback_locale
 | 
						|
 | 
						|
        if self.with_app_command:
 | 
						|
            guild_ids = attrs.pop('guild_ids', None)
 | 
						|
            if guild_ids is None:
 | 
						|
                guild_ids = getattr(self.callback, '__discord_app_commands_default_guilds__', None)
 | 
						|
            guild_only = getattr(self.callback, '__discord_app_commands_guild_only__', False)
 | 
						|
            default_permissions = getattr(self.callback, '__discord_app_commands_default_permissions__', None)
 | 
						|
            nsfw = getattr(self.callback, '__discord_app_commands_is_nsfw__', False)
 | 
						|
            contexts = getattr(self.callback, '__discord_app_commands_contexts__', MISSING)
 | 
						|
            installs = getattr(self.callback, '__discord_app_commands_installation_types__', MISSING)
 | 
						|
            self.app_command = app_commands.Group(
 | 
						|
                name=self._locale_name or self.name,
 | 
						|
                description=self._locale_description or self.description or self.short_doc or '…',
 | 
						|
                guild_ids=guild_ids,
 | 
						|
                guild_only=guild_only,
 | 
						|
                default_permissions=default_permissions,
 | 
						|
                nsfw=nsfw,
 | 
						|
                allowed_installs=installs,
 | 
						|
                allowed_contexts=contexts,
 | 
						|
            )
 | 
						|
 | 
						|
            # This prevents the group from re-adding the command at __init__
 | 
						|
            self.app_command.parent = parent
 | 
						|
            self.app_command.module = self.module
 | 
						|
 | 
						|
            if fallback is not None:
 | 
						|
                command = HybridAppCommand(self, name=fallback_locale or fallback)
 | 
						|
                self.app_command.add_command(command)
 | 
						|
 | 
						|
    @property
 | 
						|
    def _fallback_command(self) -> Optional[HybridAppCommand[CogT, ..., T]]:
 | 
						|
        if self.app_command is MISSING:
 | 
						|
            return None
 | 
						|
        return self.app_command.get_command(self.fallback)  # type: ignore
 | 
						|
 | 
						|
    @property
 | 
						|
    def cog(self) -> CogT:
 | 
						|
        return self._cog
 | 
						|
 | 
						|
    @cog.setter
 | 
						|
    def cog(self, value: CogT) -> None:
 | 
						|
        self._cog = value
 | 
						|
        fallback = self._fallback_command
 | 
						|
        if fallback:
 | 
						|
            fallback.binding = value
 | 
						|
 | 
						|
    async def can_run(self, ctx: Context[BotT], /) -> bool:
 | 
						|
        fallback = self._fallback_command
 | 
						|
        if ctx.interaction is not None and fallback:
 | 
						|
            return await fallback._check_can_run(ctx.interaction)
 | 
						|
        else:
 | 
						|
            return await super().can_run(ctx)
 | 
						|
 | 
						|
    async def _parse_arguments(self, ctx: Context[BotT]) -> None:
 | 
						|
        interaction = ctx.interaction
 | 
						|
        fallback = self._fallback_command
 | 
						|
        if interaction is not None and fallback:
 | 
						|
            ctx.kwargs = await fallback._transform_arguments(interaction, interaction.namespace)
 | 
						|
        else:
 | 
						|
            return await super()._parse_arguments(ctx)
 | 
						|
 | 
						|
    def _ensure_assignment_on_copy(self, other: Self) -> Self:
 | 
						|
        copy = super()._ensure_assignment_on_copy(other)
 | 
						|
        copy.fallback = self.fallback
 | 
						|
        return copy
 | 
						|
 | 
						|
    def _update_copy(self, kwargs: Dict[str, Any]) -> Self:
 | 
						|
        copy = super()._update_copy(kwargs)
 | 
						|
        # This is only really called inside CogMeta
 | 
						|
        # I want to ensure that the children of the app command are copied
 | 
						|
        # For example, if someone defines an app command using the app_command property
 | 
						|
        # while inside the cog. This copy is not propagated in normal means.
 | 
						|
        # For some reason doing this copy above will lead to duplicates.
 | 
						|
        if copy.app_command and self.app_command:
 | 
						|
            # This is a very lazy copy because the CogMeta will properly copy it
 | 
						|
            # with bindings and all later
 | 
						|
            copy.app_command._children = self.app_command._children.copy()
 | 
						|
 | 
						|
        # Ensure the copy's fallback wraps the copy
 | 
						|
        if copy._fallback_command and self._fallback_command:
 | 
						|
            copy._fallback_command.wrapped = copy
 | 
						|
 | 
						|
        return copy
 | 
						|
 | 
						|
    def autocomplete(
 | 
						|
        self, name: str
 | 
						|
    ) -> Callable[[AutocompleteCallback[CogT, ChoiceT]], AutocompleteCallback[CogT, ChoiceT]]:
 | 
						|
        """A decorator that registers a coroutine as an autocomplete prompt for a parameter.
 | 
						|
 | 
						|
        This is the same as :meth:`~discord.app_commands.Command.autocomplete`. It is only
 | 
						|
        applicable for the application command and doesn't do anything if the command is
 | 
						|
        a regular command.
 | 
						|
 | 
						|
        This is only available if the group has a fallback application command registered.
 | 
						|
 | 
						|
        .. note::
 | 
						|
 | 
						|
            Similar to the :meth:`~discord.app_commands.Command.autocomplete` method, this
 | 
						|
            takes :class:`~discord.Interaction` as a parameter rather than a :class:`Context`.
 | 
						|
 | 
						|
        Parameters
 | 
						|
        -----------
 | 
						|
        name: :class:`str`
 | 
						|
            The parameter name to register as autocomplete.
 | 
						|
 | 
						|
        Raises
 | 
						|
        -------
 | 
						|
        TypeError
 | 
						|
            The coroutine passed is not actually a coroutine or
 | 
						|
            the parameter is not found or of an invalid type.
 | 
						|
        """
 | 
						|
        if self._fallback_command:
 | 
						|
            return self._fallback_command.autocomplete(name)
 | 
						|
        else:
 | 
						|
 | 
						|
            def decorator(func: AutocompleteCallback[CogT, ChoiceT]) -> AutocompleteCallback[CogT, ChoiceT]:
 | 
						|
                return func
 | 
						|
 | 
						|
            return decorator
 | 
						|
 | 
						|
    def add_command(self, command: Union[HybridGroup[CogT, ..., Any], HybridCommand[CogT, ..., Any]], /) -> None:
 | 
						|
        """Adds a :class:`.HybridCommand` into the internal list of commands.
 | 
						|
 | 
						|
        This is usually not called, instead the :meth:`~.GroupMixin.command` or
 | 
						|
        :meth:`~.GroupMixin.group` shortcut decorators are used instead.
 | 
						|
 | 
						|
        Parameters
 | 
						|
        -----------
 | 
						|
        command: :class:`HybridCommand`
 | 
						|
            The command to add.
 | 
						|
 | 
						|
        Raises
 | 
						|
        -------
 | 
						|
        CommandRegistrationError
 | 
						|
            If the command or its alias is already registered by different command.
 | 
						|
        TypeError
 | 
						|
            If the command passed is not a subclass of :class:`.HybridCommand`.
 | 
						|
        """
 | 
						|
 | 
						|
        if not isinstance(command, (HybridCommand, HybridGroup)):
 | 
						|
            raise TypeError('The command passed must be a subclass of HybridCommand or HybridGroup')
 | 
						|
 | 
						|
        if isinstance(command, HybridGroup) and self.parent is not None:
 | 
						|
            raise ValueError(f'{command.qualified_name!r} is too nested, groups can only be nested at most one level')
 | 
						|
 | 
						|
        if command.app_command and self.app_command:
 | 
						|
            self.app_command.add_command(command.app_command)
 | 
						|
 | 
						|
        command.parent = self
 | 
						|
 | 
						|
        if command.name in self.all_commands:
 | 
						|
            raise CommandRegistrationError(command.name)
 | 
						|
 | 
						|
        self.all_commands[command.name] = command
 | 
						|
        for alias in command.aliases:
 | 
						|
            if alias in self.all_commands:
 | 
						|
                self.remove_command(command.name)
 | 
						|
                raise CommandRegistrationError(alias, alias_conflict=True)
 | 
						|
            self.all_commands[alias] = command
 | 
						|
 | 
						|
    def remove_command(self, name: str, /) -> Optional[Command[CogT, ..., Any]]:
 | 
						|
        cmd = super().remove_command(name)
 | 
						|
        if self.app_command:
 | 
						|
            self.app_command.remove_command(name)
 | 
						|
        return cmd
 | 
						|
 | 
						|
    def command(
 | 
						|
        self,
 | 
						|
        name: Union[str, app_commands.locale_str] = MISSING,
 | 
						|
        *args: Any,
 | 
						|
        with_app_command: bool = True,
 | 
						|
        **kwargs: Unpack[_HybridCommandDecoratorKwargs],  # type: ignore # name, with_app_command
 | 
						|
    ) -> Callable[[CommandCallback[CogT, ContextT, P2, U]], HybridCommand[CogT, P2, U]]:
 | 
						|
        """A shortcut decorator that invokes :func:`~discord.ext.commands.hybrid_command` and adds it to
 | 
						|
        the internal command list via :meth:`add_command`.
 | 
						|
 | 
						|
        Returns
 | 
						|
        --------
 | 
						|
        Callable[..., :class:`HybridCommand`]
 | 
						|
            A decorator that converts the provided method into a Command, adds it to the bot, then returns it.
 | 
						|
        """
 | 
						|
 | 
						|
        def decorator(func: CommandCallback[CogT, ContextT, P2, U]):
 | 
						|
            kwargs.setdefault('parent', self)  # type: ignore # parent is not for users to set
 | 
						|
            result = hybrid_command(name=name, *args, with_app_command=with_app_command, **kwargs)(func)  # type: ignore # name, with_app_command
 | 
						|
            self.add_command(result)
 | 
						|
            return result
 | 
						|
 | 
						|
        return decorator
 | 
						|
 | 
						|
    def group(
 | 
						|
        self,
 | 
						|
        name: Union[str, app_commands.locale_str] = MISSING,
 | 
						|
        *args: Any,
 | 
						|
        with_app_command: bool = True,
 | 
						|
        **kwargs: Unpack[_HybridGroupDecoratorKwargs],  # type: ignore # name, with_app_command
 | 
						|
    ) -> Callable[[CommandCallback[CogT, ContextT, P2, U]], HybridGroup[CogT, P2, U]]:
 | 
						|
        """A shortcut decorator that invokes :func:`~discord.ext.commands.hybrid_group` and adds it to
 | 
						|
        the internal command list via :meth:`~.GroupMixin.add_command`.
 | 
						|
 | 
						|
        Returns
 | 
						|
        --------
 | 
						|
        Callable[..., :class:`HybridGroup`]
 | 
						|
            A decorator that converts the provided method into a Group, adds it to the bot, then returns it.
 | 
						|
        """
 | 
						|
 | 
						|
        def decorator(func: CommandCallback[CogT, ContextT, P2, U]):
 | 
						|
            kwargs.setdefault('parent', self)  # type: ignore # parent is not for users to set
 | 
						|
            result = hybrid_group(name=name, *args, with_app_command=with_app_command, **kwargs)(func)  # type: ignore # name, with_app_command
 | 
						|
            self.add_command(result)
 | 
						|
            return result
 | 
						|
 | 
						|
        return decorator
 | 
						|
 | 
						|
 | 
						|
def hybrid_command(
 | 
						|
    name: Union[str, app_commands.locale_str] = MISSING,
 | 
						|
    *,
 | 
						|
    with_app_command: bool = True,
 | 
						|
    **attrs: Unpack[_HybridCommandDecoratorKwargs],  # type: ignore # name, with_app_command
 | 
						|
) -> Callable[[CommandCallback[CogT, ContextT, P, T]], HybridCommand[CogT, P, T]]:
 | 
						|
    r"""A decorator that transforms a function into a :class:`.HybridCommand`.
 | 
						|
 | 
						|
    A hybrid command is one that functions both as a regular :class:`.Command`
 | 
						|
    and one that is also a :class:`app_commands.Command <discord.app_commands.Command>`.
 | 
						|
 | 
						|
    The callback being attached to the command must be representable as an
 | 
						|
    application command callback. Converters are silently converted into a
 | 
						|
    :class:`~discord.app_commands.Transformer` with a
 | 
						|
    :attr:`discord.AppCommandOptionType.string` type.
 | 
						|
 | 
						|
    Checks and error handlers are dispatched and called as-if they were commands
 | 
						|
    similar to :class:`.Command`. This means that they take :class:`Context` as
 | 
						|
    a parameter rather than :class:`discord.Interaction`.
 | 
						|
 | 
						|
    All checks added using the :func:`.check` & co. decorators are added into
 | 
						|
    the function. There is no way to supply your own checks through this
 | 
						|
    decorator.
 | 
						|
 | 
						|
    .. versionadded:: 2.0
 | 
						|
 | 
						|
    Parameters
 | 
						|
    -----------
 | 
						|
    name: Union[:class:`str`, :class:`~discord.app_commands.locale_str`]
 | 
						|
        The name to create the command with. By default this uses the
 | 
						|
        function name unchanged.
 | 
						|
    with_app_command: :class:`bool`
 | 
						|
        Whether to register the command also as an application command.
 | 
						|
    \*\*attrs
 | 
						|
        Keyword arguments to pass into the construction of the
 | 
						|
        hybrid command.
 | 
						|
 | 
						|
    Raises
 | 
						|
    -------
 | 
						|
    TypeError
 | 
						|
        If the function is not a coroutine or is already a command.
 | 
						|
    """
 | 
						|
 | 
						|
    def decorator(func: CommandCallback[CogT, ContextT, P, T]) -> HybridCommand[CogT, P, T]:
 | 
						|
        if isinstance(func, Command):
 | 
						|
            raise TypeError('Callback is already a command.')
 | 
						|
        # Pyright does not allow Command[Any] to be assigned to Command[CogT] despite it being okay here
 | 
						|
        return HybridCommand(func, name=name, with_app_command=with_app_command, **attrs)  # type: ignore # name, with_app_command
 | 
						|
 | 
						|
    return decorator
 | 
						|
 | 
						|
 | 
						|
def hybrid_group(
 | 
						|
    name: Union[str, app_commands.locale_str] = MISSING,
 | 
						|
    *,
 | 
						|
    with_app_command: bool = True,
 | 
						|
    **attrs: Unpack[_HybridGroupDecoratorKwargs],  # type: ignore # name, with_app_command
 | 
						|
) -> Callable[[CommandCallback[CogT, ContextT, P, T]], HybridGroup[CogT, P, T]]:
 | 
						|
    """A decorator that transforms a function into a :class:`.HybridGroup`.
 | 
						|
 | 
						|
    This is similar to the :func:`~discord.ext.commands.group` decorator except it creates
 | 
						|
    a hybrid group instead.
 | 
						|
 | 
						|
    Parameters
 | 
						|
    -----------
 | 
						|
    name: Union[:class:`str`, :class:`~discord.app_commands.locale_str`]
 | 
						|
        The name to create the group with. By default this uses the
 | 
						|
        function name unchanged.
 | 
						|
    with_app_command: :class:`bool`
 | 
						|
        Whether to register the command also as an application command.
 | 
						|
 | 
						|
    Raises
 | 
						|
    -------
 | 
						|
    TypeError
 | 
						|
        If the function is not a coroutine or is already a command.
 | 
						|
    """
 | 
						|
 | 
						|
    def decorator(func: CommandCallback[CogT, ContextT, P, T]) -> HybridGroup[CogT, P, T]:
 | 
						|
        if isinstance(func, Command):
 | 
						|
            raise TypeError('Callback is already a command.')
 | 
						|
        return HybridGroup(func, name=name, with_app_command=with_app_command, **attrs)  # type: ignore # name, with_app_command
 | 
						|
 | 
						|
    return decorator
 |