diff --git a/discord/app_commands/models.py b/discord/app_commands/models.py index e8a96784b..fd92a1a5d 100644 --- a/discord/app_commands/models.py +++ b/discord/app_commands/models.py @@ -35,6 +35,7 @@ from ..enums import ( AppCommandPermissionType, ChannelType, Locale, + EntryPointCommandHandlerType, try_enum, ) from ..mixins import Hashable @@ -181,6 +182,10 @@ class AppCommand(Hashable): denotes that it is a global command. nsfw: :class:`bool` Whether the command is NSFW and should only work in NSFW channels. + handler: Optional[:class:`~discord.EntryPointCommandHandlerType`] + Determines whether the interaction is handled by the app's interactions handler or by Discord. + + This is only available for commands with type :attr:`~discord.AppCommandType.primary_entry_point`. """ __slots__ = ( @@ -198,6 +203,7 @@ class AppCommand(Hashable): 'allowed_contexts', 'allowed_installs', 'nsfw', + 'handler', '_state', ) @@ -245,6 +251,12 @@ class AppCommand(Hashable): self.name_localizations: Dict[Locale, str] = _to_locale_dict(data.get('name_localizations') or {}) self.description_localizations: Dict[Locale, str] = _to_locale_dict(data.get('description_localizations') or {}) + handler = data.get('handler') + if handler is None: + self.handler = None + else: + self.handler = try_enum(EntryPointCommandHandlerType, handler) + def to_dict(self) -> ApplicationCommandPayload: return { 'id': self.id, @@ -257,6 +269,7 @@ class AppCommand(Hashable): 'contexts': self.allowed_contexts.to_array() if self.allowed_contexts is not None else None, 'integration_types': self.allowed_installs.to_array() if self.allowed_installs is not None else None, 'options': [opt.to_dict() for opt in self.options], + 'handler': self.handler.value if self.handler is not None else None, } # type: ignore # Type checker does not understand this literal. def __str__(self) -> str: @@ -317,6 +330,7 @@ class AppCommand(Hashable): default_member_permissions: Optional[Permissions] = MISSING, dm_permission: bool = MISSING, options: List[Union[Argument, AppCommandGroup]] = MISSING, + handler: Optional[EntryPointCommandHandlerType] = MISSING, ) -> AppCommand: """|coro| @@ -335,6 +349,10 @@ class AppCommand(Hashable): Indicates if the application command can be used in DMs. options: List[Union[:class:`Argument`, :class:`AppCommandGroup`]] List of new options for this application command. + handler: Optional[:class:`~discord.EntryPointCommandHandlerType`] + Determines whether the interaction is handled by the app's interactions handler or by Discord. + + Only available for commands with type :attr:`~discord.AppCommandType.primary_entry_point`. Raises ------- @@ -376,6 +394,9 @@ class AppCommand(Hashable): if options is not MISSING: payload['options'] = [option.to_dict() for option in options] + if handler is not MISSING: + payload['handler'] = handler + if not payload: return self @@ -433,6 +454,19 @@ class AppCommand(Hashable): ) return GuildAppCommandPermissions(data=data, state=state, command=self) + def is_default_entry_point_command(self) -> bool: + """:class:`bool`: Returns ``True`` if this is the default entry point command. + + This is determined by the command's name and handler type. + More information can be found in the :ddocs:`official Discord + documentation `. + """ + return ( + self.type is AppCommandType.primary_entry_point + and self.name.casefold() == 'launch' + and self.handler is EntryPointCommandHandlerType.discord_launch_activity + ) + class Choice(Generic[ChoiceT]): """Represents an application command argument choice. diff --git a/discord/enums.py b/discord/enums.py index 7915bcb4b..746ed29ea 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -768,6 +768,7 @@ class AppCommandType(Enum): chat_input = 1 user = 2 message = 3 + primary_entry_point = 4 class AppCommandPermissionType(Enum): @@ -863,6 +864,11 @@ class SubscriptionStatus(Enum): inactive = 2 +class EntryPointCommandHandlerType(Enum): + app_handler = 1 + discord_launch_activity = 2 + + def create_unknown_value(cls: Type[E], val: Any) -> E: value_cls = cls._enum_value_cls_ # type: ignore # This is narrowed below name = f'unknown_{val}' diff --git a/discord/types/command.py b/discord/types/command.py index 7876ee6dd..618fd1a93 100644 --- a/discord/types/command.py +++ b/discord/types/command.py @@ -31,7 +31,7 @@ from .channel import ChannelType from .snowflake import Snowflake from .interactions import InteractionContextType -ApplicationCommandType = Literal[1, 2, 3] +ApplicationCommandType = Literal[1, 2, 3, 4] ApplicationCommandOptionType = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] ApplicationIntegrationType = Literal[0, 1] @@ -162,6 +162,15 @@ class _ChatInputApplicationCommand(_BaseApplicationCommand, total=False): ] +EntryPointCommandHandlerType = Literal[1, 2] + + +class _PrimaryEntryPointApplicationCommand(_BaseApplicationCommand): + description: Required[str] + type: Literal[4] + handler: EntryPointCommandHandlerType + + class _BaseContextMenuApplicationCommand(_BaseApplicationCommand): description: Literal[""] @@ -178,6 +187,7 @@ GlobalApplicationCommand = Union[ _ChatInputApplicationCommand, _UserApplicationCommand, _MessageApplicationCommand, + _PrimaryEntryPointApplicationCommand, ] diff --git a/docs/interactions/api.rst b/docs/interactions/api.rst index feab66907..17503188b 100644 --- a/docs/interactions/api.rst +++ b/docs/interactions/api.rst @@ -446,6 +446,11 @@ Enumerations .. attribute:: message A message context menu command. + .. attribute:: primary_entry_point + + .. versionadded:: 2.5 + + A command that represents the primary way to invoke an app's Activity .. class:: AppCommandPermissionType @@ -463,6 +468,21 @@ Enumerations The permission is for a user. +.. class:: EntryPointCommandHandlerType + + Represents the type of an entry point command handler. + + .. versionadded:: 2.5 + + .. attribute:: app_handler + + The app handles the interaction using an interaction token. + + .. attribute:: discord_launch_activity + + Discord handles the interaction by launching an Activity and + sending a follow-up message without coordinating with the app. + .. _discord_ui_kit: Bot UI Kit