From 9b1d710bce0b3c33087f013200eeb04ef1b62f5d Mon Sep 17 00:00:00 2001 From: grey-cat-1908 Date: Sat, 30 Jul 2022 15:49:46 +0300 Subject: [PATCH] feat(interactions): slashcommands base --- melisa/core/http.py | 38 ++--- melisa/models/interactions/__init__.py | 2 +- melisa/models/interactions/commands.py | 187 +++++++++++++++---------- melisa/models/interactions/i18n.py | 57 ++++++++ melisa/rest.py | 92 ++++++------ 5 files changed, 228 insertions(+), 148 deletions(-) create mode 100644 melisa/models/interactions/i18n.py diff --git a/melisa/core/http.py b/melisa/core/http.py index dfe7aad..e44e835 100644 --- a/melisa/core/http.py +++ b/melisa/core/http.py @@ -70,9 +70,9 @@ class HTTPClient: *, _ttl: int = None, headers: Optional[Dict[str, Any]] = None, - params: Optional[Dict] = None, + params: Optional[Dict[str, Any]] = None, **kwargs, - ) -> Optional[Dict]: + ) -> Optional[Dict[str, Any]]: """Send an API request to the Discord API.""" ttl = _ttl or self.max_ttl @@ -113,7 +113,7 @@ class HTTPClient: *, _ttl: int = None, **kwargs, - ) -> Optional[Dict]: + ) -> Optional[Dict[str, Any]]: """Handle responses from the Discord API.""" _logger.debug(f"Received response for the {endpoint} ({await res.text()})") @@ -165,7 +165,7 @@ class HTTPClient: return await self.__send(method, endpoint, _ttl=_ttl - 1, **kwargs) - async def get(self, route: str, *, params: Optional[Dict] = None) -> Optional[Dict]: + async def get(self, route: str, *, params: Optional[Dict[str, Any]] = None) -> Optional[Dict[str, Any]]: """|coro| Sends a GET request to a Discord REST API endpoint. @@ -187,22 +187,22 @@ class HTTPClient: self, route: str, *, - headers: dict = None, - json: Optional[Dict] = None, - data=None, - ) -> Optional[Dict]: + headers: Dict[str, Any] = None, + json: Optional[Dict[str, Any]] = None, + data: Optional[Dict[str, Any]] = None, + ) -> Optional[Dict[str, Any]]: """|coro| Sends a POST request to a Discord REST API endpoint. Parameters ---------- - route : :class:`str` + route: :class:`str` The endpoint to send the request to. - json : Dict + json: Dict[str, Any] Json data to post - data : Any + data: Any Data to post - headers : :class:`dict` + headers: Dict[str, Any] Custom request headers Returns @@ -212,7 +212,7 @@ class HTTPClient: """ return await self.__send("POST", route, json=json, data=data, headers=headers) - async def delete(self, route: str, *, headers: dict = None) -> Optional[Dict]: + async def delete(self, route: str, *, headers: Dict[str, Any] = None) -> Optional[Dict[str, Any]]: """|coro| Sends a DELETE request to a Discord REST API endpoint. @@ -234,10 +234,10 @@ class HTTPClient: self, route: str, *, - headers: dict = None, - json: Optional[Dict] = None, - data=None, - ) -> Optional[Dict]: + headers: Dict[str, Any] = None, + json: Optional[Dict[str, Any]] = None, + data: Optional[Dict[str, Any]] = None, + ) -> Optional[Dict[str, Any]]: """|coro| Sends a PATCH request to a Discord REST API endpoint. @@ -260,8 +260,8 @@ class HTTPClient: return await self.__send("PATCH", route, json=json, data=data, headers=headers) async def put( - self, route: str, *, headers: dict = None, data: Optional[Dict] = None - ) -> Optional[Dict]: + self, route: str, *, headers: Dict[str, Any] = None, data: Optional[Dict[str, Any]] = None + ) -> Optional[Dict[str, Any]]: """|coro| Sends a PUT request to a Discord REST API endpoint. diff --git a/melisa/models/interactions/__init__.py b/melisa/models/interactions/__init__.py index 2d4d358..e229069 100644 --- a/melisa/models/interactions/__init__.py +++ b/melisa/models/interactions/__init__.py @@ -1,7 +1,7 @@ # Copyright MelisaDev 2022 - Present # Full MIT License can be found in `LICENSE.txt` at the project root. -from .commands import ApplicationCommandTypes +from .commands import ApplicationCommandType __all__ = "ApplicationCommandTypes" diff --git a/melisa/models/interactions/commands.py b/melisa/models/interactions/commands.py index 1b58ef4..279b302 100644 --- a/melisa/models/interactions/commands.py +++ b/melisa/models/interactions/commands.py @@ -6,14 +6,15 @@ from __future__ import annotations from enum import IntEnum from typing import Optional, Dict, Union, Any, List +from .i18n import LocalizedField from ..guild.channel import ChannelType from ...utils.snowflake import Snowflake from ...utils.conversion import try_enum from ...utils.api_model import APIModelBase -class ApplicationCommandTypes(IntEnum): - """Application Command Types +class ApplicationCommandType(IntEnum): + """Application Command Type Attributes ---------- @@ -33,8 +34,8 @@ class ApplicationCommandTypes(IntEnum): return self.value -class ApplicationCommandOptionTypes(IntEnum): - """Application Command Option Types +class SlashCommandOptionType(IntEnum): + """Application Command Option Type Attributes ---------- @@ -78,43 +79,27 @@ class ApplicationCommandOptionTypes(IntEnum): return self.value -class ApplicationCommand(APIModelBase): - """Application Command +class PartialApplicationCommand(APIModelBase): + """Represents Partial Application Command Attributes ---------- id: :class:`~melisa.utils.snowflake.Snowflake` Unique ID of command - type: Optional[:class:`~melisa.interactions.commands.ApplicationCommandTypes`] + type: Optional[:class:`~melisa.interactions.commands.SlashCommandType`] Type of command, defaults to ``1`` application_id: :class:`~melisa.utils.snowflake.Snowflake` ID of the parent application guild_id: Optional[:class:`~melisa.utils.snowflake.Snowflake`] guild id of the command, if not global - name: str - Name of command, 1-32 characters - name_localizations: Optional[Dict[str, str]] - Localization dictionary for ``name`` field. - Values follow the same restrictions as ``name`` - description: str - Description for ``CHAT_INPUT`` commands, 1-100 characters. - Empty string for ``USER`` and ``MESSAGE`` commands - description_localizations: Optional[Dict[str, str]] - Localization dictionary for ``description`` field. - Values follow the same restrictions as ``description`` - options: Optional[List[:class:`~melisa.models.interactions.commands.ApplicationCommandOption`]] - Parameters for the command, max of 25. - Only available for ``CHAT_INPUT`` command type. - default_member_permissions: Optional[str] + name: :class:`~melisa.models.interactions.i18n.LocalizedField` + Name of the command + default_member_permissions: str Set of permissions represented as a bit set - dm_permission: Optional[bool] + dm_permission: bool Indicates whether the command is available in DMs with the app, only for globally-scoped commands. By default, commands are visible. - default_permission: Optional[bool] - Not recommended for use as field will soon be deprecated. - Indicates whether the command is enabled by default - when the app is added to a guild, defaults to true version: :class:`~melisa.utils.snowflake.Snowflake` Autoincrementing version identifier updated during substantial record changes """ @@ -122,17 +107,12 @@ class ApplicationCommand(APIModelBase): # ToDo: Better Permissions id: Snowflake = None - type: Optional[ApplicationCommandTypes] = 1 + type: Optional[ApplicationCommandType] = 1 application_id: Snowflake = None guild_id: Optional[Snowflake] = None - name: str = None - name_localizations: Optional[Dict[str, str]] = None - description: str = None - description_localizations: Optional[Dict[str, str]] = None - options: Optional[List[ApplicationCommandOption]] = None - default_member_permissions: Optional[str] = None - dm_permission: Optional[bool] = True - default_permission: Optional[bool] = True + name: LocalizedField = None + default_member_permissions: str = None + dm_permission: bool = True version: Snowflake = None @classmethod @@ -142,32 +122,76 @@ class ApplicationCommand(APIModelBase): Parameters ---------- data: :class:`dict` - The dictionary to convert into a ApplicationCommand. + The dictionary to convert into a PartialApplicationCommand. """ - self: ApplicationCommand = super().__new__(cls) + self: PartialApplicationCommand = super().__new__(cls) self.id = Snowflake(data.get("id", 0)) - self.type = data.get("type", 1) + self.type = try_enum(ApplicationCommandType, data.get("type", 1)) + self.application_id = Snowflake(data.get("application_id")) + self.guild_id = ( + Snowflake(data["guild_id"]) if data.get("guild_id") is not None else None + ) + + name = data.get("name") + name_localizations = data.get("name_localizations") + + self.name = LocalizedField(name, name_localizations) + + self.default_member_permissions = data.get("default_member_permissions", "") + self.dm_permission = data.get("dm_permission", True) + self.version = Snowflake(data.get("version", 0)) + + return self + + +class SlashCommand(PartialApplicationCommand): + """Represents SlashCommand + + Attributes + ---------- + description: :class:`~melisa.models.interactions.i18n.LocalizedField` + Description of command + """ + + description: LocalizedField = None + + @classmethod + def from_dict(cls, data: Dict[str, Any]): + """Generate a SlashCommand from the given data. + + Parameters + ---------- + data: :class:`dict` + The dictionary to convert into a SlashCommand. + """ + self: SlashCommand = super().__new__(cls) + + self.id = Snowflake(data.get("id", 0)) + self.type = try_enum(ApplicationCommandType, data.get("type", 1)) self.application_id = Snowflake(data.get("application_id")) self.guild_id = ( Snowflake(data["guild_id"]) if data.get("guild_id") is not None else None ) self.name = data.get("name") self.name_localizations = data.get("name_localizations") - self.description = data.get("description") - self.description_localizations = data.get("description_localizations") + + description = data.get("description") + description_localizations = data.get("description_localizations") + + self.description = LocalizedField(description, description_localizations) + self.options = [ - ApplicationCommandOption.from_dict(x) for x in data.get("options", []) + SlashCommandOption.from_dict(x) for x in data.get("options", []) ] - self.default_member_permissions = data.get("default_member_permissions") + self.default_member_permissions = data.get("default_member_permissions", "") self.dm_permission = data.get("dm_permission", True) - self.default_permission = data.get("default_permission", True) self.version = Snowflake(data.get("version", 0)) return self -class ApplicationCommandOption(APIModelBase): +class SlashCommandOption(APIModelBase): """Application Command Option .. warning:: @@ -176,7 +200,7 @@ class ApplicationCommandOption(APIModelBase): Attributes ---------- - type: :class:`~melisa.models.interactions.commands.ApplicationCommandOptionTypes` + type: :class:`~melisa.models.interactions.commands.SlashCommandOptionType` Type of option name: str 1-32 character name @@ -190,10 +214,10 @@ class ApplicationCommandOption(APIModelBase): Values follow the same restrictions as ``description`` required: Optional[bool] If the parameter is required or optional--default false - choices: Optional[List[:class:`~melisa.models.interactions.commands.ApplicationCommandOptionChoice`]] + choices: Optional[List[:class:`~melisa.models.interactions.commands.SlashCommandOptionChoice`]] Choices for ``STRING``, ``INTEGER``, and ``NUMBER`` types for the user to pick from, max 25 - options: Optional[List[ApplicationCommandOption]] + options: Optional[List[SlashCommandOption]] If the option is a subcommand or subcommand group type, these nested options will be the parameters channel_types: Optional[List[:class:`~melisa.models.guild.channel.ChannelType`]] @@ -210,14 +234,14 @@ class ApplicationCommandOption(APIModelBase): ``int``, or ``float`` type option """ - type: ApplicationCommandOptionTypes = None + type: SlashCommandOptionType = None name: str = None name_localizations: Dict[str, str] = None description: str description_localizations: Dict[str, str] = None required: Optional[bool] = False - choices: Optional[List[ApplicationCommandOptionChoice]] = None - options: Optional[List[ApplicationCommandOption]] = None + choices: Optional[List[SlashCommandOptionChoice]] = None + options: Optional[List[SlashCommandOption]] = None channel_types: Optional[List[ChannelType]] = None min_value: Optional[int, float] = None max_value: Optional[int, float] = None @@ -225,26 +249,26 @@ class ApplicationCommandOption(APIModelBase): @classmethod def from_dict(cls, data: Dict[str, Any]): - """Generate a ApplicationCommandOption from the given data. + """Generate a SlashCommandOption from the given data. Parameters ---------- data: :class:`dict` - The dictionary to convert into a ApplicationCommandOption. + The dictionary to convert into a SlashCommandOption. """ - self: ApplicationCommandOption = super().__new__(cls) + self: SlashCommandOption = super().__new__(cls) - self.type = try_enum(ApplicationCommandOptionTypes, data.get("type", 0)) + self.type = try_enum(SlashCommandOptionType, data.get("type", 0)) self.name = data.get("name") self.name_localizations = data.get("name_localizations") self.description = data.get("description") self.description_localizations = data.get("description_localizations") self.required = data.get("required", False) self.choices = [ - try_enum(ApplicationCommandOptionChoice, x) for x in data.get("choices", []) + try_enum(SlashCommandOptionChoice, x) for x in data.get("choices", []) ] self.options = [ - ApplicationCommandOption.from_dict(x) for x in data.get("options", []) + SlashCommandOption.from_dict(x) for x in data.get("options", []) ] self.channel_types = [ try_enum(ChannelType, x) for x in data.get("channel_types", []) @@ -256,7 +280,7 @@ class ApplicationCommandOption(APIModelBase): return self -class ApplicationCommandOptionChoice(APIModelBase): +class SlashCommandOptionChoice(APIModelBase): """Application Command Option Choice If you specify ``choices`` for an option, @@ -283,14 +307,14 @@ class ApplicationCommandOptionChoice(APIModelBase): @classmethod def from_dict(cls, data: Dict[str, Any]): - """Generate a ApplicationCommandOptionChoice from the given data. + """Generate a SlashCommandOptionChoice from the given data. Parameters ---------- data: :class:`dict` - The dictionary to convert into a ApplicationCommandOptionChoice. + The dictionary to convert into a SlashCommandOptionChoice. """ - self: ApplicationCommandOptionChoice = super().__new__(cls) + self: SlashCommandOptionChoice = super().__new__(cls) self.name = data.get("name") self.name_localizations = data.get("name_localizations") @@ -299,8 +323,8 @@ class ApplicationCommandOptionChoice(APIModelBase): return self -class ApplicationCommandInteractionDataOption(APIModelBase): - """Application Command Interaction Data Option +class SlashCommandInteractionDataOption(APIModelBase): + """Slash Command Interaction Data Option All options have names, and an option can either be a parameter and input value--in which case ``value`` will be set--or it @@ -313,40 +337,53 @@ class ApplicationCommandInteractionDataOption(APIModelBase): ---------- name: :class:`str` Name of the parameter - type: :class:`~melisa.models.interactions.commands.ApplicationCommandOptionTypes` - Value of :class:`~melisa.models.interactions.commands.ApplicationCommandOptionTypes` + type: :class:`~melisa.models.interactions.commands.SlashCommandOptionType` + Value of :class:`~melisa.models.interactions.commands.SlashCommandOptionType` value: Optional[Union[str, int, float]] Value of the option resulting from user input - options: Optional[List[ApplicationCommandInteractionDataOption]] + options: Optional[List[SlashCommandInteractionDataOption]] Present if this option is a group or subcommand focused: Optional[bool] ``true`` if this option is the currently focused option for autocomplete """ name: str = None - type: ApplicationCommandOptionTypes = None + type: SlashCommandOptionType = None value: Optional[Union[str, int, float]] = None - options: Optional[List[ApplicationCommandInteractionDataOption]] = None - focused: Optional[bool] = None + options: Optional[List[SlashCommandInteractionDataOption]] = None + focused: bool = None @classmethod def from_dict(cls, data: Dict[str, Any]): - """Generate a ApplicationCommandInteractionDataOption from the given data. + """Generate a SlashCommandInteractionDataOption from the given data. Parameters ---------- data: :class:`dict` - The dictionary to convert into a ApplicationCommandInteractionDataOption. + The dictionary to convert into a SlashCommandInteractionDataOption. """ - self: ApplicationCommandInteractionDataOption = super().__new__(cls) + self: SlashCommandInteractionDataOption = super().__new__(cls) self.name = data.get("name") - self.type = try_enum(ApplicationCommandOptionTypes, data.get("type", 0)) + self.type = try_enum(SlashCommandOptionType, data.get("type", 0)) self.value = data.get("value") self.options = [ - ApplicationCommandInteractionDataOption.from_dict(x) + SlashCommandInteractionDataOption.from_dict(x) for x in data.get("options", []) ] - self.focused = data.get("focused") + self.focused = data.get("focused", False) return self + + +# noinspection PyTypeChecker +command_types_for_converting: Dict[ApplicationCommandType, PartialApplicationCommand] = { + ApplicationCommandType.CHAT_INPUT: SlashCommand +} + + +def _choose_command_type(data): + data.update({"type": ApplicationCommandType(data.pop("type"))}) + + command_cls = command_types_for_converting.get(data["type"], PartialApplicationCommand) + return command_cls.from_dict(data) diff --git a/melisa/models/interactions/i18n.py b/melisa/models/interactions/i18n.py new file mode 100644 index 0000000..eaa0a7c --- /dev/null +++ b/melisa/models/interactions/i18n.py @@ -0,0 +1,57 @@ +# Copyright MelisaDev 2022 - Present +# Full MIT License can be found in `LICENSE.txt` at the project root. + +from __future__ import annotations + +from typing import Dict + + +class LocalizedField: + original: str + localizations: str + + def __init__( + self, + original: str = None, + localizations: Dict[str, str] = None, + ): + self.original: str = original + self.localizations: Dict[str, str] = localizations + + def insert(self, locale: str, value: str) -> LocalizedField: + self.localizations[locale] = value + return self + + def remove(self, locale: str) -> LocalizedField: + self.localizations.pop(locale, None) + return self + + def __repr__(self): + return f"" + + def __eq__(self, other): + return self.original == other.original and self.localizations == other.localizations + + def __hash__(self): + return hash((self.original, self.localizations)) + + def __getitem__(self, key): + return self.localizations[key] + + def __setitem__(self, key, value): + self.localizations[key] = value + + def __delitem__(self, key): + self.localizations.pop(key, None) + + def __contains__(self, key): + return key in self.localizations + + def __iter__(self): + return iter(self.localizations) + + def __len__(self): + return len(self.localizations) + + def __str__(self): + return self.original diff --git a/melisa/rest.py b/melisa/rest.py index 03d3515..b2ee641 100644 --- a/melisa/rest.py +++ b/melisa/rest.py @@ -6,8 +6,10 @@ from typing import Union, Optional, List, Dict, Any, AsyncIterator from aiohttp import FormData -from .models.interactions import ApplicationCommandTypes -from .models.interactions.commands import ApplicationCommandOption, ApplicationCommand +from .models.interactions import ApplicationCommandType +from .models.interactions.commands import SlashCommandOption, SlashCommand, PartialApplicationCommand, \ + _choose_command_type +from .models.interactions.i18n import LocalizedField from .models.message import Embed, File, AllowedMentions, Message from .exceptions import EmbedFieldError from .core.http import HTTPClient @@ -856,7 +858,7 @@ class RESTApp: application_id: Union[int, str, Snowflake], *, with_localizations: Optional[bool] = False, - ) -> List[ApplicationCommand]: + ) -> List[PartialApplicationCommand]: """|coro| [**REST API**] Fetch all of the global commands for your application. @@ -882,7 +884,7 @@ class RESTApp: """ return [ - ApplicationCommand.from_dict(x) + _choose_command_type(x) for x in await self._http.get( f"/applications/{application_id}/commands?with_localizations={with_localizations}" ) @@ -891,17 +893,15 @@ class RESTApp: async def create_global_application_command( self, application_id: Union[int, str, Snowflake], - command_type: ApplicationCommandTypes, - name: str, - description: str = None, + command_type: ApplicationCommandType, + name: LocalizedField, + description: LocalizedField = None, *, - name_localizations: Optional[Dict[str, str]] = None, - description_localizations: Optional[Dict[str, str]] = None, - options: Optional[List[ApplicationCommandOption]] = None, + options: Optional[List[SlashCommandOption]] = None, default_member_permissions: Optional[str] = None, dm_permission: Optional[bool] = None, default_permission: Optional[bool] = None, - ) -> ApplicationCommand: + ) -> PartialApplicationCommand: """|coro| [**REST API**] Create a new global command. @@ -910,20 +910,14 @@ class RESTApp: ---------- application_id: :class:`~melisa.utils.snowflake.Snowflake` ID of the parent application - command_type: Optional[:class:`~melisa.interactions.commands.ApplicationCommandTypes`] + command_type: Optional[:class:`~melisa.interactions.commands.ApplicationCommandType`] Type of command, defaults to ``1`` - name: str + name: :class:`~melisa.models.interactions.i18n.LocalizedField` Name of command, 1-32 characters - description: str + description: Optional[:class:`~melisa.models.interactions.i18n.LocalizedField`] Description for ``CHAT_INPUT`` commands, 1-100 characters. Empty string for ``USER`` and ``MESSAGE`` commands - name_localizations: Optional[Dict[str, str]] - Localization dictionary for ``name`` field. - Values follow the same restrictions as ``name`` - description_localizations: Optional[Dict[str, str]] - Localization dictionary for ``description`` field. - Values follow the same restrictions as ``description`` - options: Optional[List[:class:`~melisa.models.interactions.commands.ApplicationCommandOption`]] + options: Optional[List[:class:`~melisa.models.interactions.commands.SlashCommandOption`]] Parameters for the command, max of 25. Only available for ``CHAT_INPUT`` command type. default_member_permissions: Optional[str] @@ -948,16 +942,16 @@ class RESTApp: """ data = { - "name": name, - "description": description, + "name": name.original, + "description": description.original, "type": int(command_type), } - if name_localizations is not None: - data["name_localizations"] = name_localizations + if name.localizations is not None: + data["name_localizations"] = name.localizations - if description_localizations is not None: - data["description_localizations"] = description_localizations + if description.localizations is not None: + data["description_localizations"] = description.localizations if default_member_permissions is not None: data["default_member_permissions"] = default_member_permissions @@ -971,7 +965,7 @@ class RESTApp: if default_permission is not None: data["default_permission"] = default_permission - return ApplicationCommand.from_dict( + return _choose_command_type( await self._http.post(f"/applications/{application_id}/commands", json=data) ) @@ -979,7 +973,7 @@ class RESTApp: self, application_id: Union[int, str, Snowflake], command_id: Union[int, str, Snowflake], - ) -> ApplicationCommand: + ) -> PartialApplicationCommand: """|coro| [**REST API**] Fetch a global command for your application. @@ -1001,7 +995,7 @@ class RESTApp: You provided a wrong arguments """ - return ApplicationCommand.from_dict( + return _choose_command_type( await self._http.get( f"/applications/{application_id}/commands/{command_id}" ) @@ -1012,15 +1006,13 @@ class RESTApp: application_id: Union[int, str, Snowflake], command_id: Union[int, str, Snowflake], *, - name: Optional[str] = None, - description: Optional[str] = None, - name_localizations: Optional[Dict[str, str]] = None, - description_localizations: Optional[Dict[str, str]] = None, - options: Optional[List[ApplicationCommandOption]] = None, + name: Optional[LocalizedField] = None, + description: Optional[LocalizedField] = None, + options: Optional[List[SlashCommandOption]] = None, default_member_permissions: Optional[str] = None, dm_permission: Optional[bool] = None, default_permission: Optional[bool] = None, - ) -> ApplicationCommand: + ) -> PartialApplicationCommand: """|coro| All parameters are optional, but any parameters @@ -1034,17 +1026,11 @@ class RESTApp: ID of the parent application command_id: Optional[bool] ID of command to edit. - name: Optional[str] + name: Optional[:class:`~melisa.models.interactions.i18n.LocalizedField`] Name of command, 1-32 characters - description: Optional[str] + description: Optional[:class:`~melisa.models.interactions.i18n.LocalizedField`] Description for ``CHAT_INPUT`` commands, 1-100 characters. Empty string for ``USER`` and ``MESSAGE`` commands - name_localizations: Optional[Dict[str, str]] - Localization dictionary for ``name`` field. - Values follow the same restrictions as ``name`` - description_localizations: Optional[Dict[str, str]] - Localization dictionary for ``description`` field. - Values follow the same restrictions as ``description`` options: Optional[List[:class:`~melisa.models.interactions.commands.ApplicationCommandOption`]] Parameters for the command, max of 25. Only available for ``CHAT_INPUT`` command type. @@ -1071,17 +1057,17 @@ class RESTApp: data = {} - if name is not None: - data["name"] = name + if name.original is not None: + data["name"] = name.original - if description is not None: - data["description"] = description + if description.original is not None: + data["description"] = description.original - if name_localizations is not None: - data["name_localizations"] = name_localizations + if name.localizations is not None: + data["name_localizations"] = name.localizations - if description_localizations is not None: - data["description_localizations"] = description_localizations + if description.localizations is not None: + data["description_localizations"] = description.localizations if default_member_permissions is not None: data["default_member_permissions"] = default_member_permissions @@ -1095,7 +1081,7 @@ class RESTApp: if default_permission is not None: data["default_permission"] = default_permission - return ApplicationCommand.from_dict( + return _choose_command_type( await self._http.patch( f"/applications/{application_id}/commands/{command_id}", json=data )