diff --git a/discord/raw_models.py b/discord/raw_models.py index 6952cbe14..ba8b257c8 100644 --- a/discord/raw_models.py +++ b/discord/raw_models.py @@ -27,6 +27,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Literal, Optional, Set, List, Tuple, Union from .enums import ChannelType, ReadStateType, try_enum +from .colour import Colour from .utils import _get_as_snowflake if TYPE_CHECKING: @@ -204,9 +205,29 @@ class RawReactionActionEvent(_RawReprMixin): ``REACTION_REMOVE`` for reaction removal. .. versionadded:: 1.3 + burst: :class:`bool` + Whether the reaction was a burst reaction, also known as a "super reaction". + + .. versionadded:: 2.4 + burst_colours: List[:class:`Colour`] + A list of colours used for burst reaction animation. Only available if ``burst`` is ``True`` + and if ``event_type`` is ``REACTION_ADD``. + + .. versionadded:: 2.0 """ - __slots__ = ('message_id', 'user_id', 'channel_id', 'guild_id', 'emoji', 'event_type', 'member', 'message_author_id') + __slots__ = ( + 'message_id', + 'user_id', + 'channel_id', + 'guild_id', + 'emoji', + 'event_type', + 'member', + 'message_author_id', + 'burst', + 'burst_colours', + ) def __init__(self, data: ReactionActionEvent, emoji: PartialEmoji, event_type: ReactionActionType) -> None: self.message_id: int = int(data['message_id']) @@ -216,12 +237,22 @@ class RawReactionActionEvent(_RawReprMixin): self.event_type: ReactionActionType = event_type self.member: Optional[Member] = None self.message_author_id: Optional[int] = _get_as_snowflake(data, 'message_author_id') + self.burst: bool = data.get('burst', False) + self.burst_colours: List[Colour] = [Colour.from_str(c) for c in data.get('burst_colours', [])] try: self.guild_id: Optional[int] = int(data['guild_id']) except KeyError: self.guild_id: Optional[int] = None + @property + def burst_colors(self) -> List[Colour]: + """An alias of :attr:`burst_colours`. + + .. versionadded:: 2.4 + """ + return self.burst_colours + class RawReactionClearEvent(_RawReprMixin): """Represents the payload for a :func:`on_raw_reaction_clear` event. diff --git a/discord/reaction.py b/discord/reaction.py index e93f323e9..f72bf216c 100644 --- a/discord/reaction.py +++ b/discord/reaction.py @@ -74,20 +74,40 @@ class Reaction: emoji: Union[:class:`Emoji`, :class:`PartialEmoji`, :class:`str`] The reaction emoji. May be a custom emoji, or a unicode emoji. count: :class:`int` - Number of times this reaction was made + Number of times this reaction was made. This is a sum of :attr:`normal_count` and :attr:`burst_count`. me: :class:`bool` If the user sent this reaction. message: :class:`Message` Message this reaction is for. + me_burst: :class:`bool` + If the user sent this super reaction. + + .. versionadded:: 2.4 + normal_count: :class:`int` + The number of times this reaction was made using normal reactions. + This is not available in the gateway events such as :func:`on_reaction_add` + or :func:`on_reaction_remove`. + + .. versionadded:: 2.4 + burst_count: :class:`int` + The number of times this reaction was made using super reactions. + This is not available in the gateway events such as :func:`on_reaction_add` + or :func:`on_reaction_remove`. + + .. versionadded:: 2.4 """ - __slots__ = ('message', 'count', 'emoji', 'me') + __slots__ = ('message', 'count', 'emoji', 'me', 'me_burst', 'normal_count', 'burst_count') def __init__(self, *, message: Message, data: ReactionPayload, emoji: Optional[Union[PartialEmoji, Emoji, str]] = None): self.message: Message = message self.emoji: Union[PartialEmoji, Emoji, str] = emoji or message._state.get_reaction_emoji(data['emoji']) self.count: int = data.get('count', 1) self.me: bool = data['me'] + details = data.get('count_details', {}) + self.normal_count: int = details.get('normal', 0) + self.burst_count: int = details.get('burst', 0) + self.me_burst: bool = data.get('me_burst', False) # TODO: typeguard def is_custom_emoji(self) -> bool: diff --git a/discord/types/gateway.py b/discord/types/gateway.py index fb200c20b..64c796fd8 100644 --- a/discord/types/gateway.py +++ b/discord/types/gateway.py @@ -190,6 +190,8 @@ class MessageReactionAddEvent(TypedDict): member: NotRequired[MemberWithUser] guild_id: NotRequired[Snowflake] message_author_id: NotRequired[Snowflake] + burst: bool + burst_colors: NotRequired[List[str]] class MessageReactionRemoveEvent(TypedDict): @@ -198,6 +200,7 @@ class MessageReactionRemoveEvent(TypedDict): message_id: Snowflake emoji: PartialEmoji guild_id: NotRequired[Snowflake] + burst: bool class MessageReactionRemoveAllEvent(TypedDict): diff --git a/discord/types/message.py b/discord/types/message.py index 7de75da80..f9432a04c 100644 --- a/discord/types/message.py +++ b/discord/types/message.py @@ -52,10 +52,18 @@ class ChannelMention(TypedDict): name: str +class ReactionCountDetails(TypedDict): + burst: int + normal: int + + class Reaction(TypedDict): count: int me: bool emoji: PartialEmoji + me_burst: bool + count_details: ReactionCountDetails + burst_colors: List[str] class Attachment(TypedDict): diff --git a/docs/api.rst b/docs/api.rst index 8ad9f14fa..5c2dfe15e 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1276,6 +1276,12 @@ Reactions To get the :class:`Message` being reacted, access it via :attr:`Reaction.message`. + .. warning:: + + This event does not have a way of differentiating whether a reaction is a + burst reaction (also known as "super reaction") or not. If you need this, + consider using :func:`on_raw_reaction_add` instead. + :param reaction: The current state of the reaction. :type reaction: :class:`Reaction` :param user: The user who added the reaction. @@ -1295,6 +1301,12 @@ Reactions Consider using :func:`on_raw_reaction_remove` if you need this and do not have a complete member cache. + .. warning:: + + This event does not have a way of differentiating whether a reaction is a + burst reaction (also known as "super reaction") or not. If you need this, + consider using :func:`on_raw_reaction_remove` instead. + :param reaction: The current state of the reaction. :type reaction: :class:`Reaction` :param user: The user whose reaction was removed.