Repository: Rapptz/discord.py
File: discord/message.py (Python)

Code before:
            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.

    There are two ways to construct this class. The first one is through
    the constructor itself, and the second is via the following:

    - :meth:`TextChannel.get_partial_message`
    - :meth:`VoiceChannel.get_partial_message`
    - :meth:`StageChannel.get_partial_message`
    - :meth:`Thread.get_partial_message`
    - :meth:`DMChannel.get_partial_message`

    Note that this class is trimmed down and has no rich attributes.

    .. versionadded:: 1.6

    .. container:: operations

        .. describe:: x == y

            Checks if two partial messages are equal.

        .. describe:: x != y

            Checks if two partial messages are not equal.

        .. describe:: hash(x)

            Returns the partial message's hash.

    Attributes
    -----------
    channel: Union[:class:`PartialMessageable`, :class:`TextChannel`, :class:`StageChannel`, :class:`VoiceChannel`, :class:`Thread`, :class:`DMChannel`]
        The channel associated with this partial message.
    id: :class:`int`
        The message ID.
    guild: Optional[:class:`Guild`]
        The guild that the partial message belongs to, if applicable.
    """

    __slots__ = ('channel', 'id', '_cs_guild', '_state', 'guild')

    def __init__(self, *, channel: MessageableChannel, id: int) -> None:
        if not isinstance(channel, PartialMessageable) and channel.type not in (
            ChannelType.text,
            ChannelType.voice,
            ChannelType.stage_voice,

Review comment (suggestion):
`__hash__` with no `__eq__` is weird, you should define both if you're going to do that.

Code after:
        self.tier_name: str = data['tier_name']
        self.total_months_subscribed: int = data['total_months_subscribed']
        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']