Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for Application Emoji #1742

Open
wants to merge 2 commits into
base: unstable
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions interactions/api/http/http_requests/emojis.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}"))
4 changes: 2 additions & 2 deletions interactions/client/smart_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
36 changes: 36 additions & 0 deletions interactions/models/discord/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
4 changes: 3 additions & 1 deletion interactions/models/discord/emoji.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]:
Expand Down Expand Up @@ -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]:
Expand Down
4 changes: 2 additions & 2 deletions tests/test_emoji.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def test_emoji_formatting() -> None:

def test_emoji_processing() -> None:
raw_sample = "<:sparklesnek:910496037708374016>"
dict_sample = {"id": 910496037708374016, "name": "sparklesnek", "animated": False}
dict_sample = {"id": 910496037708374016, "name": "sparklesnek", "animated": False, "available": True}
unicode_sample = "👍"
target = "sparklesnek:910496037708374016"

Expand All @@ -48,7 +48,7 @@ def test_emoji_processing() -> None:

assert isinstance(raw_emoji, dict) and raw_emoji == dict_sample
assert isinstance(dict_emoji, dict) and dict_emoji == dict_sample
assert isinstance(unicode_emoji, dict) and unicode_emoji == {"name": "👍", "animated": False}
assert isinstance(unicode_emoji, dict) and unicode_emoji == {"name": "👍", "animated": False, "available": True}

from_str = PartialEmoji.from_str(raw_sample)
assert from_str.req_format == target
Expand Down
Loading