diff --git a/changelog/1225.feature.rst b/changelog/1225.feature.rst new file mode 100644 index 0000000000..d40b80c00a --- /dev/null +++ b/changelog/1225.feature.rst @@ -0,0 +1,4 @@ +Implement new :attr:`Message.type`: :attr:`MessageType.purchase_notifcation`. +- New types :class:`PurchaseNotificationInfo`, :class:`GuildProductInfo`. +- New :attr:`Message.purchase_information` attribute. +- New enum :class:`PurchaseType`. diff --git a/disnake/enums.py b/disnake/enums.py index 56b06ca5a2..949ad22c28 100644 --- a/disnake/enums.py +++ b/disnake/enums.py @@ -71,6 +71,7 @@ "OnboardingPromptType", "SKUType", "EntitlementType", + "PurchaseType", ) @@ -260,6 +261,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 PartyType(Enum): @@ -1364,6 +1366,10 @@ class EntitlementType(Enum): application_subscription = 8 +class PurchaseType(Enum): + guild_product = 0 + + T = TypeVar("T") diff --git a/disnake/message.py b/disnake/message.py index 7957799de6..e39faf02ef 100644 --- a/disnake/message.py +++ b/disnake/message.py @@ -26,7 +26,14 @@ from .components import ActionRow, MessageComponent, _component_factory from .embeds import Embed from .emoji import Emoji -from .enums import ChannelType, InteractionType, MessageType, try_enum, try_enum_to_int +from .enums import ( + ChannelType, + InteractionType, + MessageType, + PurchaseType, + try_enum, + try_enum_to_int, +) from .errors import HTTPException from .file import File from .flags import AttachmentFlags, MessageFlags @@ -64,10 +71,12 @@ from .types.member import Member as MemberPayload, UserWithMember as UserWithMemberPayload from .types.message import ( Attachment as AttachmentPayload, + GuildProductPurchase as GuildProductPayload, Message as MessagePayload, MessageActivity as MessageActivityPayload, MessageApplication as MessageApplicationPayload, MessageReference as MessageReferencePayload, + PurchaseNotification as PurchaseNotificationPayload, Reaction as ReactionPayload, RoleSubscriptionData as RoleSubscriptionDataPayload, ) @@ -86,6 +95,8 @@ "InteractionReference", "DeletedReferencedMessage", "RoleSubscriptionData", + "GuildProductInfo", + "PurchaseNotificationInfo", ) @@ -748,6 +759,42 @@ def __init__(self, data: RoleSubscriptionDataPayload) -> None: self.is_renewal: bool = data["is_renewal"] +class GuildProductInfo: + """Represents the information about the guild product purchased by a user in a message + of type :attr:`MessageType.purchase_notification`. + + .. versionadded:: 2.10 + + Attributes + ---------- + listing_id: :class:`int` + The ID of the listing the user purchased. + product_name: :class:`str` + The name of the product the user purchased. + """ + + __slots__ = ("listing_id", "product_name") + + def __init__(self, data: GuildProductPayload) -> None: + self.listing_id: int = int(data["listing_id"]) + self.product_name: str = data["product_name"] + + +class PurchaseNotificationInfo: + """Represents the information about a purchase made by a user in a message of type + :attr:`MessageType.purchase_notification`. + + .. versionadded:: 2.10 + """ + + __slots__ = ("type", "guild_product") + + def __init__(self, data: PurchaseNotificationPayload) -> None: + self.type: PurchaseType = try_enum(PurchaseType, data["type"]) + guild_product_info = data.get("guild_product_purchase") + self.guild_product: Optional[GuildProductInfo] = (GuildProductInfo(guild_product_info) if guild_product_info is not None else None) + + def flatten_handlers(cls): prefix = len("_handle_") handlers = [ @@ -894,6 +941,11 @@ class Message(Hashable): .. versionadded:: 2.0 + purchase_notification: Optional[:class:`PurchaseNotification`] + The purchase a user made if this message is of type :attr:`MessageType.purchase_notification`. + + .. versionadded:: 2.10 + guild: Optional[:class:`Guild`] The guild that the message belongs to, if applicable. """ @@ -930,6 +982,7 @@ class Message(Hashable): "activity", "stickers", "components", + "purchase_notification", "guild", "_edited_timestamp", "_role_subscription_data", @@ -986,6 +1039,9 @@ def __init__( for d in data.get("components", []) ] + purchase_notif = data.get("purchase_notification") + self.purchase_notification: Optional[PurchaseNotificationInfo] = PurchaseNotificationInfo(purchase_notif) if purchase_notif is not None else None + inter_payload = data.get("interaction") inter = ( None if inter_payload is None else InteractionReference(state=state, data=inter_payload) @@ -1538,6 +1594,12 @@ def system_content(self) -> Optional[str]: if self.type is MessageType.guild_incident_report_false_alarm: return f"{self.author.name} resolved an Activity Alert." + if self.type is MessageType.purchase_notification and self.purchase_notification is not None: + if self.purchase_notification.guild_product is not None: + return f"{self.author.name} has purchased {self.purchase_notification.guild_product.product_name}!" + + # TODO: maybe more purcahse notification types will be added? + # in the event of an unknown or unsupported message type, we return nothing return None diff --git a/disnake/types/message.py b/disnake/types/message.py index 29fdfda374..23aac16769 100644 --- a/disnake/types/message.py +++ b/disnake/types/message.py @@ -77,8 +77,21 @@ class RoleSubscriptionData(TypedDict): is_renewal: bool +class GuildProductPurchase(TypedDict): + listing_id: Snowflake + product_name: str + + +PurchaseType = Literal[0] + + +class PurchaseNotification(TypedDict): + type: PurchaseType + guild_product_purchase: NotRequired[GuildProductPurchase] + + # fmt: off -MessageType = Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 36, 37, 38, 39] +MessageType = Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 36, 37, 38, 39, 44] # fmt: on @@ -114,6 +127,7 @@ class Message(TypedDict): sticker_items: NotRequired[List[StickerItem]] position: NotRequired[int] role_subscription_data: NotRequired[RoleSubscriptionData] + purchase_notification: NotRequired[PurchaseNotification] # specific to MESSAGE_CREATE/MESSAGE_UPDATE events guild_id: NotRequired[Snowflake] diff --git a/docs/api/messages.rst b/docs/api/messages.rst index e0a7c8a513..090bfa3bb1 100644 --- a/docs/api/messages.rst +++ b/docs/api/messages.rst @@ -62,6 +62,22 @@ RoleSubscriptionData .. autoclass:: RoleSubscriptionData :members: +GuildProductInfo +~~~~~~~~~~~~~~~~ + +.. attributetable:: GuildProductInfo + +.. autoclass:: GuildProductInfo + :members: + +PurchaseNotificationInfo +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. attributetable:: PurchaseNotificationInfo + +.. autoclass:: PurchaseNotificationInfo + :members: + RawTypingEvent ~~~~~~~~~~~~~~ @@ -183,6 +199,14 @@ Enumerations MessageType ~~~~~~~~~~~ +.. class:: PurchaseType + + Specifies the type of purchase for :class:`PurchaseNotificationInfo`. + + .. attribute:: guild_product + + The purchase is of a product from a guild. + .. class:: MessageType Specifies the type of :class:`Message`. This is used to denote if a message @@ -367,6 +391,11 @@ MessageType The system message denoting that a raid report was a false alarm. + .. versionadded:: 2.10 + .. attribute:: purchase_notification + + The system message denoting that a user has purchased a product. + .. versionadded:: 2.10 Events