Skip to content

Commit

Permalink
edit Emoji to represent app emojis
Browse files Browse the repository at this point in the history
  • Loading branch information
Snipy7374 committed Aug 21, 2024
1 parent 18a127b commit 710c40f
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 152 deletions.
2 changes: 1 addition & 1 deletion changelog/1223.feature.rst
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
Add new :class:`.ApplicationEmoji` representing application emojis.
Edit :class:`.Emoji` to represent application emojis.
Add new methods and properties on :class:`Client` to get, fetch and create application emojis: :meth:`Client.get_application_emoji`, :meth:`Client.fetch_application_emoji`, :meth:`Client.get_or_fetch_application_emoji`, :meth:`Client.getch_application_emoji`, :meth:`Client.fetch_application_emojis`, :attr:`Client.application_emojis` and :meth:`Client.create_application_emoji`.
46 changes: 22 additions & 24 deletions disnake/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
from .application_role_connection import ApplicationRoleConnectionMetadata
from .backoff import ExponentialBackoff
from .channel import PartialMessageable, _threaded_channel_factory
from .emoji import ApplicationEmoji, Emoji
from .emoji import Emoji
from .entitlement import Entitlement
from .enums import ApplicationCommandType, ChannelType, Event, Status
from .errors import (
Expand Down Expand Up @@ -568,8 +568,11 @@ def emojis(self) -> List[Emoji]:
return self._connection.emojis

@property
def application_emojis(self) -> List[ApplicationEmoji]:
"""List[:class:`.ApplicationEmoji`]: The application emojis that the connected client has."""
def application_emojis(self) -> List[Emoji]:
"""List[:class:`.ApplicationEmoji`]: The application emojis that the connected client has.
.. versionadded:: 2.10
"""
return self._connection.application_emojis

@property
Expand Down Expand Up @@ -723,18 +726,18 @@ async def get_or_fetch_user(self, user_id: int, *, strict: bool = False) -> Opti
@overload
async def get_or_fetch_application_emoji(
self, emoji_id: int, *, strict: Literal[False] = ...
) -> Optional[ApplicationEmoji]:
) -> Optional[Emoji]:
...

@overload
async def get_or_fetch_application_emoji(
self, emoji_id: int, *, strict: Literal[True] = ...
) -> ApplicationEmoji:
) -> Emoji:
...

async def get_or_fetch_application_emoji(
self, emoji_id: int, *, strict: bool = False
) -> Optional[ApplicationEmoji]:
) -> Optional[Emoji]:
"""|coro|
Tries to get the application emoji from the cache. If it fails,
Expand All @@ -756,7 +759,7 @@ async def get_or_fetch_application_emoji(
Returns
-------
Optional[:class:`~disnake.ApplicationEmoji`]
Optional[:class:`~disnake.Emoji`]
The app emoji with the given ID, or ``None`` if not found and ``strict`` is set to ``False``.
"""
app_emoji = self.get_application_emoji(emoji_id)
Expand Down Expand Up @@ -1550,7 +1553,7 @@ def get_emoji(self, id: int, /) -> Optional[Emoji]:
"""
return self._connection.get_emoji(id)

def get_application_emoji(self, emoji_id: int, /) -> Optional[ApplicationEmoji]:
def get_application_emoji(self, emoji_id: int, /) -> Optional[Emoji]:
"""Returns an application emoji with the given ID.
.. versionadded:: 2.10
Expand All @@ -1567,7 +1570,7 @@ def get_application_emoji(self, emoji_id: int, /) -> Optional[ApplicationEmoji]:
Returns
-------
Optional[:class:`ApplicationEmoji`]
Optional[:class:`Emoji`]
The returned application emoji or ``None`` if not found.
"""
return self._connection.get_application_emoji(emoji_id)
Expand Down Expand Up @@ -2455,12 +2458,10 @@ async def application_info(self) -> AppInfo:
data["rpc_origins"] = None
return AppInfo(self._connection, data)

async def fetch_application_emoji(
self, emoji_id: int, /, cache: bool = False
) -> ApplicationEmoji:
async def fetch_application_emoji(self, emoji_id: int, /, cache: bool = False) -> Emoji:
"""|coro|
Retrieves an application level :class:`~disnake.ApplicationEmoji` based on its ID.
Retrieves an application level :class:`~disnake.Emoji` based on its ID.
.. note::
Expand All @@ -2484,16 +2485,16 @@ async def fetch_application_emoji(
Returns
-------
:class:`ApplicationEmoji`
:class:`Emoji`
The application emoji you requested.
"""
data = await self.http.get_app_emoji(self.application_id, emoji_id)

if cache:
return self._connection.store_application_emoji(data=data)
return ApplicationEmoji(app_id=self.application_id, state=self._connection, data=data)
return Emoji(guild=None, state=self._connection, data=data)

async def create_application_emoji(self, *, name: str, image: AssetBytes) -> ApplicationEmoji:
async def create_application_emoji(self, *, name: str, image: AssetBytes) -> Emoji:
"""|coro|
Creates an application emoji.
Expand Down Expand Up @@ -2523,17 +2524,17 @@ async def create_application_emoji(self, *, name: str, image: AssetBytes) -> App
Returns
-------
:class:`ApplicationEmoji`
:class:`Emoji`
The newly created application emoji.
"""
img = await utils._assetbytes_to_base64_data(image)
data = await self.http.create_app_emoji(self.application_id, name, img)
return self._connection.store_application_emoji(data)

async def fetch_application_emojis(self, *, cache: bool = False) -> List[ApplicationEmoji]:
async def fetch_application_emojis(self, *, cache: bool = False) -> List[Emoji]:
"""|coro|
Retrieves all the :class:`ApplicationEmoji` of the application.
Retrieves all the :class:`Emoji` of the application.
.. versionadded:: 2.10
Expand All @@ -2552,7 +2553,7 @@ async def fetch_application_emojis(self, *, cache: bool = False) -> List[Applica
Returns
-------
List[:class:`ApplicationEmoji`]
List[:class:`Emoji`]
The list of application emojis you requested.
"""
data = await self.http.get_all_app_emojis(self.application_id)
Expand All @@ -2564,10 +2565,7 @@ async def fetch_application_emojis(self, *, cache: bool = False) -> List[Applica

return app_emojis

return [
ApplicationEmoji(app_id=self.application_id, state=self._connection, data=emoji_data)
for emoji_data in data
]
return [Emoji(guild=None, state=self._connection, data=emoji_data) for emoji_data in data]

async def fetch_user(self, user_id: int, /) -> User:
"""|coro|
Expand Down
168 changes: 56 additions & 112 deletions disnake/emoji.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from .user import User
from .utils import MISSING, SnowflakeList, snowflake_time

__all__ = ("Emoji", "ApplicationEmoji", "AppEmoji")
__all__ = ("Emoji",)

if TYPE_CHECKING:
from datetime import datetime
Expand Down Expand Up @@ -51,6 +51,10 @@ class Emoji(_EmojiTag, AssetMixin):
Returns the emoji rendered for Discord.
.. versionchanched:: 2.10
This class can now represents app emojis too. Denoted by having :attr:`.Emoji.guild_id` as ``None``.
Attributes
----------
name: :class:`str`
Expand All @@ -63,8 +67,8 @@ class Emoji(_EmojiTag, AssetMixin):
Whether the emoji is animated or not.
managed: :class:`bool`
Whether the emoji is managed by a Twitch integration.
guild_id: :class:`int`
The guild ID the emoji belongs to.
guild_id: Optional[:class:`int`]
The guild ID the emoji belongs to. ``None`` if this is an app emoji.
available: :class:`bool`
Whether the emoji is available for use.
user: Optional[:class:`User`]
Expand All @@ -81,14 +85,20 @@ class Emoji(_EmojiTag, AssetMixin):
"name",
"_roles",
"guild_id",
"_application_id",
"user",
"available",
)

def __init__(
self, *, guild: Union[Guild, GuildPreview], state: ConnectionState, data: EmojiPayload
self,
*,
guild: Optional[Union[Guild, GuildPreview]],
state: ConnectionState,
data: EmojiPayload,
) -> None:
self.guild_id: int = guild.id
self.guild_id: Optional[int] = guild.id if guild else None
self._application_id: Optional[int] = state.application_id
self._state: ConnectionState = state
self._from_data(data)

Expand Down Expand Up @@ -151,16 +161,34 @@ def roles(self) -> List[Role]:
and count towards a separate limit of 25 emojis.
"""
guild = self.guild
if guild is None: # pyright: ignore[reportUnnecessaryComparison]
if guild is None:
return []

return [role for role in guild.roles if self._roles.has(role.id)]

@property
def guild(self) -> Guild:
""":class:`Guild`: The guild this emoji belongs to."""
def guild(self) -> Optional[Guild]:
"""Optional[:class:`Guild`]: The guild this emoji belongs to.
``None`` if this is an app emoji.
.. versionchanged:: 2.10
This can now return ``None`` if the emoji is an
application owned emoji.
"""
# this will most likely never return None but there's a possibility
return self._state._get_guild(self.guild_id) # type: ignore
return self._state._get_guild(self.guild_id)

@property
def application_id(self) -> Optional[int]:
""":class:`int`: The ID of the application which owns this emoji.
.. versionadded:: 2.10
"""
if self.guild is None:
return
return self._application_id

def is_usable(self) -> bool:
"""Whether the bot can use this emoji.
Expand All @@ -173,6 +201,8 @@ def is_usable(self) -> bool:
return False
if not self._roles:
return True
if not self.guild:
return self.available
emoji_roles, my_roles = self._roles, self.guild.me._roles
return any(my_roles.has(role_id) for role_id in emoji_roles)

Expand All @@ -196,6 +226,13 @@ async def delete(self, *, reason: Optional[str] = None) -> None:
HTTPException
An error occurred deleting the emoji.
"""
# this is an app emoji
if self.guild is None:
if self.application_id is None:
# should never happen
raise ValueError("Idk message about invalid state?! Pls catch this when reviewing")

return await self._state.http.delete_app_emoji(self.application_id, self.id)
await self._state.http.delete_custom_emoji(self.guild.id, self.id, reason=reason)

async def edit(
Expand Down Expand Up @@ -242,107 +279,14 @@ async def edit(
if roles is not MISSING:
payload["roles"] = [role.id for role in roles]

data = await self._state.http.edit_custom_emoji(
self.guild.id, self.id, payload=payload, reason=reason
)
return Emoji(guild=self.guild, data=data, state=self._state)


class ApplicationEmoji(Emoji):
"""Represents an application custom emoji.
.. versionadded:: 2.10
.. collapse:: operations
.. describe:: x == y
Checks if two emoji are the same.
.. describe:: x != y
Checks if two emoji are not the same.
.. describe:: hash(x)
Return the emoji's hash.
.. describe:: iter(x)
Returns an iterator of ``(field, value)`` pairs. This allows this class
to be used as an iterable in list/dict/etc constructions.
.. describe:: str(x)
Returns the emoji rendered for Discord.
Attributes
----------
name: :class:`str`
The emoji's name.
id: :class:`int`
The emoji's ID.
application_id: :class:`int`
The apllication ID this emoji belongs to.
require_colons: :class:`bool`
Whether colons are required to use this emoji in the client (:PJSalt: vs PJSalt).
animated: :class:`bool`
Whether the emoji is animated or not.
managed: :class:`bool`
Whether the emoji is managed by a Twitch integration.
available: :class:`bool`
Whether the emoji is available for use.
user: :class:`User`
The user that created this emoji.
"""

def __init__(self, *, app_id: int, state: ConnectionState, data: EmojiPayload) -> None:
self.guild_id: Optional[int] = None
self.application_id: int = app_id
self._state: ConnectionState = state
self._from_data(data)

def __repr__(self) -> str:
return f"<ApplicationEmoji id={self.id} application_id={self.application_id} name={self.name} animated={self.animated} managed={self.managed}>"

async def edit(self, *, name: str) -> ApplicationEmoji:
"""|coro|
Edits the application custom emoji.
Parameters
----------
name: :class:`str`
The new emoji name.
Raises
------
Forbidden
You are not allowed to edit this emoji.
HTTPException
An error occurred editing the emoji.
Returns
-------
:class:`ApplicationEmoji`
The newly updated emoji.
"""
data = await self._state.http.edit_app_emoji(self.application_id, self.id, name)
return ApplicationEmoji(app_id=self.application_id, state=self._state, data=data)

async def delete(self) -> None:
"""|coro|
if self.guild is None:
if self.application_id is None:
# should never happen
raise ValueError("Idk message about invalid state?! Pls catch this when reviewing")

Deletes the application custom emoji.
Raises
------
Forbidden
You are not allowed to delete this emoji.
HTTPException
An error occurred deleting the emoji.
"""
await self._state.http.delete_app_emoji(self.application_id, self.id)


AppEmoji = ApplicationEmoji
data = await self._state.http.edit_app_emoji(self.application_id, self.id, name)
else:
data = await self._state.http.edit_custom_emoji(
self.guild.id, self.id, payload=payload, reason=reason
)
return Emoji(guild=self.guild, data=data, state=self._state)
Loading

0 comments on commit 710c40f

Please sign in to comment.