diff --git a/changelog/1570.feature.rst b/changelog/1570.feature.rst new file mode 100644 index 0000000000..0bc817e4b6 --- /dev/null +++ b/changelog/1570.feature.rst @@ -0,0 +1 @@ +Add basic support for "entry point" application commands (:class:`EntryPointCommand`), and launching an activity in response to an interaction (:meth:`InteractionResponse.launch_activity`). diff --git a/disnake/app_commands.py b/disnake/app_commands.py index 336cee6c2b..7d8aac6476 100644 --- a/disnake/app_commands.py +++ b/disnake/app_commands.py @@ -10,6 +10,7 @@ from . import utils from .enums import ( + ApplicationCommandHandlerType, ApplicationCommandPermissionType, ApplicationCommandType, ChannelType, @@ -48,7 +49,8 @@ | Sequence[Localized[str]] ) - APIApplicationCommand: TypeAlias = "APIUserCommand | APIMessageCommand | APISlashCommand" + APIGuildApplicationCommand: TypeAlias = "APIUserCommand | APIMessageCommand | APISlashCommand" + APIApplicationCommand: TypeAlias = APIGuildApplicationCommand | "APIEntryPointCommand" __all__ = ( @@ -60,6 +62,8 @@ "APIUserCommand", "MessageCommand", "APIMessageCommand", + "EntryPointCommand", + "APIEntryPointCommand", "OptionChoice", "Option", "ApplicationCommandPermissions", @@ -67,7 +71,19 @@ ) -def application_command_factory(data: ApplicationCommandPayload) -> APIApplicationCommand: +# `guild_only` is purely a type checking hint, we assume the API only returns the +# types it's supposed (and documented) to return +@overload +def application_command_factory( + data: ApplicationCommandPayload, guild_only: Literal[False] = False +) -> APIApplicationCommand: ... +@overload +def application_command_factory( + data: ApplicationCommandPayload, guild_only: Literal[True] +) -> APIGuildApplicationCommand: ... +def application_command_factory( + data: ApplicationCommandPayload, guild_only: bool = False +) -> APIApplicationCommand: cmd_type = try_enum(ApplicationCommandType, data.get("type", 1)) if cmd_type is ApplicationCommandType.chat_input: return APISlashCommand.from_dict(data) @@ -75,7 +91,10 @@ def application_command_factory(data: ApplicationCommandPayload) -> APIApplicati return APIUserCommand.from_dict(data) if cmd_type is ApplicationCommandType.message: return APIMessageCommand.from_dict(data) + if cmd_type is ApplicationCommandType.primary_entry_point: + return APIEntryPointCommand.from_dict(data) + utils.assert_never(cmd_type) msg = f"Application command of type {cmd_type} is not valid" raise TypeError(msg) @@ -514,6 +533,7 @@ class ApplicationCommand(ABC): # noqa: B024 # this will get refactored eventua - :class:`~.SlashCommand` - :class:`~.MessageCommand` - :class:`~.UserCommand` + - :class:`~.EntryPointCommand` Attributes ---------- @@ -1370,6 +1390,139 @@ def from_dict(cls, data: ApplicationCommandPayload) -> Self: return self +class EntryPointCommand(ApplicationCommand): + """A primary entry point command, used for launching an app's associated activity. + + More details can be found in the :ddocs:`API documentation `. + + .. versionadded:: |vnext| + + Attributes + ---------- + name: :class:`str` + The command's name. + name_localizations: :class:`.LocalizationValue` + Localizations for ``name``. + nsfw: :class:`bool` + Whether this command is :ddocs:`age-restricted `. + Defaults to ``False``. + install_types: :class:`ApplicationInstallTypes` | :data:`None` + The installation types where the command is available. + Defaults to :attr:`ApplicationInstallTypes.guild` only. + contexts: :class:`InteractionContextTypes` | :data:`None` + The interaction contexts where the command can be used. + handler: :class:`ApplicationCommandHandlerType` + Whether the interaction triggered by invoking this command should be handled + by the app or Discord itself. + Defaults to :attr:`ApplicationCommandHandlerType.discord`. + """ + + __repr_attributes__: ClassVar[tuple[str, ...]] = ( + *tuple(n for n in ApplicationCommand.__repr_attributes__ if n != "type"), + "handler", + ) + + def __init__( + self, + name: LocalizedRequired, + default_member_permissions: Permissions | int | None = None, + nsfw: bool | None = None, + install_types: ApplicationInstallTypes | None = None, + contexts: InteractionContextTypes | None = None, + handler: ApplicationCommandHandlerType | None = None, + ) -> None: + super().__init__( + type=ApplicationCommandType.primary_entry_point, + name=name, + default_member_permissions=default_member_permissions, + nsfw=nsfw, + install_types=install_types, + contexts=contexts, + ) + + self.handler: ApplicationCommandHandlerType = ( + handler or ApplicationCommandHandlerType.discord + ) + + def __eq__(self, other: object) -> bool: + return ( + super().__eq__(other) and self.handler == other.handler # pyright: ignore[reportAttributeAccessIssue] + ) + + def to_dict(self) -> EditApplicationCommandPayload: + res = super().to_dict() + res["handler"] = self.handler.value + return res + + +class APIEntryPointCommand(EntryPointCommand, _APIApplicationCommandMixin): + """A primary entry point command returned by the API. + + .. versionadded:: |vnext| + + Attributes + ---------- + name: :class:`str` + The command's name. + name_localizations: :class:`.LocalizationValue` + Localizations for ``name``. + nsfw: :class:`bool` + Whether this command is :ddocs:`age-restricted `. + install_types: :class:`ApplicationInstallTypes` | :data:`None` + The installation types where the command is available. + Defaults to :attr:`ApplicationInstallTypes.guild` only. + contexts: :class:`InteractionContextTypes` | :data:`None` + The interaction contexts where the command can be used. + handler: :class:`ApplicationCommandHandlerType` + Whether the interaction triggered by invoking this command should be handled + by the app or Discord itself. + id: :class:`int` + The command's ID. + application_id: :class:`int` + The application ID this command belongs to. + guild_id: :data:`None` + The ID of the guild this command is enabled in. + Always :data:`None` for this type of command. + version: :class:`int` + Autoincrementing version identifier updated during substantial record changes. + """ + + __repr_attributes__: ClassVar[tuple[str, ...]] = ( + *EntryPointCommand.__repr_attributes__, + *_APIApplicationCommandMixin.__repr_attributes__, + ) + + @classmethod + def from_dict(cls, data: ApplicationCommandPayload) -> Self: + cmd_type = data.get("type", 0) + if cmd_type != ApplicationCommandType.primary_entry_point.value: + msg = f"Invalid payload type for EntryPointCommand: {cmd_type}" + raise ValueError(msg) + + self = cls( + name=Localized(data["name"], data=data.get("name_localizations")), + default_member_permissions=_get_as_snowflake(data, "default_member_permissions"), + nsfw=data.get("nsfw"), + install_types=( + ApplicationInstallTypes._from_values(install_types) + if (install_types := data.get("integration_types")) is not None + else None + ), + contexts=( + InteractionContextTypes._from_values(contexts) + if (contexts := data.get("contexts")) is not None + else None + ), + handler=( + try_enum(ApplicationCommandHandlerType, handler) + if (handler := data.get("handler")) is not None + else None + ), + ) + self._update_common(data) + return self + + class ApplicationCommandPermissions: """Represents application command permissions for a role, user, or channel. diff --git a/disnake/client.py b/disnake/client.py index 003390856d..1c48b43a1e 100644 --- a/disnake/client.py +++ b/disnake/client.py @@ -26,6 +26,7 @@ from . import abc, utils from .activity import ActivityTypes, BaseActivity, create_activity from .app_commands import ( + APIEntryPointCommand, APIMessageCommand, APISlashCommand, APIUserCommand, @@ -76,7 +77,14 @@ from typing_extensions import NotRequired from .abc import GuildChannel, PrivateChannel, Snowflake, SnowflakeTime - from .app_commands import APIApplicationCommand, MessageCommand, SlashCommand, UserCommand + from .app_commands import ( + APIApplicationCommand, + APIGuildApplicationCommand, + EntryPointCommand, + MessageCommand, + SlashCommand, + UserCommand, + ) from .asset import AssetBytes from .channel import DMChannel from .member import Member @@ -609,7 +617,7 @@ def application_flags(self) -> ApplicationFlags: @property def global_application_commands(self) -> list[APIApplicationCommand]: - r""":class:`list`\[:class:`.APIUserCommand` | :class:`.APIMessageCommand` | :class:`.APISlashCommand`]: The client's global application commands.""" + r""":class:`list`\[:class:`.APIUserCommand` | :class:`.APIMessageCommand` | :class:`.APISlashCommand` | :class:`.APIEntryPointCommand`]: The client's global application commands.""" return list(self._connection._global_application_commands.values()) @property @@ -1545,7 +1553,7 @@ def get_all_members(self) -> Generator[Member]: for guild in self.guilds: yield from guild.members - def get_guild_application_commands(self, guild_id: int) -> list[APIApplicationCommand]: + def get_guild_application_commands(self, guild_id: int) -> list[APIGuildApplicationCommand]: r"""Returns a list of all application commands in the guild with the given ID. Parameters @@ -1619,12 +1627,12 @@ def get_global_command(self, id: int) -> APIApplicationCommand | None: Returns ------- - :class:`.APIUserCommand` | :class:`.APIMessageCommand` | :class:`.APISlashCommand` | :data:`None` + :class:`.APIUserCommand` | :class:`.APIMessageCommand` | :class:`.APISlashCommand` | :class:`.APIEntryPointCommand` | :data:`None` The application command. """ return self._connection._get_global_application_command(id) - def get_guild_command(self, guild_id: int, id: int) -> APIApplicationCommand | None: + def get_guild_command(self, guild_id: int, id: int) -> APIGuildApplicationCommand | None: """Returns a guild application command with the given guild ID and application command ID. Parameters @@ -1655,14 +1663,14 @@ def get_global_command_named( Returns ------- - :class:`.APIUserCommand` | :class:`.APIMessageCommand` | :class:`.APISlashCommand` | :data:`None` + :class:`.APIUserCommand` | :class:`.APIMessageCommand` | :class:`.APISlashCommand` | :class:`.APIEntryPointCommand` | :data:`None` The application command. """ return self._connection._get_global_command_named(name, cmd_type) def get_guild_command_named( self, guild_id: int, name: str, cmd_type: ApplicationCommandType | None = None - ) -> APIApplicationCommand | None: + ) -> APIGuildApplicationCommand | None: """Returns a guild application command matching the given name. Parameters @@ -2708,7 +2716,7 @@ async def fetch_global_commands( Returns ------- - :class:`list`\[:class:`.APIUserCommand` | :class:`.APIMessageCommand` | :class:`.APISlashCommand`] + :class:`list`\[:class:`.APIUserCommand` | :class:`.APIMessageCommand` | :class:`.APISlashCommand` | :class:`.APIEntryPointCommand`] A list of application commands. """ return await self._connection.fetch_global_commands(with_localizations=with_localizations) @@ -2727,7 +2735,7 @@ async def fetch_global_command(self, command_id: int) -> APIApplicationCommand: Returns ------- - :class:`.APIUserCommand` | :class:`.APIMessageCommand` | :class:`.APISlashCommand` + :class:`.APIUserCommand` | :class:`.APIMessageCommand` | :class:`.APISlashCommand` | :class:`.APIEntryPointCommand` The requested application command. """ return await self._connection.fetch_global_command(command_id) @@ -2743,6 +2751,11 @@ async def create_global_command( self, application_command: MessageCommand ) -> APIMessageCommand: ... + @overload + async def create_global_command( + self, application_command: EntryPointCommand + ) -> APIEntryPointCommand: ... + @overload async def create_global_command( self, application_command: ApplicationCommand @@ -2764,7 +2777,7 @@ async def create_global_command( Returns ------- - :class:`.APIUserCommand` | :class:`.APIMessageCommand` | :class:`.APISlashCommand` + :class:`.APIUserCommand` | :class:`.APIMessageCommand` | :class:`.APISlashCommand` | :class:`.APIEntryPointCommand` The application command that was created. """ application_command.localize(self.i18n) @@ -2785,6 +2798,11 @@ async def edit_global_command( self, command_id: int, new_command: MessageCommand ) -> APIMessageCommand: ... + @overload + async def edit_global_command( + self, command_id: int, new_command: EntryPointCommand + ) -> APIEntryPointCommand: ... + @overload async def edit_global_command( self, command_id: int, new_command: ApplicationCommand @@ -2808,7 +2826,7 @@ async def edit_global_command( Returns ------- - :class:`.APIUserCommand` | :class:`.APIMessageCommand` | :class:`.APISlashCommand` + :class:`.APIUserCommand` | :class:`.APIMessageCommand` | :class:`.APISlashCommand` | :class:`.APIEntryPointCommand` The edited application command. """ new_command.localize(self.i18n) @@ -2858,7 +2876,7 @@ async def fetch_guild_commands( guild_id: int, *, with_localizations: bool = True, - ) -> list[APIApplicationCommand]: + ) -> list[APIGuildApplicationCommand]: r"""|coro| Retrieves a list of guild application commands. @@ -2883,7 +2901,9 @@ async def fetch_guild_commands( guild_id, with_localizations=with_localizations ) - async def fetch_guild_command(self, guild_id: int, command_id: int) -> APIApplicationCommand: + async def fetch_guild_command( + self, guild_id: int, command_id: int + ) -> APIGuildApplicationCommand: """|coro| Retrieves a guild application command. @@ -2922,11 +2942,11 @@ async def create_guild_command( @overload async def create_guild_command( self, guild_id: int, application_command: ApplicationCommand - ) -> APIApplicationCommand: ... + ) -> APIGuildApplicationCommand: ... async def create_guild_command( self, guild_id: int, application_command: ApplicationCommand - ) -> APIApplicationCommand: + ) -> APIGuildApplicationCommand: """|coro| Creates a guild application command. @@ -2966,11 +2986,11 @@ async def edit_guild_command( @overload async def edit_guild_command( self, guild_id: int, command_id: int, new_command: ApplicationCommand - ) -> APIApplicationCommand: ... + ) -> APIGuildApplicationCommand: ... async def edit_guild_command( self, guild_id: int, command_id: int, new_command: ApplicationCommand - ) -> APIApplicationCommand: + ) -> APIGuildApplicationCommand: """|coro| Edits a guild application command. @@ -3012,7 +3032,7 @@ async def delete_guild_command(self, guild_id: int, command_id: int) -> None: async def bulk_overwrite_guild_commands( self, guild_id: int, application_commands: list[ApplicationCommand] - ) -> list[APIApplicationCommand]: + ) -> list[APIGuildApplicationCommand]: r"""|coro| Overwrites several guild application commands in one API request. diff --git a/disnake/enums.py b/disnake/enums.py index f5527bcd35..52ae687166 100644 --- a/disnake/enums.py +++ b/disnake/enums.py @@ -52,6 +52,7 @@ "OptionType", "ApplicationCommandType", "ApplicationCommandPermissionType", + "ApplicationCommandHandlerType", "GuildScheduledEventEntityType", "GuildScheduledEventStatus", "GuildScheduledEventPrivacyLevel", @@ -1159,6 +1160,11 @@ class InteractionResponseType(Enum): .. deprecated:: 2.11 Use premium buttons (:class:`ui.Button` with :attr:`~ui.Button.sku_id`) instead. """ + launch_activity = 12 + """Responds to the interaction by launching the activity associated with the app. + + .. versionadded:: |vnext| + """ class VideoQualityMode(Enum): @@ -1377,6 +1383,14 @@ class ApplicationCommandType(Enum): """Represents a user command from the context menu.""" message = 3 """Represents a message command from the context menu.""" + primary_entry_point = 4 + """Represents a command that invokes an app's embedded activity. + + .. versionadded:: |vnext| + + .. note:: + Application commands of this type must always be global commands. + """ class ApplicationCommandPermissionType(Enum): @@ -1396,6 +1410,21 @@ def __int__(self) -> int: return self.value +class ApplicationCommandHandlerType(Enum): + """Represents the type of handler responsible for processing interactions + from a :attr:`~ApplicationCommandType.primary_entry_point` application command. + + More details can be found in the :ddocs:`API documentation `. + + .. versionadded:: |vnext| + """ + + app = 1 + """The app handles the interaction by sending an interaction response.""" + discord = 2 + """Discord handles the interaction by launching an activity (without coordinating with the app).""" + + class OptionType(Enum): """Represents the type of an option. diff --git a/disnake/ext/commands/interaction_bot_base.py b/disnake/ext/commands/interaction_bot_base.py index 4df2017f4d..3bf9501dc5 100644 --- a/disnake/ext/commands/interaction_bot_base.py +++ b/disnake/ext/commands/interaction_bot_base.py @@ -20,7 +20,7 @@ import disnake from disnake import utils -from disnake.app_commands import ApplicationCommand, Option +from disnake.app_commands import ApplicationCommand, EntryPointCommand, Option from disnake.custom_warnings import SyncWarning from disnake.enums import ApplicationCommandType from disnake.flags import ApplicationInstallTypes, InteractionContextTypes @@ -105,7 +105,12 @@ def _app_commands_diff( diff["no_changes"].append(new_cmd) for name_and_type, old_cmd in old_cmds.items(): - if name_and_type not in new_cmds: + if isinstance(old_cmd, EntryPointCommand): + # NOTE: entry point commands bypass the traditional sync mechanism, largely because they + # cannot be removed via the bulk update endpoint and are not necessarily handled by + # the bot in the first place + diff["no_changes"].append(old_cmd) + elif name_and_type not in new_cmds: diff["delete"].append(old_cmd) return diff diff --git a/disnake/guild.py b/disnake/guild.py index e1eb40d9a0..4f609a301b 100644 --- a/disnake/guild.py +++ b/disnake/guild.py @@ -86,7 +86,7 @@ if TYPE_CHECKING: from .abc import Snowflake, SnowflakeTime - from .app_commands import APIApplicationCommand + from .app_commands import APIGuildApplicationCommand from .asset import AssetBytes from .automod import AutoModTriggerMetadata from .permissions import Permissions @@ -582,7 +582,7 @@ def _remove_role(self, role_id: int, /) -> Role: return role - def get_command(self, application_command_id: int, /) -> APIApplicationCommand | None: + def get_command(self, application_command_id: int, /) -> APIGuildApplicationCommand | None: """Gets a cached application command matching the specified ID. Parameters @@ -597,7 +597,7 @@ def get_command(self, application_command_id: int, /) -> APIApplicationCommand | """ return self._state._get_guild_application_command(self.id, application_command_id) - def get_command_named(self, name: str, /) -> APIApplicationCommand | None: + def get_command_named(self, name: str, /) -> APIGuildApplicationCommand | None: """Gets a cached application command matching the specified name. Parameters diff --git a/disnake/interactions/base.py b/disnake/interactions/base.py index 3564d64830..516f929009 100644 --- a/disnake/interactions/base.py +++ b/disnake/interactions/base.py @@ -1724,6 +1724,46 @@ async def cool_command(inter: disnake.ApplicationCommandInteraction): self._response_type = response_type + async def launch_activity( + self, + ) -> InteractionCallbackResponse[InteractionCallbackActivityInstance]: + """|coro| + + Responds to this interaction by launching the activity associated with the app. + + Only available for applications with activities enabled. + + .. versionadded:: |vnext| + + Raises + ------ + HTTPException + Sending the response has failed. + InteractionResponded + This interaction has already been responded to before. + + Returns + ------- + :class:`InteractionCallbackResponse` + The callback response data, with an :class:`InteractionCallbackActivityInstance` resource. + """ + if self._response_type is not None: + raise InteractionResponded(self._parent) + + parent = self._parent + adapter = async_context.get() + response_type = InteractionResponseType.launch_activity + callback_data = await adapter.create_interaction_response( + parent.id, + parent.token, + session=parent._session, + type=response_type.value, + ) + + self._response_type = response_type + + return InteractionCallbackResponse(callback_data, parent=self._parent) + class _InteractionMessageState: __slots__ = ("_parent", "_interaction") diff --git a/disnake/state.py b/disnake/state.py index f7ca893187..c32e7a4d3e 100644 --- a/disnake/state.py +++ b/disnake/state.py @@ -93,7 +93,7 @@ from typing import Concatenate from .abc import AnyChannel, MessageableChannel, PrivateChannel - from .app_commands import APIApplicationCommand, ApplicationCommand + from .app_commands import APIApplicationCommand, APIGuildApplicationCommand, ApplicationCommand from .client import Client from .gateway import DiscordWebSocket from .guild import GuildChannel, VocalGuildChannel @@ -309,7 +309,7 @@ def clear( if application_commands: self._global_application_commands: dict[int, APIApplicationCommand] = {} - self._guild_application_commands: dict[int, dict[int, APIApplicationCommand]] = {} + self._guild_application_commands: dict[int, dict[int, APIGuildApplicationCommand]] = {} if views: self._view_store: ViewStore = ViewStore(self) @@ -488,14 +488,14 @@ def _clear_global_application_commands(self) -> None: def _get_guild_application_command( self, guild_id: int, application_command_id: int - ) -> APIApplicationCommand | None: + ) -> APIGuildApplicationCommand | None: granula = self._guild_application_commands.get(guild_id) if granula is not None: return granula.get(application_command_id) return None def _add_guild_application_command( - self, guild_id: int, application_command: APIApplicationCommand + self, guild_id: int, application_command: APIGuildApplicationCommand ) -> None: if not application_command.id: msg = "The provided application command does not have an ID" @@ -528,7 +528,7 @@ def _get_global_command_named( def _get_guild_command_named( self, guild_id: int, name: str, cmd_type: ApplicationCommandType | None = None - ) -> APIApplicationCommand | None: + ) -> APIGuildApplicationCommand | None: granula = self._guild_application_commands.get(guild_id, {}) for cmd in granula.values(): if cmd.name == name and (cmd_type is None or cmd.type is cmd_type): @@ -2394,37 +2394,39 @@ async def fetch_guild_commands( guild_id: int, *, with_localizations: bool = True, - ) -> list[APIApplicationCommand]: + ) -> list[APIGuildApplicationCommand]: assert self.application_id is not None results = await self.http.get_guild_commands( self.application_id, guild_id, with_localizations=with_localizations ) - return [application_command_factory(data) for data in results] + return [application_command_factory(data, guild_only=True) for data in results] - async def fetch_guild_command(self, guild_id: int, command_id: int) -> APIApplicationCommand: + async def fetch_guild_command( + self, guild_id: int, command_id: int + ) -> APIGuildApplicationCommand: assert self.application_id is not None result = await self.http.get_guild_command(self.application_id, guild_id, command_id) - return application_command_factory(result) + return application_command_factory(result, guild_only=True) async def create_guild_command( self, guild_id: int, application_command: ApplicationCommand - ) -> APIApplicationCommand: + ) -> APIGuildApplicationCommand: assert self.application_id is not None result = await self.http.upsert_guild_command( self.application_id, guild_id, application_command.to_dict() ) - cmd = application_command_factory(result) + cmd = application_command_factory(result, guild_only=True) self._add_guild_application_command(guild_id, cmd) return cmd async def edit_guild_command( self, guild_id: int, command_id: int, new_command: ApplicationCommand - ) -> APIApplicationCommand: + ) -> APIGuildApplicationCommand: assert self.application_id is not None result = await self.http.edit_guild_command( self.application_id, guild_id, command_id, new_command.to_dict() ) - cmd = application_command_factory(result) + cmd = application_command_factory(result, guild_only=True) self._add_guild_application_command(guild_id, cmd) return cmd @@ -2435,11 +2437,11 @@ async def delete_guild_command(self, guild_id: int, command_id: int) -> None: async def bulk_overwrite_guild_commands( self, guild_id: int, application_commands: list[ApplicationCommand] - ) -> list[APIApplicationCommand]: + ) -> list[APIGuildApplicationCommand]: assert self.application_id is not None payload = [cmd.to_dict() for cmd in application_commands] results = await self.http.bulk_upsert_guild_commands(self.application_id, guild_id, payload) - commands = [application_command_factory(data) for data in results] + commands = [application_command_factory(data, guild_only=True) for data in results] self._guild_application_commands[guild_id] = {cmd.id: cmd for cmd in commands} return commands diff --git a/disnake/types/interactions.py b/disnake/types/interactions.py index 45380d6fcb..9b47795ec2 100644 --- a/disnake/types/interactions.py +++ b/disnake/types/interactions.py @@ -24,7 +24,8 @@ from .message import AllowedMentions, Attachment, Message -ApplicationCommandType = Literal[1, 2, 3] +ApplicationCommandType = Literal[1, 2, 3, 4] +ApplicationCommandHandlerType = Literal[1, 2] InteractionContextType = Literal[1, 2, 3] # GUILD, BOT_DM, PRIVATE_CHANNEL @@ -46,6 +47,7 @@ class ApplicationCommand(TypedDict): integration_types: NotRequired[list[ApplicationIntegrationType]] contexts: NotRequired[list[InteractionContextType] | None] version: Snowflake + handler: NotRequired[ApplicationCommandHandlerType] ApplicationCommandOptionType = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] @@ -408,7 +410,7 @@ class InteractionAutocompleteResponseData(TypedDict): choices: list[ApplicationCommandOptionChoice] -InteractionResponseType: TypeAlias = Literal[1, 4, 5, 6, 7, 10] +InteractionResponseType: TypeAlias = Literal[1, 4, 5, 6, 7, 8, 9, 10, 12] InteractionResponseData: TypeAlias = ( InteractionApplicationCommandResponseData | InteractionAutocompleteResponseData | Modal @@ -478,6 +480,9 @@ class InteractionCallbackResponse(TypedDict): class EditApplicationCommand(TypedDict): + # n.b. this cannot be changed + type: NotRequired[ApplicationCommandType] + name: str name_localizations: NotRequired[LocalizationDict | None] description: NotRequired[str] @@ -489,5 +494,4 @@ class EditApplicationCommand(TypedDict): nsfw: NotRequired[bool] integration_types: NotRequired[list[ApplicationIntegrationType] | None] contexts: NotRequired[list[InteractionContextType] | None] - # n.b. this cannot be changed - type: NotRequired[ApplicationCommandType] + handler: NotRequired[ApplicationCommandHandlerType] diff --git a/docs/api/app_commands.rst b/docs/api/app_commands.rst index 8511c5e764..357a7abb1e 100644 --- a/docs/api/app_commands.rst +++ b/docs/api/app_commands.rst @@ -38,6 +38,15 @@ APIMessageCommand :members: :inherited-members: +APIEntryPointCommand +~~~~~~~~~~~~~~~~~~~~ + +.. attributetable:: APIEntryPointCommand + +.. autoclass:: APIEntryPointCommand() + :members: + :inherited-members: + ApplicationCommandPermissions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -92,6 +101,15 @@ MessageCommand :members: :inherited-members: +EntryPointCommand +~~~~~~~~~~~~~~~~~ + +.. attributetable:: EntryPointCommand + +.. autoclass:: EntryPointCommand() + :members: + :inherited-members: + Option ~~~~~~ @@ -139,6 +157,12 @@ ApplicationCommandType .. autoclass:: ApplicationCommandType() :members: +ApplicationCommandHandlerType +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: ApplicationCommandHandlerType() + :members: + ApplicationCommandPermissionType ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~