From 65663e0eb99423b92b9a6dd7dfa41ce84cd0cf22 Mon Sep 17 00:00:00 2001 From: grey-cat-1908 Date: Sat, 30 Jul 2022 19:15:49 +0300 Subject: [PATCH] feat(rest): bulk overwrite global application commands method --- melisa/core/http.py | 21 +++-- melisa/models/interactions/commands.py | 13 +-- melisa/models/interactions/i18n.py | 8 +- melisa/rest.py | 116 +++++++++++++++++++++++-- 4 files changed, 140 insertions(+), 18 deletions(-) diff --git a/melisa/core/http.py b/melisa/core/http.py index e44e835..b1cd36e 100644 --- a/melisa/core/http.py +++ b/melisa/core/http.py @@ -6,7 +6,7 @@ from __future__ import annotations import asyncio import logging from urllib.parse import quote -from typing import Dict, Optional, Any +from typing import Dict, Optional, Any, Union, List from aiohttp import ClientSession, ClientResponse @@ -165,7 +165,9 @@ class HTTPClient: return await self.__send(method, endpoint, _ttl=_ttl - 1, **kwargs) - async def get(self, route: str, *, params: Optional[Dict[str, Any]] = None) -> Optional[Dict[str, Any]]: + 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. @@ -212,7 +214,9 @@ class HTTPClient: """ return await self.__send("POST", route, json=json, data=data, headers=headers) - async def delete(self, route: str, *, headers: Dict[str, Any] = None) -> Optional[Dict[str, Any]]: + 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. @@ -260,7 +264,12 @@ class HTTPClient: return await self.__send("PATCH", route, json=json, data=data, headers=headers) async def put( - self, route: str, *, headers: Dict[str, Any] = None, data: Optional[Dict[str, Any]] = None + self, + route: str, + *, + headers: Dict[str, Any] = None, + json: Optional[Union[List[Any], 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. @@ -271,6 +280,8 @@ class HTTPClient: The endpoint to send the request to. data : Dict Data to post + json: Dict + Json data to post headers : :class:`dict` Custom request headers @@ -279,4 +290,4 @@ class HTTPClient: Optional[:class:`Dict`] JSON response from the Discord API. """ - return await self.__send("PUT", route, json=data, headers=headers) + return await self.__send("PUT", route, json=json, data=data, headers=headers) diff --git a/melisa/models/interactions/commands.py b/melisa/models/interactions/commands.py index 2067d6b..11a00d8 100644 --- a/melisa/models/interactions/commands.py +++ b/melisa/models/interactions/commands.py @@ -155,6 +155,7 @@ class SlashCommand(PartialApplicationCommand): """ description: LocalizedField = None + options: List[SlashCommandOption] = None @classmethod def from_dict(cls, data: Dict[str, Any]): @@ -314,7 +315,7 @@ class SlashCommandOptionChoice(APIModelBase): name_localizations = data.get("name_localizations") self.name = LocalizedField(name, name_localizations) - + self.value = data.get("value") return self @@ -374,13 +375,15 @@ class SlashCommandInteractionDataOption(APIModelBase): # noinspection PyTypeChecker -command_types_for_converting: Dict[ApplicationCommandType, PartialApplicationCommand] = { - ApplicationCommandType.CHAT_INPUT: SlashCommand -} +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) + 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 index f0f1ad1..d2dafe3 100644 --- a/melisa/models/interactions/i18n.py +++ b/melisa/models/interactions/i18n.py @@ -3,7 +3,7 @@ from __future__ import annotations -from typing import Dict +from typing import Dict, Optional class LocalizedField: @@ -18,7 +18,6 @@ class LocalizedField: Localization dictionary for the name field. Values follow the same restrictions as name - """ original: str @@ -44,7 +43,10 @@ class LocalizedField: return f"" def __eq__(self, other): - return self.original == other.original and self.localizations == other.localizations + return ( + self.original == other.original + and self.localizations == other.localizations + ) def __hash__(self): return hash((self.original, self.localizations)) diff --git a/melisa/rest.py b/melisa/rest.py index e2cd64c..efb7d69 100644 --- a/melisa/rest.py +++ b/melisa/rest.py @@ -7,8 +7,12 @@ from typing import Union, Optional, List, Dict, Any, AsyncIterator from aiohttp import FormData from .models.interactions import ApplicationCommandType -from .models.interactions.commands import SlashCommandOption, SlashCommand, PartialApplicationCommand, \ - _choose_command_type +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 @@ -943,13 +947,15 @@ class RESTApp: data = { "name": name.original, - "description": description.original, "type": int(command_type), } if name.localizations is not None: data["name_localizations"] = name.localizations + if description.original is not None: + data["description"] = description.original + if description.localizations is not None: data["description_localizations"] = description.localizations @@ -966,7 +972,9 @@ class RESTApp: if option.name.localizations is not None: option_data["name_localizations"] = option.name.localizations if option.description.localizations is not None: - option_data["description_localizations"] = option.description.localizations + option_data[ + "description_localizations" + ] = option.description.localizations if dm_permission is not None: data["dm_permission"] = dm_permission @@ -1081,8 +1089,23 @@ class RESTApp: if default_member_permissions is not None: data["default_member_permissions"] = default_member_permissions + data["options"] = [] + if options is not None: - data["options"] = [x.to_dict() for x in options] + for option in options: + option_data = option.to_dict() + + option_data["name"] = option.name.original + option_data["description"] = option.description.original + + if option.name.localizations is not None: + option_data["name_localizations"] = option.name.localizations + if option.description.localizations is not None: + option_data[ + "description_localizations" + ] = option.description.localizations + + data["options"].append(option_data) if dm_permission is not None: data["dm_permission"] = dm_permission @@ -1126,6 +1149,89 @@ class RESTApp: return None + async def bulk_overwrite_global_application_commands( + self, + application_id: Union[int, str, Snowflake], + commands: List[Union[SlashCommand, PartialApplicationCommand]], + ) -> List[Union[SlashCommand, PartialApplicationCommand]]: + """|coro| + + [**REST API**] Overwrites all existing global commands. + + Parameters + ---------- + application_id: :class:`~melisa.utils.snowflake.Snowflake` + ID of the parent application + commands: List[Union[:class:`~melisa.models.interactions.commands.SlashCommand`, + :class:`~melisa.models.interactions.commands.PartialApplicationCommand`]] + + Raises + ------- + HTTPException + The request to perform the action failed with other http exception. + ForbiddenError + You do not have proper permissions to do the actions required. + BadRequestError + You provided a wrong arguments + """ + + better_commands = [] + + for command in commands: + command_data = { + "name": command.name.original, + "type": int(command.type), + } + + if command.name.localizations is not None: + command_data["name_localizations"] = command.name.localizations + + if command.default_member_permissions is not None: + command_data[ + "default_member_permissions" + ] = command.default_member_permissions + + if command.dm_permission is not None: + command_data["dm_permission"] = command.dm_permission + + if isinstance(command, SlashCommand): + if command.description is not None: + command_data["description"] = command.description.original + + if command.description.localizations is not None: + command_data[ + "description_localizations" + ] = command.description.localizations + + command_data["options"] = [] + + if command.options is not None: + for option in command.options: + option_data = option.to_dict() + + option_data["name"] = option.name.original + option_data["description"] = option.description.original + + if option.name.localizations is not None: + option_data[ + "name_localizations" + ] = option.name.localizations + if option.description.localizations is not None: + option_data[ + "description_localizations" + ] = option.description.localizations + + command_data["options"].append(option_data) + + better_commands.append(command_data) + + return [ + _choose_command_type(x) + for x in await self._http.put( + f"/applications/{application_id}/commands", json=better_commands + ) + ] + class CDNBuilder: """Can be used to build images