diff --git a/discord/bot.py b/discord/bot.py index 5ba5f9b5df..27e17647ee 100644 --- a/discord/bot.py +++ b/discord/bot.py @@ -66,7 +66,13 @@ from .utils import MISSING, async_all, find, get if TYPE_CHECKING: + from typing_extensions import Never + + from .cog import Cog + from .commands import Option + from .ext.commands import Cooldown from .member import Member + from .permissions import Permissions C = TypeVar("C", bound=MessageCommand | SlashCommand | UserCommand) CoroFunc = Callable[..., Coroutine[Any, Any, Any]] @@ -909,7 +915,26 @@ async def callback() -> None: if not autocomplete_task.done(): autocomplete_task.cancel() - def slash_command(self, **kwargs): + def slash_command( + self, + *, + checks: list[Callable[[ApplicationContext], bool]] | None = MISSING, + cog: Cog | None = MISSING, + contexts: set[InteractionContextType] | None = MISSING, + cooldown: Cooldown | None = MISSING, + default_member_permissions: Permissions | None = MISSING, + description: str | None = MISSING, + description_localizations: dict[str, str] | None = MISSING, + guild_ids: list[int] | None = MISSING, + guild_only: bool | None = MISSING, + integration_types: set[IntegrationType] | None = MISSING, + name: str | None = MISSING, + name_localizations: dict[str, str] | None = MISSING, + nsfw: bool = False, + options: list[Option] | None = MISSING, + parent: SlashCommandGroup | None = MISSING, + **kwargs: Never, + ) -> Callable[[...], SlashCommand]: """A shortcut decorator for adding a slash command to the bot. This is equivalent to using :meth:`application_command`, providing the :class:`SlashCommand` class. @@ -922,9 +947,42 @@ def slash_command(self, **kwargs): A decorator that converts the provided function into a :class:`.SlashCommand`, adds it to the bot, and returns it. """ - return self.application_command(cls=SlashCommand, **kwargs) + return self.application_command( + cls=SlashCommand, + checks=checks, + cog=cog, + contexts=contexts, + cooldown=cooldown, + default_member_permissions=default_member_permissions, + description=description, + description_localizations=description_localizations, + guild_ids=guild_ids, + guild_only=guild_only, + integration_types=integration_types, + name=name, + name_localizations=name_localizations, + nsfw=nsfw, + options=options, + parent=parent, + **kwargs, + ) - def user_command(self, **kwargs): + def user_command( + self, + *, + checks: list[Callable[[ApplicationContext], bool]] | None = MISSING, + cog: Cog | None = MISSING, + contexts: set[InteractionContextType] | None = MISSING, + cooldown: Cooldown | None = MISSING, + default_member_permissions: Permissions | None = MISSING, + guild_ids: list[int] | None = MISSING, + guild_only: bool | None = MISSING, + integration_types: set[IntegrationType] | None = MISSING, + name: str | None = MISSING, + name_localizations: dict[str, str] | None = MISSING, + nsfw: bool = False, + **kwargs: Never, + ) -> Callable[..., UserCommand]: """A shortcut decorator for adding a user command to the bot. This is equivalent to using :meth:`application_command`, providing the :class:`UserCommand` class. @@ -937,9 +995,38 @@ def user_command(self, **kwargs): A decorator that converts the provided function into a :class:`.UserCommand`, adds it to the bot, and returns it. """ - return self.application_command(cls=UserCommand, **kwargs) + return self.application_command( + cls=UserCommand, + checks=checks, + cog=cog, + contexts=contexts, + cooldown=cooldown, + default_member_permissions=default_member_permissions, + guild_ids=guild_ids, + guild_only=guild_only, + integration_types=integration_types, + name=name, + name_localizations=name_localizations, + nsfw=nsfw, + **kwargs, + ) - def message_command(self, **kwargs): + def message_command( + self, + *, + checks: list[Callable[[ApplicationContext], bool]] | None = MISSING, + cog: Cog | None = MISSING, + contexts: set[InteractionContextType] | None = MISSING, + cooldown: Cooldown | None = MISSING, + default_member_permissions: Permissions | None = MISSING, + guild_ids: list[int] | None = MISSING, + guild_only: bool | None = MISSING, + integration_types: set[IntegrationType] | None = MISSING, + name: str | None = MISSING, + name_localizations: dict[str, str] | None = MISSING, + nsfw: bool = False, + **kwargs: Never, + ) -> Callable[..., MessageCommand]: """A shortcut decorator for adding a message command to the bot. This is equivalent to using :meth:`application_command`, providing the :class:`MessageCommand` class. @@ -952,9 +1039,43 @@ def message_command(self, **kwargs): A decorator that converts the provided function into a :class:`.MessageCommand`, adds it to the bot, and returns it. """ - return self.application_command(cls=MessageCommand, **kwargs) + return self.application_command( + cls=MessageCommand, + checks=checks, + cog=cog, + contexts=contexts, + cooldown=cooldown, + default_member_permissions=default_member_permissions, + guild_ids=guild_ids, + guild_only=guild_only, + integration_types=integration_types, + name=name, + name_localizations=name_localizations, + nsfw=nsfw, + **kwargs, + ) - def application_command(self, cls: type[C] = SlashCommand, **kwargs): + def application_command( + self, + *, + cls: type[C] = SlashCommand, + checks: list[Callable[[ApplicationContext], bool]] | None = MISSING, + cog: Cog | None = MISSING, + contexts: set[InteractionContextType] | None = MISSING, + cooldown: Cooldown | None = MISSING, + default_member_permissions: Permissions | None = MISSING, + description: str | None = MISSING, + description_localizations: dict[str, str] | None = MISSING, + guild_ids: list[int] | None = MISSING, + guild_only: bool | None = MISSING, + integration_types: set[IntegrationType] | None = MISSING, + name: str | None = MISSING, + name_localizations: dict[str, str] | None = MISSING, + nsfw: bool = False, + options: list[Option] | None = MISSING, + parent: SlashCommandGroup | None = MISSING, + **kwargs: Never, + ) -> Callable[..., C]: """A shortcut decorator that converts the provided function into an application command via :func:`command` and adds it to the internal command list via :meth:`~.Bot.add_application_command`. @@ -976,6 +1097,26 @@ class be provided, it must be a subclass of either adds it to the bot, and returns it. """ + params = { + "checks": checks, + "cog": cog, + "contexts": contexts, + "cooldown": cooldown, + "default_member_permissions": default_member_permissions, + "description": description, + "description_localizations": description_localizations, + "guild_ids": guild_ids, + "guild_only": guild_only, + "integration_types": integration_types, + "name": name, + "name_localizations": name_localizations, + "nsfw": nsfw, + "options": options, + "parent": parent, + **kwargs, + } + kwargs = {k: v for k, v in params.items() if v is not MISSING} + def decorator(func) -> C: result = command(cls=cls, **kwargs)(func) self.add_application_command(result) diff --git a/discord/commands/core.py b/discord/commands/core.py index 0f721f15a0..0ae6e2b7ff 100644 --- a/discord/commands/core.py +++ b/discord/commands/core.py @@ -90,11 +90,12 @@ ) if TYPE_CHECKING: - from typing_extensions import Concatenate, ParamSpec + from typing_extensions import Concatenate, Never, ParamSpec from .. import Permissions + from ..bot import C from ..cog import Cog - from ..ext.commands.cooldowns import CooldownMapping, MaxConcurrency + from ..ext.commands.cooldowns import Cooldown, CooldownMapping, MaxConcurrency T = TypeVar("T") CogT = TypeVar("CogT", bound="Cog") @@ -1978,7 +1979,25 @@ def _update_copy(self, kwargs: dict[str, Any]): return self.copy() -def slash_command(**kwargs): +def slash_command( + *, + checks: list[Callable[[ApplicationContext], bool]] | None = MISSING, + cog: Cog | None = MISSING, + contexts: set[InteractionContextType] | None = MISSING, + cooldown: Cooldown | None = MISSING, + default_member_permissions: Permissions | None = MISSING, + description: str | None = MISSING, + description_localizations: dict[str, str] | None = MISSING, + guild_ids: list[int] | None = MISSING, + guild_only: bool | None = MISSING, + integration_types: set[IntegrationType] | None = MISSING, + name: str | None = MISSING, + name_localizations: dict[str, str] | None = MISSING, + nsfw: bool = False, + options: list[Option] | None = MISSING, + parent: SlashCommandGroup | None = MISSING, + **kwargs: Never, +) -> Callable[[...], SlashCommand]: """Decorator for slash commands that invokes :func:`application_command`. .. versionadded:: 2.0 @@ -1988,10 +2007,42 @@ def slash_command(**kwargs): Callable[..., :class:`.SlashCommand`] A decorator that converts the provided method into a :class:`.SlashCommand`. """ - return application_command(cls=SlashCommand, **kwargs) - - -def user_command(**kwargs): + return application_command( + cls=SlashCommand, + checks=checks, + cog=cog, + contexts=contexts, + cooldown=cooldown, + default_member_permissions=default_member_permissions, + description=description, + description_localizations=description_localizations, + guild_ids=guild_ids, + guild_only=guild_only, + integration_types=integration_types, + name=name, + name_localizations=name_localizations, + nsfw=nsfw, + options=options, + parent=parent, + **kwargs, + ) + + +def user_command( + *, + checks: list[Callable[[ApplicationContext], bool]] | None = MISSING, + cog: Cog | None = MISSING, + contexts: set[InteractionContextType] | None = MISSING, + cooldown: Cooldown | None = MISSING, + default_member_permissions: Permissions | None = MISSING, + guild_ids: list[int] | None = MISSING, + guild_only: bool | None = MISSING, + integration_types: set[IntegrationType] | None = MISSING, + name: str | None = MISSING, + name_localizations: dict[str, str] | None = MISSING, + nsfw: bool = False, + **kwargs: Never, +) -> Callable[..., UserCommand]: """Decorator for user commands that invokes :func:`application_command`. .. versionadded:: 2.0 @@ -2001,10 +2052,38 @@ def user_command(**kwargs): Callable[..., :class:`.UserCommand`] A decorator that converts the provided method into a :class:`.UserCommand`. """ - return application_command(cls=UserCommand, **kwargs) - - -def message_command(**kwargs): + return application_command( + cls=UserCommand, + checks=checks, + cog=cog, + contexts=contexts, + cooldown=cooldown, + default_member_permissions=default_member_permissions, + guild_ids=guild_ids, + guild_only=guild_only, + integration_types=integration_types, + name=name, + name_localizations=name_localizations, + nsfw=nsfw, + **kwargs, + ) + + +def message_command( + *, + checks: list[Callable[[ApplicationContext], bool]] | None = MISSING, + cog: Cog | None = MISSING, + contexts: set[InteractionContextType] | None = MISSING, + cooldown: Cooldown | None = MISSING, + default_member_permissions: Permissions | None = MISSING, + guild_ids: list[int] | None = MISSING, + guild_only: bool | None = MISSING, + integration_types: set[IntegrationType] | None = MISSING, + name: str | None = MISSING, + name_localizations: dict[str, str] | None = MISSING, + nsfw: bool = False, + **kwargs: Never, +) -> Callable[..., MessageCommand]: """Decorator for message commands that invokes :func:`application_command`. .. versionadded:: 2.0 @@ -2014,10 +2093,43 @@ def message_command(**kwargs): Callable[..., :class:`.MessageCommand`] A decorator that converts the provided method into a :class:`.MessageCommand`. """ - return application_command(cls=MessageCommand, **kwargs) - - -def application_command(cls=SlashCommand, **attrs): + return application_command( + cls=MessageCommand, + checks=checks, + cog=cog, + contexts=contexts, + cooldown=cooldown, + default_member_permissions=default_member_permissions, + guild_ids=guild_ids, + guild_only=guild_only, + integration_types=integration_types, + name=name, + name_localizations=name_localizations, + nsfw=nsfw, + **kwargs, + ) + + +def application_command( + *, + cls=SlashCommand, + checks: list[Callable[[ApplicationContext], bool]] | None = MISSING, + cog: Cog | None = MISSING, + contexts: set[InteractionContextType] | None = MISSING, + cooldown: Cooldown | None = MISSING, + default_member_permissions: Permissions | None = MISSING, + description: str | None = MISSING, + description_localizations: dict[str, str] | None = MISSING, + guild_ids: list[int] | None = MISSING, + guild_only: bool | None = MISSING, + integration_types: set[IntegrationType] | None = MISSING, + name: str | None = MISSING, + name_localizations: dict[str, str] | None = MISSING, + nsfw: bool = False, + options: list[Option] | None = MISSING, + parent: SlashCommandGroup | None = MISSING, + **kwargs: Never, +) -> Callable[..., C]: """A decorator that transforms a function into an :class:`.ApplicationCommand`. More specifically, usually one of :class:`.SlashCommand`, :class:`.UserCommand`, or :class:`.MessageCommand`. The exact class depends on the ``cls`` parameter. @@ -2048,6 +2160,25 @@ def application_command(cls=SlashCommand, **attrs): TypeError If the function is not a coroutine or is already a command. """ + params = { + "checks": checks, + "cog": cog, + "contexts": contexts, + "cooldown": cooldown, + "default_member_permissions": default_member_permissions, + "description": description, + "description_localizations": description_localizations, + "guild_ids": guild_ids, + "guild_only": guild_only, + "integration_types": integration_types, + "name": name, + "name_localizations": name_localizations, + "nsfw": nsfw, + "options": options, + "parent": parent, + **kwargs, + } + kwargs = {k: v for k, v in params.items() if v is not MISSING} def decorator(func: Callable) -> cls: if isinstance(func, ApplicationCommand): @@ -2056,7 +2187,7 @@ def decorator(func: Callable) -> cls: raise TypeError( "func needs to be a callable or a subclass of ApplicationCommand." ) - return cls(func, **attrs) + return cls(func, **kwargs) return decorator