From 91306d7e89b2c2d172454966b91c9b6aa3d8090d Mon Sep 17 00:00:00 2001 From: Katelyn Gigante Date: Tue, 12 Nov 2024 12:25:15 +1100 Subject: [PATCH] Feat: Add support for Application Emoji --- interactions/api/http/http_requests/emojis.py | 85 +++++++++++++++++++ interactions/client/smart_cache.py | 4 +- interactions/models/discord/application.py | 36 ++++++++ interactions/models/discord/emoji.py | 4 +- 4 files changed, 126 insertions(+), 3 deletions(-) diff --git a/interactions/api/http/http_requests/emojis.py b/interactions/api/http/http_requests/emojis.py index c8a683cd0..1e94616c7 100644 --- a/interactions/api/http/http_requests/emojis.py +++ b/interactions/api/http/http_requests/emojis.py @@ -110,3 +110,88 @@ async def delete_guild_emoji( Route("DELETE", "/guilds/{guild_id}/emojis/{emoji_id}", guild_id=guild_id, emoji_id=emoji_id), reason=reason, ) + + async def get_application_emojis(self, application_id: "Snowflake_Type") -> list[discord_typings.EmojiData]: + """ + Fetch all emojis for this application + + Args: + application_id: The id of the application + + Returns: + List of emojis + + """ + result = await self.request(Route("GET", f"/applications/{application_id}/emojis")) + result = cast(dict, result) + return cast(list[discord_typings.EmojiData], result["items"]) + + async def get_application_emoji( + self, application_id: "Snowflake_Type", emoji_id: "Snowflake_Type" + ) -> discord_typings.EmojiData: + """ + Fetch an emoji for this application + + Args: + application_id: The id of the application + emoji_id: The id of the emoji + + Returns: + Emoji object + + """ + result = await self.request(Route("GET", f"/applications/{application_id}/emojis/{emoji_id}")) + return cast(discord_typings.EmojiData, result) + + async def create_application_emoji( + self, payload: dict, application_id: "Snowflake_Type", reason: str | None = None + ) -> discord_typings.EmojiData: + """ + Create an emoji for this application + + Args: + application_id: The id of the application + name: The name of the emoji + imagefile: The image file to use for the emoji + + Returns: + Emoji object + + """ + result = await self.request( + Route("POST", f"/applications/{application_id}/emojis"), payload=payload, reason=reason + ) + return cast(discord_typings.EmojiData, result) + + async def edit_application_emoji( + self, application_id: "Snowflake_Type", emoji_id: "Snowflake_Type", name: str + ) -> discord_typings.EmojiData: + """ + Edit an emoji for this application + + Args: + application_id: The id of the application + emoji_id: The id of the emoji + name: The new name for the emoji + + Returns: + Emoji object + + """ + result = await self.request( + Route("PATCH", f"/applications/{application_id}/emojis/{emoji_id}"), payload={"name": name} + ) + return cast(discord_typings.EmojiData, result) + + async def delete_application_emoji( + self, application_id: discord_typings.Snowflake, emoji_id: discord_typings.Snowflake + ) -> None: + """ + Delete an emoji for this application + + Args: + application_id: The id of the application + emoji_id: The id of the emoji + + """ + await self.request(Route("DELETE", f"/applications/{application_id}/emojis/{emoji_id}")) diff --git a/interactions/client/smart_cache.py b/interactions/client/smart_cache.py index bc7ab5b14..8f2d7a44e 100644 --- a/interactions/client/smart_cache.py +++ b/interactions/client/smart_cache.py @@ -642,7 +642,7 @@ def place_guild_data(self, data: discord_typings.GuildData) -> Guild: """ guild_id = to_snowflake(data["id"]) - guild: Guild = self.guild_cache.get(guild_id) + guild: Guild | None = self.guild_cache.get(guild_id) if guild is None: guild = Guild.from_dict(data, self._client) self.guild_cache[guild_id] = guild @@ -929,7 +929,7 @@ def place_emoji_data(self, guild_id: "Snowflake_Type", data: discord_typings.Emo with suppress(KeyError): del data["guild_id"] # discord sometimes packages a guild_id - this will cause an exception - emoji = CustomEmoji.from_dict(data, self._client, to_snowflake(guild_id)) + emoji = CustomEmoji.from_dict(data, self._client, to_optional_snowflake(guild_id)) if self.emoji_cache is not None: self.emoji_cache[emoji.id] = emoji diff --git a/interactions/models/discord/application.py b/interactions/models/discord/application.py index f6fdee89d..73a379559 100644 --- a/interactions/models/discord/application.py +++ b/interactions/models/discord/application.py @@ -4,8 +4,11 @@ from interactions.client.const import MISSING from interactions.client.utils.attr_converters import optional +from interactions.client.utils.serializer import to_image_data from interactions.models.discord.asset import Asset +from interactions.models.discord.emoji import PartialEmoji from interactions.models.discord.enums import ApplicationFlags +from interactions.models.discord.file import UPLOADABLE_TYPE from interactions.models.discord.snowflake import Snowflake_Type, to_snowflake from interactions.models.discord.team import Team from .base import DiscordObject @@ -88,3 +91,36 @@ def _process_dict(cls, data: Dict[str, Any], client: "Client") -> Dict[str, Any] def owner(self) -> "User": """The user object for the owner of this application""" return self._client.cache.get_user(self.owner_id) + + async def fetch_all_emoji(self) -> List[PartialEmoji]: + """Fetch all emojis for this application""" + response = await self._client.http.get_application_emojis(self.id) + return [self._client.cache.place_emoji_data(None, emoji) for emoji in response] + + async def fetch_emoji(self, emoji_id: Snowflake_Type) -> PartialEmoji: + """Fetch an emoji for this application""" + return await self._client.cache.place_emoji_data( + None, self._client.http.get_application_emoji(self.id, emoji_id) + ) + + async def create_emoji(self, name: str, imagefile: UPLOADABLE_TYPE) -> PartialEmoji: + """Create an emoji for this application""" + data_payload = { + "name": name, + "image": to_image_data(imagefile), + "roles": MISSING, + } + + return self._client.cache.place_emoji_data( + None, await self._client.http.create_application_emoji(data_payload, self.id) + ) + + async def edit_emoji(self, emoji_id: Snowflake_Type, name: str) -> PartialEmoji: + """Edit an emoji for this application""" + return await self._client.cache.place_emoji_data( + None, self._client.http.edit_application_emoji(self.id, emoji_id, name) + ) + + async def delete_emoji(self, emoji_id: Snowflake_Type) -> None: + """Delete an emoji for this application""" + return await self._client.http.delete_application_emoji(self.id, emoji_id) diff --git a/interactions/models/discord/emoji.py b/interactions/models/discord/emoji.py index 0364a7e5e..99152aa2d 100644 --- a/interactions/models/discord/emoji.py +++ b/interactions/models/discord/emoji.py @@ -38,6 +38,8 @@ class PartialEmoji(SnowflakeObject, DictSerializationMixin): """The custom emoji name, or standard unicode emoji in string""" animated: bool = attrs.field(repr=True, default=False) """Whether this emoji is animated""" + available: bool = attrs.field(repr=False, default=True) + """whether this emoji can be used, may be false due to loss of Server Boosts""" @classmethod def from_str(cls, emoji_str: str, *, language: str = "alias") -> Optional["PartialEmoji"]: @@ -120,7 +122,7 @@ class CustomEmoji(PartialEmoji, ClientObject): _role_ids: List["Snowflake_Type"] = attrs.field( repr=False, factory=list, converter=optional(list_converter(to_snowflake)) ) - _guild_id: "Snowflake_Type" = attrs.field(repr=False, default=None, converter=to_snowflake) + _guild_id: "Snowflake_Type" = attrs.field(repr=False, default=None, converter=optional(to_snowflake)) @classmethod def _process_dict(cls, data: Dict[str, Any], client: "Client") -> Dict[str, Any]: