diff --git a/discord/enums.py b/discord/enums.py index 3aecfc92b..1312f0320 100644 --- a/discord/enums.py +++ b/discord/enums.py @@ -258,6 +258,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 12a4c90ce..6ce7cf9e9 100644 --- a/discord/message.py +++ b/discord/message.py @@ -77,6 +77,8 @@ if TYPE_CHECKING: RoleSubscriptionData as RoleSubscriptionDataPayload, MessageInteractionMetadata as MessageInteractionMetadataPayload, CallMessage as CallMessagePayload, + PurchaseNotificationResponse as PurchaseNotificationResponsePayload, + GuildProductPurchase as GuildProductPurchasePayload, ) from .types.interactions import MessageInteraction as MessageInteractionPayload @@ -114,6 +116,8 @@ __all__ = ( 'RoleSubscriptionInfo', 'MessageInteractionMetadata', 'CallMessage', + 'GuildProductPurchase', + 'PurchaseNotification', ) @@ -890,6 +894,59 @@ 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) + + class PartialMessage(Hashable): """Represents a partial message to aid with working messages when only a message and channel ID are present. @@ -1820,6 +1877,10 @@ class Message(PartialMessage, Hashable): call: Optional[:class:`CallMessage`] The call associated with this message. + .. versionadded:: 2.5 + purchase_notification: Optional[:class:`PurchaseNotification`] + The data of the purchase notification that prompted this :attr:`MessageType.purchase_notification` message. + .. versionadded:: 2.5 """ @@ -1858,6 +1919,7 @@ class Message(PartialMessage, Hashable): 'interaction_metadata', 'poll', 'call', + 'purchase_notification', ) if TYPE_CHECKING: @@ -1983,6 +2045,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) + for handler in ('author', 'member', 'mentions', 'mention_roles', 'components', 'call'): try: getattr(self, f'_handle_{handler}')(data[handler]) @@ -2496,6 +2566,11 @@ class Message(PartialMessage, Hashable): else: return '{0.author.name} started a call.'.format(self) + 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 995dc8b8b..c1972541a 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] + + class CallMessage(TypedDict): participants: SnowflakeList ended_timestamp: NotRequired[Optional[str]] @@ -156,6 +169,7 @@ MessageType = Literal[ 37, 38, 39, + 44, ] @@ -193,6 +207,7 @@ class Message(PartialMessage): role_subscription_data: NotRequired[RoleSubscriptionData] thread: NotRequired[Thread] call: NotRequired[CallMessage] + purchase_notification: NotRequired[PurchaseNotificationResponse] AllowedMentionType = Literal['roles', 'users', 'everyone'] diff --git a/docs/api.rst b/docs/api.rst index 3531dde06..6f728d56b 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1881,6 +1881,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. @@ -5418,6 +5424,22 @@ RoleSubscriptionInfo .. autoclass:: RoleSubscriptionInfo :members: +PurchaseNotification +~~~~~~~~~~~~~~~~~~~~~ + +.. attributetable:: PurchaseNotification + +.. autoclass:: PurchaseNotification() + :members: + +GuildProductPurchase ++++++++++++++++++++++ + +.. attributetable:: GuildProductPurchase + +.. autoclass:: GuildProductPurchase() + :members: + Intents ~~~~~~~~~~