Skip to content

Commit 72ba1b1

Browse files
NeloBlivionpre-commit-ci[bot]Lulalabyplun1331
authored
feat: app emojis (#2501)
* add routes * unfinished methods, needs rework * style(pre-commit): auto fixes from pre-commit.com hooks * new classes * style(pre-commit): auto fixes from pre-commit.com hooks * refinements * style(pre-commit): auto fixes from pre-commit.com hooks * fix kwargs * _state -> _connection * style(pre-commit): auto fixes from pre-commit.com hooks * cache on ready * full cache * remove delete reason * style(pre-commit): auto fixes from pre-commit.com hooks * adjust slots * style(pre-commit): auto fixes from pre-commit.com hooks * Update discord/emoji.py Signed-off-by: plun1331 <[email protected]> * style(pre-commit): auto fixes from pre-commit.com hooks * update all references to the Emoji class * style(pre-commit): auto fixes from pre-commit.com hooks * misc * cl * style(pre-commit): auto fixes from pre-commit.com hooks * add deprecation --------- Signed-off-by: plun1331 <[email protected]> Signed-off-by: UK <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Lala Sabathil <[email protected]> Co-authored-by: plun1331 <[email protected]>
1 parent 33adf22 commit 72ba1b1

25 files changed

+595
-174
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ These changes are available on the `master` branch, but have not yet been releas
2222
`tags`. ([#2520](https://github.com/Pycord-Development/pycord/pull/2520))
2323
- Added `Member.guild_banner` and `Member.display_banner` properties.
2424
([#2556](https://github.com/Pycord-Development/pycord/pull/2556))
25+
- Added support for Application Emojis.
26+
([#2501](https://github.com/Pycord-Development/pycord/pull/2501))
27+
- Added `cache_app_emojis` parameter to `Client`.
28+
([#2501](https://github.com/Pycord-Development/pycord/pull/2501))
2529
- Added `elapsed` method to `VoiceClient`.
2630
([#2587](https://github.com/Pycord-Development/pycord/pull/2587/))
2731
- Added optional `filter` parameter to `utils.basic_autocomplete()`.
@@ -52,6 +56,8 @@ These changes are available on the `master` branch, but have not yet been releas
5256
([#2496](https://github.com/Pycord-Development/pycord/pull/2496))
5357
- ⚠️ **Removed support for Python 3.8.**
5458
([#2521](https://github.com/Pycord-Development/pycord/pull/2521))
59+
- `Emoji` has been renamed to `GuildEmoji`.
60+
([#2501](https://github.com/Pycord-Development/pycord/pull/2501))
5561
- Replaced audioop (deprecated module) implementation of `PCMVolumeTransformer.read`
5662
method with a pure Python equivalent.
5763
([#2176](https://github.com/Pycord-Development/pycord/pull/2176))
@@ -60,6 +66,8 @@ These changes are available on the `master` branch, but have not yet been releas
6066

6167
- Deprecated `AppInfo.summary` in favor of `AppInfo.description`.
6268
([#2520](https://github.com/Pycord-Development/pycord/pull/2520))
69+
- Deprecated `Emoji` in favor of `GuildEmoji`
70+
([#2501](https://github.com/Pycord-Development/pycord/pull/2501))
6371

6472
## [2.6.1] - 2024-09-15
6573

discord/abc.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,9 @@ async def _edit(
513513
except KeyError:
514514
pass
515515
else:
516-
if isinstance(default_reaction_emoji, _EmojiTag): # Emoji, PartialEmoji
516+
if isinstance(
517+
default_reaction_emoji, _EmojiTag
518+
): # GuildEmoji, PartialEmoji
517519
default_reaction_emoji = default_reaction_emoji._to_partial()
518520
elif isinstance(default_reaction_emoji, int):
519521
default_reaction_emoji = PartialEmoji(
@@ -523,7 +525,7 @@ async def _edit(
523525
default_reaction_emoji = PartialEmoji.from_str(default_reaction_emoji)
524526
else:
525527
raise InvalidArgument(
526-
"default_reaction_emoji must be of type: Emoji | int | str"
528+
"default_reaction_emoji must be of type: GuildEmoji | int | str"
527529
)
528530

529531
options["default_reaction_emoji"] = (
@@ -1792,7 +1794,7 @@ def can_send(self, *objects) -> bool:
17921794
"Message": "send_messages",
17931795
"Embed": "embed_links",
17941796
"File": "attach_files",
1795-
"Emoji": "use_external_emojis",
1797+
"GuildEmoji": "use_external_emojis",
17961798
"GuildSticker": "use_external_stickers",
17971799
}
17981800
# Can't use channel = await self._get_channel() since its async
@@ -1817,7 +1819,7 @@ def can_send(self, *objects) -> bool:
18171819
mapping.get(type(obj).__name__) or mapping[obj.__name__]
18181820
)
18191821

1820-
if type(obj).__name__ == "Emoji":
1822+
if type(obj).__name__ == "GuildEmoji":
18211823
if (
18221824
obj._to_partial().is_unicode_emoji
18231825
or obj.guild_id == channel.guild.id

discord/audit_logs.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
import datetime
4848

4949
from . import abc
50-
from .emoji import Emoji
50+
from .emoji import GuildEmoji
5151
from .guild import Guild
5252
from .member import Member
5353
from .role import Role
@@ -617,7 +617,7 @@ def target(
617617
| User
618618
| Role
619619
| Invite
620-
| Emoji
620+
| GuildEmoji
621621
| StageInstance
622622
| GuildSticker
623623
| Thread
@@ -689,7 +689,7 @@ def _convert_target_invite(self, target_id: int) -> Invite:
689689
pass
690690
return obj
691691

692-
def _convert_target_emoji(self, target_id: int) -> Emoji | Object:
692+
def _convert_target_emoji(self, target_id: int) -> GuildEmoji | Object:
693693
return self._state.get_emoji(target_id) or Object(id=target_id)
694694

695695
def _convert_target_message(self, target_id: int) -> Member | User | None:

discord/channel.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
from . import utils
3434
from .asset import Asset
35-
from .emoji import Emoji
35+
from .emoji import GuildEmoji
3636
from .enums import (
3737
ChannelType,
3838
EmbeddedActivity,
@@ -143,7 +143,7 @@ def __init__(
143143
self.emoji = PartialEmoji.from_str(emoji)
144144
else:
145145
raise TypeError(
146-
"emoji must be a Emoji, PartialEmoji, or str and not"
146+
"emoji must be a GuildEmoji, PartialEmoji, or str and not"
147147
f" {emoji.__class__!r}"
148148
)
149149

@@ -1018,7 +1018,7 @@ class ForumChannel(_TextChannel):
10181018
The initial slowmode delay to set on newly created threads in this channel.
10191019
10201020
.. versionadded:: 2.3
1021-
default_reaction_emoji: Optional[:class:`str` | :class:`discord.Emoji`]
1021+
default_reaction_emoji: Optional[:class:`str` | :class:`discord.GuildEmoji`]
10221022
The default forum reaction emoji.
10231023
10241024
.. versionadded:: 2.5
@@ -1087,7 +1087,7 @@ async def edit(
10871087
default_auto_archive_duration: ThreadArchiveDuration = ...,
10881088
default_thread_slowmode_delay: int = ...,
10891089
default_sort_order: SortOrder = ...,
1090-
default_reaction_emoji: Emoji | int | str | None = ...,
1090+
default_reaction_emoji: GuildEmoji | int | str | None = ...,
10911091
available_tags: list[ForumTag] = ...,
10921092
require_tag: bool = ...,
10931093
overwrites: Mapping[Role | Member | Snowflake, PermissionOverwrite] = ...,
@@ -1138,10 +1138,10 @@ async def edit(self, *, reason=None, **options):
11381138
The default sort order type to use to order posts in this channel.
11391139
11401140
.. versionadded:: 2.3
1141-
default_reaction_emoji: Optional[:class:`discord.Emoji` | :class:`int` | :class:`str`]
1141+
default_reaction_emoji: Optional[:class:`discord.GuildEmoji` | :class:`int` | :class:`str`]
11421142
The default reaction emoji.
11431143
Can be a unicode emoji or a custom emoji in the forms:
1144-
:class:`Emoji`, snowflake ID, string representation (eg. '<a:emoji_name:emoji_id>').
1144+
:class:`GuildEmoji`, snowflake ID, string representation (eg. '<a:emoji_name:emoji_id>').
11451145
11461146
.. versionadded:: 2.5
11471147
available_tags: List[:class:`ForumTag`]

discord/client.py

Lines changed: 144 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
from .application_role_connection import ApplicationRoleConnectionMetadata
4242
from .backoff import ExponentialBackoff
4343
from .channel import PartialMessageable, _threaded_channel_factory
44-
from .emoji import Emoji
44+
from .emoji import AppEmoji, GuildEmoji
4545
from .enums import ChannelType, Status
4646
from .errors import *
4747
from .flags import ApplicationFlags, Intents
@@ -199,6 +199,16 @@ class Client:
199199
To enable these events, this must be set to ``True``. Defaults to ``False``.
200200
201201
.. versionadded:: 2.0
202+
cache_app_emojis: :class:`bool`
203+
Whether to automatically fetch and cache the application's emojis on startup and when fetching. Defaults to ``False``.
204+
205+
.. warning::
206+
207+
There are no events related to application emojis - if any are created/deleted on the
208+
Developer Dashboard while the client is running, the cache will not be updated until you manually
209+
run :func:`fetch_emojis`.
210+
211+
.. versionadded:: 2.7
202212
203213
Attributes
204214
-----------
@@ -330,10 +340,30 @@ def guilds(self) -> list[Guild]:
330340
return self._connection.guilds
331341

332342
@property
333-
def emojis(self) -> list[Emoji]:
334-
"""The emojis that the connected client has."""
343+
def emojis(self) -> list[GuildEmoji | AppEmoji]:
344+
"""The emojis that the connected client has.
345+
346+
.. note::
347+
348+
This only includes the application's emojis if `cache_app_emojis` is ``True``.
349+
"""
335350
return self._connection.emojis
336351

352+
@property
353+
def guild_emojis(self) -> list[GuildEmoji]:
354+
"""The :class:`~discord.GuildEmoji` that the connected client has."""
355+
return [e for e in self.emojis if isinstance(e, GuildEmoji)]
356+
357+
@property
358+
def app_emojis(self) -> list[AppEmoji]:
359+
"""The :class:`~discord.AppEmoji` that the connected client has.
360+
361+
.. note::
362+
363+
This is only available if `cache_app_emojis` is ``True``.
364+
"""
365+
return [e for e in self.emojis if isinstance(e, AppEmoji)]
366+
337367
@property
338368
def stickers(self) -> list[GuildSticker]:
339369
"""The stickers that the connected client has.
@@ -994,7 +1024,7 @@ def get_user(self, id: int, /) -> User | None:
9941024
"""
9951025
return self._connection.get_user(id)
9961026

997-
def get_emoji(self, id: int, /) -> Emoji | None:
1027+
def get_emoji(self, id: int, /) -> GuildEmoji | AppEmoji | None:
9981028
"""Returns an emoji with the given ID.
9991029
10001030
Parameters
@@ -1004,7 +1034,7 @@ def get_emoji(self, id: int, /) -> Emoji | None:
10041034
10051035
Returns
10061036
-------
1007-
Optional[:class:`.Emoji`]
1037+
Optional[:class:`.GuildEmoji` | :class:`.AppEmoji`]
10081038
The custom emoji or ``None`` if not found.
10091039
"""
10101040
return self._connection.get_emoji(id)
@@ -2130,3 +2160,112 @@ def store_url(self) -> str:
21302160
.. versionadded:: 2.6
21312161
"""
21322162
return f"https://discord.com/application-directory/{self.application_id}/store"
2163+
2164+
async def fetch_emojis(self) -> list[AppEmoji]:
2165+
r"""|coro|
2166+
2167+
Retrieves all custom :class:`AppEmoji`\s from the application.
2168+
2169+
Raises
2170+
---------
2171+
HTTPException
2172+
An error occurred fetching the emojis.
2173+
2174+
Returns
2175+
--------
2176+
List[:class:`AppEmoji`]
2177+
The retrieved emojis.
2178+
"""
2179+
data = await self._connection.http.get_all_application_emojis(
2180+
self.application_id
2181+
)
2182+
return [
2183+
self._connection.maybe_store_app_emoji(self.application_id, d)
2184+
for d in data["items"]
2185+
]
2186+
2187+
async def fetch_emoji(self, emoji_id: int, /) -> AppEmoji:
2188+
"""|coro|
2189+
2190+
Retrieves a custom :class:`AppEmoji` from the application.
2191+
2192+
Parameters
2193+
----------
2194+
emoji_id: :class:`int`
2195+
The emoji's ID.
2196+
2197+
Returns
2198+
-------
2199+
:class:`AppEmoji`
2200+
The retrieved emoji.
2201+
2202+
Raises
2203+
------
2204+
NotFound
2205+
The emoji requested could not be found.
2206+
HTTPException
2207+
An error occurred fetching the emoji.
2208+
"""
2209+
data = await self._connection.http.get_application_emoji(
2210+
self.application_id, emoji_id
2211+
)
2212+
return self._connection.maybe_store_app_emoji(self.application_id, data)
2213+
2214+
async def create_emoji(
2215+
self,
2216+
*,
2217+
name: str,
2218+
image: bytes,
2219+
) -> AppEmoji:
2220+
r"""|coro|
2221+
2222+
Creates a custom :class:`AppEmoji` for the application.
2223+
2224+
There is currently a limit of 2000 emojis per application.
2225+
2226+
Parameters
2227+
-----------
2228+
name: :class:`str`
2229+
The emoji name. Must be at least 2 characters.
2230+
image: :class:`bytes`
2231+
The :term:`py:bytes-like object` representing the image data to use.
2232+
Only JPG, PNG and GIF images are supported.
2233+
2234+
Raises
2235+
-------
2236+
HTTPException
2237+
An error occurred creating an emoji.
2238+
2239+
Returns
2240+
--------
2241+
:class:`AppEmoji`
2242+
The created emoji.
2243+
"""
2244+
2245+
img = utils._bytes_to_base64_data(image)
2246+
data = await self._connection.http.create_application_emoji(
2247+
self.application_id, name, img
2248+
)
2249+
return self._connection.maybe_store_app_emoji(self.application_id, data)
2250+
2251+
async def delete_emoji(self, emoji: Snowflake) -> None:
2252+
"""|coro|
2253+
2254+
Deletes the custom :class:`AppEmoji` from the application.
2255+
2256+
Parameters
2257+
----------
2258+
emoji: :class:`abc.Snowflake`
2259+
The emoji you are deleting.
2260+
2261+
Raises
2262+
------
2263+
HTTPException
2264+
An error occurred deleting the emoji.
2265+
"""
2266+
2267+
await self._connection.http.delete_application_emoji(
2268+
self.application_id, emoji.id
2269+
)
2270+
if self._connection.cache_app_emojis and self._connection.get_emoji(emoji.id):
2271+
self._connection.remove_emoji(emoji)

discord/components.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
from .utils import MISSING, get_slots
3333

3434
if TYPE_CHECKING:
35-
from .emoji import Emoji
35+
from .emoji import AppEmoji, GuildEmoji
3636
from .types.components import ActionRow as ActionRowPayload
3737
from .types.components import ButtonComponent as ButtonComponentPayload
3838
from .types.components import Component as ComponentPayload
@@ -412,7 +412,7 @@ def __init__(
412412
label: str,
413413
value: str = MISSING,
414414
description: str | None = None,
415-
emoji: str | Emoji | PartialEmoji | None = None,
415+
emoji: str | GuildEmoji | AppEmoji | PartialEmoji | None = None,
416416
default: bool = False,
417417
) -> None:
418418
if len(label) > 100:
@@ -444,7 +444,7 @@ def __str__(self) -> str:
444444
return base
445445

446446
@property
447-
def emoji(self) -> str | Emoji | PartialEmoji | None:
447+
def emoji(self) -> str | GuildEmoji | AppEmoji | PartialEmoji | None:
448448
"""The emoji of the option, if available."""
449449
return self._emoji
450450

@@ -457,7 +457,7 @@ def emoji(self, value) -> None:
457457
value = value._to_partial()
458458
else:
459459
raise TypeError(
460-
"expected emoji to be str, Emoji, or PartialEmoji not"
460+
"expected emoji to be str, GuildEmoji, AppEmoji, or PartialEmoji, not"
461461
f" {value.__class__}"
462462
)
463463

0 commit comments

Comments
 (0)