From 81e1a2970833f8b0ef2bb3f670e6a207a49d9a16 Mon Sep 17 00:00:00 2001 From: Soheab <33902984+Soheab@users.noreply.github.com> Date: Fri, 11 Oct 2024 01:43:07 +0200 Subject: [PATCH] Add support for messages with type purchase_notification --- discord/enums.py | 5 +++ discord/message.py | 82 +++++++++++++++++++++++++++++++++++++++- discord/types/message.py | 15 ++++++++ docs/api.rst | 30 ++++++++++++++- 4 files changed, 129 insertions(+), 3 deletions(-) diff --git a/discord/enums.py b/discord/enums.py index 3a472a050..77a454a42 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -314,6 +314,7 @@ class MessageType(Enum): guild_incident_alert_mode_disabled = 37 guild_incident_report_raid = 38 guild_incident_report_false_alarm = 39 + purchase_notification = 44 class SpeakingState(Enum): @@ -1685,6 +1686,10 @@ class PollLayoutType(Enum): image_only_answers = 2 +class PurchaseNotificationType(Enum): + guild_product = 0 + + class ReactionType(Enum): normal = 0 burst = 1 diff --git a/discord/message.py b/discord/message.py index ece466d4a..6a00ec81d 100644 --- a/discord/message.py +++ b/discord/message.py @@ -51,7 +51,7 @@ from .reaction import Reaction from .emoji import Emoji from .partial_emoji import PartialEmoji from .calls import CallMessage -from .enums import MessageType, ChannelType, ApplicationCommandType, try_enum +from .enums import MessageType, ChannelType, ApplicationCommandType, PurchaseNotificationType, try_enum from .errors import HTTPException from .components import _component_factory from .embeds import Embed @@ -83,6 +83,8 @@ if TYPE_CHECKING: MessageActivity as MessageActivityPayload, RoleSubscriptionData as RoleSubscriptionDataPayload, MessageSearchResult as MessageSearchResultPayload, + PurchaseNotificationResponse as PurchaseNotificationResponsePayload, + GuildProductPurchase as GuildProductPurchasePayload, ) from .types.interactions import MessageInteraction as MessageInteractionPayload @@ -116,6 +118,8 @@ __all__ = ( 'MessageReference', 'DeletedReferencedMessage', 'RoleSubscriptionInfo', + 'GuildProductPurchase', + 'PurchaseNotification', ) @@ -627,6 +631,64 @@ class RoleSubscriptionInfo: self.is_renewal: bool = data['is_renewal'] +class GuildProductPurchase: + """Represents a message's guild product that the user has purchased. + + .. versionadded:: 2.5 + + Attributes + ----------- + listing_id: :class:`int` + The ID of the listing that the user has purchased. + product_name: :class:`str` + The name of the product that the user has purchased. + """ + + __slots__ = ('listing_id', 'product_name') + + def __init__(self, data: GuildProductPurchasePayload) -> None: + self.listing_id: int = int(data['listing_id']) + self.product_name: str = data['product_name'] + + def __hash__(self) -> int: + return self.listing_id >> 22 + + def __eq__(self, other: object) -> bool: + return isinstance(other, GuildProductPurchase) and other.listing_id == self.listing_id + + def __ne__(self, other: object) -> bool: + return not self.__eq__(other) + + +class PurchaseNotification: + """Represents a message's purchase notification data. + + This is currently only attached to messages of type :attr:`MessageType.purchase_notification`. + + .. versionadded:: 2.5 + + Attributes + ----------- + guild_product_purchase: Optional[:class:`GuildProductPurchase`] + The guild product purchase that prompted the message. + """ + + __slots__ = ('_type', 'guild_product_purchase') + + def __init__(self, data: PurchaseNotificationResponsePayload) -> None: + self._type: int = data['type'] + + self.guild_product_purchase: Optional[GuildProductPurchase] = None + guild_product_purchase = data.get('guild_product_purchase') + if guild_product_purchase is not None: + self.guild_product_purchase = GuildProductPurchase(guild_product_purchase) + + @property + def type(self) -> PurchaseNotificationType: + """:class:`PurchaseNotificationType`: The type of purchase notification.""" + return try_enum(PurchaseNotificationType, self._type) + + class PartialMessage(Hashable): """Represents a partial message to aid with working messages when only a message and channel ID are present. @@ -1595,6 +1657,10 @@ class Message(PartialMessage, Hashable): The poll attached to this message. .. versionadded:: 2.4 + purchase_notification: Optional[:class:`PurchaseNotification`] + The data of the purchase notification that prompted this :attr:`MessageType.purchase_notification` message. + + .. versionadded:: 2.5 hit: :class:`bool` Whether the message was a hit in a search result. As surrounding messages are no longer returned in search results, this is always ``True`` for search results. @@ -1649,6 +1715,7 @@ class Message(PartialMessage, Hashable): 'application_id', 'position', 'poll', + 'purchase_notification', 'hit', 'total_results', 'analytics_id', @@ -1769,6 +1836,14 @@ class Message(PartialMessage, Hashable): else: self.role_subscription = RoleSubscriptionInfo(role_subscription) + self.purchase_notification: Optional[PurchaseNotification] = None + try: + purchase_notification = data['purchase_notification'] + except KeyError: + pass + else: + self.purchase_notification = PurchaseNotification(purchase_notification) + search_payload = search_result or {} self.hit: bool = data.get('hit', False) self.total_results: Optional[int] = search_payload.get('total_results') @@ -2295,6 +2370,11 @@ class Message(PartialMessage, Hashable): if self.type is MessageType.guild_incident_report_false_alarm: return f'{self.author.name} reported a false alarm in {self.guild}.' + if self.type is MessageType.purchase_notification and self.purchase_notification is not None: + guild_product_purchase = self.purchase_notification.guild_product_purchase + if guild_product_purchase is not None: + return f'{self.author.name} has purchased {guild_product_purchase.product_name}!' + # Fallback for unknown message types return self.content diff --git a/discord/types/message.py b/discord/types/message.py index 6b9a9e8cc..d0c7038a7 100644 --- a/discord/types/message.py +++ b/discord/types/message.py @@ -114,6 +114,19 @@ class RoleSubscriptionData(TypedDict): is_renewal: bool +PurchaseNotificationResponseType = Literal[0] + + +class GuildProductPurchase(TypedDict): + listing_id: Snowflake + product_name: str + + +class PurchaseNotificationResponse(TypedDict): + type: PurchaseNotificationResponseType + guild_product_purchase: Optional[GuildProductPurchase] + + MessageType = Literal[ 0, 1, @@ -149,6 +162,7 @@ MessageType = Literal[ 37, 38, 39, + 44, ] @@ -186,6 +200,7 @@ class Message(PartialMessage): role_subscription_data: NotRequired[RoleSubscriptionData] hit: NotRequired[bool] thread: NotRequired[Thread] + purchase_notification: NotRequired[PurchaseNotificationResponse] AllowedMentionType = Literal['roles', 'users', 'everyone'] diff --git a/docs/api.rst b/docs/api.rst index 60f6aab9f..7617aedff 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -2017,6 +2017,12 @@ of :class:`enum.Enum`. .. versionadded:: 2.4 + .. attribute:: purchase_notification + + The system message sent when a purchase is made in the guild. + + .. versionadded:: 2.5 + .. class:: UserFlags Represents Discord User flags. @@ -6052,7 +6058,7 @@ of :class:`enum.Enum`. Represents how a poll answers are shown - .. versionadded:: 2.4 + .. versionadded:: 2.1 .. attribute:: default @@ -6062,7 +6068,7 @@ of :class:`enum.Enum`. Represents the type of a reaction. - .. versionadded:: 2.4 + .. versionadded:: 2.1 .. attribute:: normal @@ -6072,6 +6078,16 @@ of :class:`enum.Enum`. A burst reaction, also known as a "super reaction". +.. class:: PurchaseNotificationType + + Represents the type of a purchase notification. + + .. versionadded:: 2.1 + + .. attribute:: guild_product + + A guild product was purchased. + .. _discord-api-audit-logs: @@ -7846,6 +7862,16 @@ Message .. autoclass:: RoleSubscriptionInfo() :members: +.. attributetable:: PurchaseNotification + +.. autoclass:: PurchaseNotification() + :members: + +.. attributetable:: GuildProductPurchase + +.. autoclass:: GuildProductPurchase() + :members: + Reaction ~~~~~~~~