Browse Source

Add support for role subscription reading

pull/9171/head
Rapptz 2 years ago
parent
commit
3d24f0fb12
  1. 1
      discord/enums.py
  2. 18
      discord/flags.py
  3. 55
      discord/message.py
  4. 16
      discord/role.py
  5. 2
      discord/types/integration.py
  6. 10
      discord/types/message.py
  7. 2
      discord/types/role.py
  8. 13
      docs/api.rst

1
discord/enums.py

@ -232,6 +232,7 @@ class MessageType(Enum):
guild_invite_reminder = 22 guild_invite_reminder = 22
context_menu_command = 23 context_menu_command = 23
auto_moderation_action = 24 auto_moderation_action = 24
role_subscription_purchase = 25
class SpeakingState(Enum): class SpeakingState(Enum):

18
discord/flags.py

@ -290,6 +290,24 @@ class SystemChannelFlags(BaseFlags):
""" """
return 8 return 8
@flag_value
def role_subscription_purchase_notifications(self):
""":class:`bool`: Returns ``True`` if role subscription purchase and renewal
notifications are enabled.
.. versionadded:: 2.2
"""
return 16
@flag_value
def role_subscription_purchase_notification_replies(self):
""":class:`bool`: Returns ``True`` if the role subscription notifications
have a sticker reply button.
.. versionadded:: 2.2
"""
return 32
@fill_with_flags() @fill_with_flags()
class MessageFlags(BaseFlags): class MessageFlags(BaseFlags):

55
discord/message.py

@ -73,6 +73,7 @@ if TYPE_CHECKING:
MessageReference as MessageReferencePayload, MessageReference as MessageReferencePayload,
MessageApplication as MessageApplicationPayload, MessageApplication as MessageApplicationPayload,
MessageActivity as MessageActivityPayload, MessageActivity as MessageActivityPayload,
RoleSubscriptionData as RoleSubscriptionDataPayload,
) )
from .types.interactions import MessageInteraction as MessageInteractionPayload from .types.interactions import MessageInteraction as MessageInteractionPayload
@ -108,6 +109,7 @@ __all__ = (
'MessageReference', 'MessageReference',
'DeletedReferencedMessage', 'DeletedReferencedMessage',
'MessageApplication', 'MessageApplication',
'RoleSubscriptionInfo',
) )
@ -657,6 +659,39 @@ class MessageApplication:
return None return None
class RoleSubscriptionInfo:
"""Represents a message's role subscription information.
This is currently only attached to messages of type :attr:`MessageType.role_subscription_purchase`.
.. versionadded:: 2.0
Attributes
-----------
role_subscription_listing_id: :class:`int`
The ID of the SKU and listing that the user is subscribed to.
tier_name: :class:`str`
The name of the tier that the user is subscribed to.
total_months_subscribed: :class:`int`
The cumulative number of months that the user has been subscribed for.
is_renewal: :class:`bool`
Whether this notification is for a renewal rather than a new purchase.
"""
__slots__ = (
'role_subscription_listing_id',
'tier_name',
'total_months_subscribed',
'is_renewal',
)
def __init__(self, data: RoleSubscriptionDataPayload) -> None:
self.role_subscription_listing_id: int = int(data['role_subscription_listing_id'])
self.tier_name: str = data['tier_name']
self.total_months_subscribed: int = data['total_months_subscribed']
self.is_renewal: bool = data['is_renewal']
class PartialMessage(Hashable): class PartialMessage(Hashable):
"""Represents a partial message to aid with working messages when only """Represents a partial message to aid with working messages when only
a message and channel ID are present. a message and channel ID are present.
@ -1399,6 +1434,11 @@ class Message(PartialMessage, Hashable):
The interaction that this message is a response to. The interaction that this message is a response to.
.. versionadded:: 2.0 .. versionadded:: 2.0
role_subscription: Optional[:class:`RoleSubscriptionInfo`]
The data of the role subscription purchase or renewal that prompted this
:attr:`MessageType.role_subscription_purchase` message.
.. versionadded:: 2.2
guild: Optional[:class:`Guild`] guild: Optional[:class:`Guild`]
The guild that the message belongs to, if applicable. The guild that the message belongs to, if applicable.
""" """
@ -1431,6 +1471,7 @@ class Message(PartialMessage, Hashable):
'stickers', 'stickers',
'components', 'components',
'interaction', 'interaction',
'role_subscription',
) )
if TYPE_CHECKING: if TYPE_CHECKING:
@ -1516,6 +1557,14 @@ class Message(PartialMessage, Hashable):
else: else:
self.application = MessageApplication(state=self._state, data=application) self.application = MessageApplication(state=self._state, data=application)
self.role_subscription: Optional[RoleSubscriptionInfo] = None
try:
role_subscription = data['role_subscription_data']
except KeyError:
pass
else:
self.role_subscription = RoleSubscriptionInfo(role_subscription)
for handler in ('author', 'member', 'mentions', 'mention_roles', 'components'): for handler in ('author', 'member', 'mentions', 'mention_roles', 'components'):
try: try:
getattr(self, f'_handle_{handler}')(data[handler]) getattr(self, f'_handle_{handler}')(data[handler])
@ -1939,6 +1988,12 @@ class Message(PartialMessage, Hashable):
if self.type is MessageType.guild_invite_reminder: if self.type is MessageType.guild_invite_reminder:
return 'Wondering who to invite?\nStart by inviting anyone who can help you build the server!' return 'Wondering who to invite?\nStart by inviting anyone who can help you build the server!'
if self.type is MessageType.role_subscription_purchase and self.role_subscription is not None:
# TODO: figure out how the message looks like for is_renewal: true
total_months = self.role_subscription.total_months_subscribed
months = '1 month' if total_months == 1 else f'{total_months} months'
return f'{self.author.name} joined {self.role_subscription.tier_name} and has been a subscriber of {self.guild} for {months}!'
# Fallback for unknown message types # Fallback for unknown message types
return '' return ''

16
discord/role.py

@ -65,22 +65,31 @@ class RoleTags:
The bot's user ID that manages this role. The bot's user ID that manages this role.
integration_id: Optional[:class:`int`] integration_id: Optional[:class:`int`]
The integration ID that manages the role. The integration ID that manages the role.
subscription_listing_id: Optional[:class:`int`]
The ID of this role's subscription SKU and listing.
.. versionadded:: 2.2
""" """
__slots__ = ( __slots__ = (
'bot_id', 'bot_id',
'integration_id', 'integration_id',
'_premium_subscriber', '_premium_subscriber',
'_available_for_purchase',
'subscription_listing_id',
) )
def __init__(self, data: RoleTagPayload): def __init__(self, data: RoleTagPayload):
self.bot_id: Optional[int] = _get_as_snowflake(data, 'bot_id') self.bot_id: Optional[int] = _get_as_snowflake(data, 'bot_id')
self.integration_id: Optional[int] = _get_as_snowflake(data, 'integration_id') self.integration_id: Optional[int] = _get_as_snowflake(data, 'integration_id')
self.subscription_listing_id: Optional[int] = _get_as_snowflake(data, 'subscription_listing_id')
# NOTE: The API returns "null" for this if it's valid, which corresponds to None. # NOTE: The API returns "null" for this if it's valid, which corresponds to None.
# This is different from other fields where "null" means "not there". # This is different from other fields where "null" means "not there".
# So in this case, a value of None is the same as True. # So in this case, a value of None is the same as True.
# Which means we would need a different sentinel. # Which means we would need a different sentinel.
self._premium_subscriber: Optional[Any] = data.get('premium_subscriber', MISSING) self._premium_subscriber: Optional[Any] = data.get('premium_subscriber', MISSING)
self._available_for_purchase: Optional[Any] = data.get('available_for_purchase', MISSING)
def is_bot_managed(self) -> bool: def is_bot_managed(self) -> bool:
""":class:`bool`: Whether the role is associated with a bot.""" """:class:`bool`: Whether the role is associated with a bot."""
@ -94,6 +103,13 @@ class RoleTags:
""":class:`bool`: Whether the role is managed by an integration.""" """:class:`bool`: Whether the role is managed by an integration."""
return self.integration_id is not None return self.integration_id is not None
def is_available_for_purchase(self) -> bool:
""":class:`bool`: Whether the role is available for purchase.
.. versionadded:: 2.2
"""
return self._available_for_purchase is None
def __repr__(self) -> str: def __repr__(self) -> str:
return ( return (
f'<RoleTags bot_id={self.bot_id} integration_id={self.integration_id} ' f'<RoleTags bot_id={self.bot_id} integration_id={self.integration_id} '

2
discord/types/integration.py

@ -56,7 +56,7 @@ class PartialIntegration(TypedDict):
application_id: Snowflake application_id: Snowflake
IntegrationType = Literal['twitch', 'youtube', 'discord'] IntegrationType = Literal['twitch', 'youtube', 'discord', 'guild_subscription']
class BaseIntegration(PartialIntegration): class BaseIntegration(PartialIntegration):

10
discord/types/message.py

@ -93,7 +93,14 @@ class MessageReference(TypedDict, total=False):
fail_if_not_exists: bool fail_if_not_exists: bool
MessageType = Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 18, 19, 20, 21] class RoleSubscriptionData(TypedDict):
role_subscription_listing_id: Snowflake
tier_name: str
total_months_subscribed: int
is_renewal: bool
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]
class Message(PartialMessage): class Message(PartialMessage):
@ -124,6 +131,7 @@ class Message(PartialMessage):
referenced_message: NotRequired[Optional[Message]] referenced_message: NotRequired[Optional[Message]]
interaction: NotRequired[MessageInteraction] interaction: NotRequired[MessageInteraction]
components: NotRequired[List[Component]] components: NotRequired[List[Component]]
role_subscription_data: NotRequired[RoleSubscriptionData]
AllowedMentionType = Literal['roles', 'users', 'everyone'] AllowedMentionType = Literal['roles', 'users', 'everyone']

2
discord/types/role.py

@ -47,4 +47,6 @@ class Role(TypedDict):
class RoleTags(TypedDict, total=False): class RoleTags(TypedDict, total=False):
bot_id: Snowflake bot_id: Snowflake
integration_id: Snowflake integration_id: Snowflake
subscription_listing_id: Snowflake
premium_subscriber: None premium_subscriber: None
available_for_purchase: None

13
docs/api.rst

@ -1618,6 +1618,11 @@ of :class:`enum.Enum`.
sent if the rule is configured to sent an alert when triggered. sent if the rule is configured to sent an alert when triggered.
.. versionadded:: 2.0 .. versionadded:: 2.0
.. attribute:: role_subscription_purchase
The system message sent when a user purchases or renews a role subscription.
.. versionadded:: 2.2
.. class:: UserFlags .. class:: UserFlags
@ -4626,6 +4631,14 @@ MessageApplication
.. autoclass:: MessageApplication .. autoclass:: MessageApplication
:members: :members:
RoleSubscriptionInfo
~~~~~~~~~~~~~~~~~~~~~
.. attributetable:: RoleSubscriptionInfo
.. autoclass:: RoleSubscriptionInfo
:members:
Intents Intents
~~~~~~~~~~ ~~~~~~~~~~

Loading…
Cancel
Save