diff --git a/discord/app_commands/models.py b/discord/app_commands/models.py index 5851e7d8c..1b2b26145 100644 --- a/discord/app_commands/models.py +++ b/discord/app_commands/models.py @@ -254,8 +254,8 @@ class AppCommand(Hashable): 'description': self.description, 'name_localizations': {str(k): v for k, v in self.name_localizations.items()}, 'description_localizations': {str(k): v for k, v in self.description_localizations.items()}, - '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, + 'contexts': self.allowed_contexts.to_array() if self.allowed_contexts else None, + 'integration_types': self.allowed_installs.to_array() if self.allowed_installs else None, 'options': [opt.to_dict() for opt in self.options], } # type: ignore # Type checker does not understand this literal. @@ -394,6 +394,41 @@ class AppCommand(Hashable): ) return AppCommand(data=data, state=state) + async def sync( + self, + ) -> AppCommand: + """|coro| + + Syncs the application command to Discord. + + .. versionadded:: 2.6 + + Raises + ------- + HTTPException + Syncing the commands failed. + Forbidden + The client does not have the ``applications.commands`` scope in the guild. + MissingApplicationID + The client does not have an application ID. + + Returns + -------- + :class:`AppCommand` + The application command that got synced. + """ + state = self._state + if not state.application_id: + raise MissingApplicationID + + payload = self.to_dict() + if self.guild_id: + data = await state.http.upsert_guild_command(state.application_id, self.guild_id, payload=payload) + else: + data = await state.http.upsert_global_command(state.application_id, payload=payload) + + return AppCommand(data=data, state=state) + async def fetch_permissions(self, guild: Snowflake) -> GuildAppCommandPermissions: """|coro| diff --git a/discord/app_commands/tree.py b/discord/app_commands/tree.py index 3099071c0..5957db1ad 100644 --- a/discord/app_commands/tree.py +++ b/discord/app_commands/tree.py @@ -1136,6 +1136,72 @@ class CommandTree(Generic[ClientT]): return [AppCommand(data=d, state=self._state) for d in data] + async def sync_command( + self, command: Union[AppCommand, Command, ContextMenu, Group], *, guild: Optional[Snowflake] = None + ) -> AppCommand: + """|coro| + + Syncs a single application command to Discord. + + .. note :: + This is not recommended for use in most cases, use :meth:`.sync` instead. + + .. versionadded:: 2.6 + + Parameters + ----------- + command: Union[:class:`AppCommand`, :class:`Command`, :class:`ContextMenu`, :class:`Group`] + The application command to sync. + If this is an :class:`AppCommand`, it will call its :meth:`AppCommand.sync` method. + guild: Optional[:class:`~discord.abc.Snowflake`] + The guild to sync the command in. If not provided, the command is synced globally. + This is not applicable for :class:`AppCommand` instances, as they are already guild- + specific. + + Raises + ------- + HTTPException + Syncing the commands failed. + CommandSyncFailure + Syncing the commands failed due to a user related error, typically because + the command has invalid data. This is equivalent to an HTTP status code of + 400. + Forbidden + The client does not have the ``applications.commands`` scope in the guild. + MissingApplicationID + The client does not have an application ID. + TranslationError + An error occurred while translating the commands. + + Returns + -------- + :class:`AppCommand` + The application command that got synced. + """ + if isinstance(command, AppCommand): + return await command.sync() + + if self.client.application_id is None: + raise MissingApplicationID + + translator = self.translator + if translator: + payload = await command.get_translated_payload(self, translator) + else: + payload = command.to_dict(self) + + try: + if guild is None: + data = await self._http.upsert_global_command(self.client.application_id, payload=payload) # type: ignore + else: + data = await self._http.upsert_guild_command(self.client.application_id, guild.id, payload=payload) # type: ignore + except HTTPException as e: + if e.status == 400 and e.code == 50035: + raise CommandSyncFailure(e, [command]) from None + raise + + return AppCommand(data=data, state=self._state) + async def _dispatch_error(self, interaction: Interaction[ClientT], error: AppCommandError, /) -> None: command = interaction.command interaction.command_failed = True diff --git a/discord/http.py b/discord/http.py index 02fd1e136..0171541e0 100644 --- a/discord/http.py +++ b/discord/http.py @@ -2291,7 +2291,7 @@ class HTTPClient: self, application_id: Snowflake, guild_id: Snowflake, - payload: Dict[str, Any], + payload: command.ApplicationCommand, ) -> Response[command.ApplicationCommand]: r = Route( 'POST',