diff --git a/discord/partial_emoji.py b/discord/partial_emoji.py index c9ddd6504..fc40ff55e 100644 --- a/discord/partial_emoji.py +++ b/discord/partial_emoji.py @@ -25,6 +25,7 @@ DEALINGS IN THE SOFTWARE. from __future__ import annotations from typing import Any, Dict, Optional, TYPE_CHECKING, Type, TypeVar +import re from .asset import Asset, AssetMixin from .errors import InvalidArgument @@ -88,6 +89,8 @@ class PartialEmoji(_EmojiTag, AssetMixin): __slots__ = ('animated', 'name', 'id', '_state') + _CUSTOM_EMOJI_RE = re.compile(r'a)?:?(?P[A-Za-z0-9\_]+):(?P[0-9]{13,20})>?') + if TYPE_CHECKING: id: Optional[int] @@ -105,6 +108,41 @@ class PartialEmoji(_EmojiTag, AssetMixin): name=data.get('name', ''), ) + @classmethod + def from_str(cls: Type[PE], value: str) -> PE: + """Converts a Discord string representation of an emoji to a :class:`PartialEmoji`. + + The formats accepted are: + + - ``a:name:id`` + - ```` + - ``name:id`` + - ``<:name:id>`` + + If the format does not match then it is assumed to be a unicode emoji. + + .. versionadded:: 2.0 + + Parameters + ------------ + value: :class:`str` + The string representation of an emoji. + + Returns + -------- + :class:`PartialEmoji` + The partial emoji from this string. + """ + match = cls._CUSTOM_EMOJI_RE.match(value) + if match is not None: + groups = match.groupdict() + animated = bool(groups['animated']) + emoji_id = int(groups['id']) + name = groups['name'] + return cls(name=name, animated=animated, id=emoji_id) + + return cls(name=value, id=None, animated=False) + def to_dict(self) -> Dict[str, Any]: o: Dict[str, Any] = {'name': self.name} if self.id: diff --git a/discord/ui/button.py b/discord/ui/button.py index 729565624..ca32098d9 100644 --- a/discord/ui/button.py +++ b/discord/ui/button.py @@ -26,7 +26,6 @@ from __future__ import annotations from typing import Callable, Optional, TYPE_CHECKING, Tuple, Type, TypeVar, Union import inspect -import re import os @@ -43,25 +42,6 @@ __all__ = ( if TYPE_CHECKING: from .view import View -_custom_emoji = re.compile(r'a)?:?(?P[A-Za-z0-9\_]+):(?P[0-9]{13,20})>?') - - -def _to_partial_emoji(obj: Union[str, PartialEmoji], *, _custom_emoji=_custom_emoji) -> PartialEmoji: - if isinstance(obj, PartialEmoji): - return obj - - obj = str(obj) - match = _custom_emoji.match(obj) - if match is not None: - groups = match.groupdict() - animated = bool(groups['animated']) - emoji_id = int(groups['id']) - name = groups['name'] - return PartialEmoji(name=name, animated=animated, id=emoji_id) - - return PartialEmoji(name=obj, id=None, animated=False) - - B = TypeVar('B', bound='Button') V = TypeVar('V', bound='View', covariant=True) @@ -118,6 +98,9 @@ class Button(Item[V]): if url is not None: style = ButtonStyle.link + if isinstance(emoji, str): + emoji = PartialEmoji.from_str(emoji) + self._underlying = ButtonComponent._raw_construct( type=ComponentType.button, custom_id=custom_id, @@ -125,7 +108,7 @@ class Button(Item[V]): disabled=disabled, label=label, style=style, - emoji=None if emoji is None else _to_partial_emoji(emoji), + emoji=emoji, ) self.group_id = group @@ -190,7 +173,10 @@ class Button(Item[V]): @emoji.setter def emoji(self, value: Optional[Union[str, PartialEmoji]]): # type: ignore if value is not None: - self._underlying.emoji = _to_partial_emoji(value) + if isinstance(value, str): + self._underlying.emoji = PartialEmoji.from_str(value) + else: + self._underlying.emoji = value else: self._underlying.emoji = None