diff --git a/discord/enums.py b/discord/enums.py index eaf8aef5e058..f94e4428f41c 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -256,6 +256,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): diff --git a/discord/message.py b/discord/message.py index 0247ee8c11c8..1a69d4d429b3 100644 --- a/discord/message.py +++ b/discord/message.py @@ -76,6 +76,8 @@ MessageActivity as MessageActivityPayload, RoleSubscriptionData as RoleSubscriptionDataPayload, MessageInteractionMetadata as MessageInteractionMetadataPayload, + PurchaseNotificationResponse as PurchaseNotificationResponsePayload, + GuildProductPurchase as GuildProductPurchasePayload, ) from .types.interactions import MessageInteraction as MessageInteractionPayload @@ -112,6 +114,8 @@ 'MessageApplication', 'RoleSubscriptionInfo', 'MessageInteractionMetadata', + 'GuildProductPurchase', + 'PurchaseNotification', ) @@ -843,6 +847,51 @@ def __init__(self, data: RoleSubscriptionDataPayload) -> None: 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. + """ + + 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 + + +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) + + class PartialMessage(Hashable): """Represents a partial message to aid with working messages when only a message and channel ID are present. @@ -1768,6 +1817,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 """ __slots__ = ( @@ -1804,6 +1857,7 @@ class Message(PartialMessage, Hashable): 'position', 'interaction_metadata', 'poll', + 'purchase_notification', ) if TYPE_CHECKING: @@ -1931,6 +1985,14 @@ def __init__( 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) + for handler in ('author', 'member', 'mentions', 'mention_roles', 'components'): try: getattr(self, f'_handle_{handler}')(data[handler]) @@ -2421,6 +2483,11 @@ def system_content(self) -> str: 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 '' diff --git a/discord/types/message.py b/discord/types/message.py index bdb3f10ef9e6..a246e40ba9e7 100644 --- a/discord/types/message.py +++ b/discord/types/message.py @@ -116,6 +116,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, @@ -151,6 +164,7 @@ class RoleSubscriptionData(TypedDict): 37, 38, 39, + 44, ] @@ -187,6 +201,7 @@ class Message(PartialMessage): position: NotRequired[int] role_subscription_data: NotRequired[RoleSubscriptionData] thread: NotRequired[Thread] + purchase_notification: NotRequired[PurchaseNotificationResponse] AllowedMentionType = Literal['roles', 'users', 'everyone'] diff --git a/docs/api.rst b/docs/api.rst index 41cf6549d169..3dc5672a9fb6 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1810,6 +1810,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. @@ -5198,6 +5204,22 @@ RoleSubscriptionInfo .. autoclass:: RoleSubscriptionInfo :members: +PurchaseNotification +~~~~~~~~~~~~~~~~~~~~~ + +.. attributetable:: PurchaseNotification + +.. autoclass:: PurchaseNotification() + :members: + +GuildProductPurchase ++++++++++++++++++++++ + +.. attributetable:: GuildProductPurchase + +.. autoclass:: GuildProductPurchase() + :members: + Intents ~~~~~~~~~~