Skip to content

Commit 98d6018

Browse files
feat: implement monetization (Pycord-Development#2273)
Signed-off-by: plun1331 <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 6ba54b4 commit 98d6018

17 files changed

+611
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ These changes are available on the `master` branch, but have not yet been releas
8888
([#2131](https://github.com/Pycord-Development/pycord/pull/2131))
8989
- Added support for guild onboarding related features.
9090
([#2127](https://github.com/Pycord-Development/pycord/pull/2127))
91+
- Added support for monetization-related objects and events.
92+
([#2273](https://github.com/Pycord-Development/pycord/pull/2273))
9193

9294
### Changed
9395

discord/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
from .member import *
5353
from .mentions import *
5454
from .message import *
55+
from .monetization import *
5556
from .object import *
5657
from .onboarding import *
5758
from .partial_emoji import *

discord/client.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
from .invite import Invite
5252
from .iterators import GuildIterator
5353
from .mentions import AllowedMentions
54+
from .monetization import SKU, Entitlement
5455
from .object import Object
5556
from .stage_instance import StageInstance
5657
from .state import ConnectionState
@@ -2002,3 +2003,33 @@ async def update_role_connection_metadata_records(
20022003
self.application_id, payload
20032004
)
20042005
return [ApplicationRoleConnectionMetadata.from_dict(r) for r in data]
2006+
2007+
async def fetch_skus(self) -> list[SKU]:
2008+
"""|coro|
2009+
2010+
Fetches the bot's SKUs.
2011+
2012+
.. versionadded:: 2.5
2013+
2014+
Returns
2015+
-------
2016+
List[:class:`.SKU`]
2017+
The bot's SKUs.
2018+
"""
2019+
data = await self._connection.http.list_skus(self.application_id)
2020+
return [SKU(data=s) for s in data]
2021+
2022+
async def fetch_entitlements(self) -> list[Entitlement]:
2023+
"""|coro|
2024+
2025+
Fetches the bot's entitlements.
2026+
2027+
.. versionadded:: 2.5
2028+
2029+
Returns
2030+
-------
2031+
List[:class:`.Entitlement`]
2032+
The bot's entitlements.
2033+
"""
2034+
data = await self._connection.http.list_entitlements(self.application_id)
2035+
return [Entitlement(data=e, state=self._connection) for e in data]

discord/enums.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@
7070
"PromptType",
7171
"OnboardingMode",
7272
"ReactionType",
73+
"SKUType",
74+
"EntitlementType",
75+
"EntitlementOwnerType",
7376
)
7477

7578

@@ -674,6 +677,7 @@ class InteractionResponseType(Enum):
674677
message_update = 7 # for components
675678
auto_complete_result = 8 # for autocomplete interactions
676679
modal = 9 # for modal dialogs
680+
premium_required = 10
677681

678682

679683
class VideoQualityMode(Enum):
@@ -985,6 +989,26 @@ class ReactionType(Enum):
985989
burst = 1
986990

987991

992+
class SKUType(Enum):
993+
"""The SKU type"""
994+
995+
subscription = 5
996+
subscription_group = 6
997+
998+
999+
class EntitlementType(Enum):
1000+
"""The entitlement type"""
1001+
1002+
application_subscription = 8
1003+
1004+
1005+
class EntitlementOwnerType(Enum):
1006+
"""The entitlement owner type"""
1007+
1008+
guild = 1
1009+
user = 2
1010+
1011+
9881012
T = TypeVar("T")
9891013

9901014

discord/flags.py

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"MemberCacheFlags",
3939
"ApplicationFlags",
4040
"ChannelFlags",
41+
"SKUFlags",
4142
)
4243

4344
FV = TypeVar("FV", bound="flag_value")
@@ -1492,8 +1493,6 @@ def require_tag(self):
14921493
class AttachmentFlags(BaseFlags):
14931494
r"""Wraps up the Discord Attachment flags.
14941495
1495-
See :class:`SystemChannelFlags`.
1496-
14971496
.. container:: operations
14981497
14991498
.. describe:: x == y
@@ -1519,20 +1518,20 @@ class AttachmentFlags(BaseFlags):
15191518
Returns the inverse of a flag.
15201519
.. describe:: hash(x)
15211520
1522-
Return the flag's hash.
1521+
Return the flag's hash.
15231522
.. describe:: iter(x)
15241523
1525-
Returns an iterator of ``(name, value)`` pairs. This allows it
1526-
to be, for example, constructed as a dict or a list of pairs.
1524+
Returns an iterator of ``(name, value)`` pairs. This allows it
1525+
to be, for example, constructed as a dict or a list of pairs.
1526+
Note that aliases are not shown.
15271527
15281528
.. versionadded:: 2.5
15291529
15301530
Attributes
15311531
-----------
15321532
value: :class:`int`
1533-
The raw value. This value is a bit array field of a 53-bit integer
1534-
representing the currently available flags. You should query
1535-
flags via the properties rather than using this raw value.
1533+
The raw value. You should query flags via the properties
1534+
rather than using this raw value.
15361535
"""
15371536

15381537
__slots__ = ()
@@ -1551,3 +1550,66 @@ def is_thumbnail(self):
15511550
def is_remix(self):
15521551
""":class:`bool`: Returns ``True`` if the attachment has been remixed."""
15531552
return 1 << 2
1553+
1554+
1555+
@fill_with_flags()
1556+
class SKUFlags(BaseFlags):
1557+
r"""Wraps up the Discord SKU flags.
1558+
1559+
.. container:: operations
1560+
1561+
.. describe:: x == y
1562+
1563+
Checks if two SKUFlags are equal.
1564+
.. describe:: x != y
1565+
1566+
Checks if two SKUFlags are not equal.
1567+
.. describe:: x + y
1568+
1569+
Adds two flags together. Equivalent to ``x | y``.
1570+
.. describe:: x - y
1571+
1572+
Subtracts two flags from each other.
1573+
.. describe:: x | y
1574+
1575+
Returns the union of two flags. Equivalent to ``x + y``.
1576+
.. describe:: x & y
1577+
1578+
Returns the intersection of two flags.
1579+
.. describe:: ~x
1580+
1581+
Returns the inverse of a flag.
1582+
.. describe:: hash(x)
1583+
1584+
Return the flag's hash.
1585+
.. describe:: iter(x)
1586+
1587+
Returns an iterator of ``(name, value)`` pairs. This allows it
1588+
to be, for example, constructed as a dict or a list of pairs.
1589+
Note that aliases are not shown.
1590+
1591+
.. versionadded:: 2.5
1592+
1593+
Attributes
1594+
-----------
1595+
value: :class:`int`
1596+
The raw value. You should query flags via the properties
1597+
rather than using this raw value.
1598+
"""
1599+
1600+
__slots__ = ()
1601+
1602+
@flag_value
1603+
def available(self):
1604+
""":class:`bool`: Returns ``True`` if the SKU is available for purchase."""
1605+
return 1 << 2
1606+
1607+
@flag_value
1608+
def guild_subscription(self):
1609+
""":class:`bool`: Returns ``True`` if the SKU is a guild subscription."""
1610+
return 1 << 7
1611+
1612+
@flag_value
1613+
def user_subscription(self):
1614+
""":class:`bool`: Returns ``True`` if the SKU is a user subscription."""
1615+
return 1 << 8

discord/guild.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
AutoModTriggerType,
5555
ChannelType,
5656
ContentFilter,
57+
EntitlementOwnerType,
5758
NotificationLevel,
5859
NSFWLevel,
5960
ScheduledEventLocationType,
@@ -71,6 +72,7 @@
7172
from .iterators import AuditLogIterator, BanIterator, MemberIterator
7273
from .member import Member, VoiceState
7374
from .mixins import Hashable
75+
from .monetization import Entitlement
7476
from .onboarding import Onboarding
7577
from .permissions import PermissionOverwrite
7678
from .role import Role
@@ -3950,3 +3952,26 @@ async def delete_auto_moderation_rule(
39503952
"""
39513953

39523954
await self._state.http.delete_auto_moderation_rule(self.id, id, reason=reason)
3955+
3956+
async def create_test_entitlement(self, sku: Snowflake) -> Entitlement:
3957+
"""|coro|
3958+
3959+
Creates a test entitlement for the guild.
3960+
3961+
Parameters
3962+
----------
3963+
sku: :class:`Snowflake`
3964+
The SKU to create a test entitlement for.
3965+
3966+
Returns
3967+
-------
3968+
:class:`Entitlement`
3969+
The created entitlement.
3970+
"""
3971+
payload = {
3972+
"sku_id": sku.id,
3973+
"owner_id": self.id,
3974+
"owner_type": EntitlementOwnerType.guild.value,
3975+
}
3976+
data = await self._state.http.create_test_entitlement(self.id, payload)
3977+
return Entitlement(data=data, state=self._state)

discord/http.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
invite,
7070
member,
7171
message,
72+
monetization,
7273
onboarding,
7374
role,
7475
scheduled_events,
@@ -2885,6 +2886,55 @@ def update_application_role_connection_metadata_records(
28852886
)
28862887
return self.request(r, json=payload)
28872888

2889+
# Monetization
2890+
2891+
def list_skus(
2892+
self,
2893+
application_id: Snowflake,
2894+
) -> Response[list[monetization.SKU]]:
2895+
r = Route(
2896+
"GET",
2897+
"/applications/{application_id}/skus",
2898+
application_id=application_id,
2899+
)
2900+
return self.request(r)
2901+
2902+
def list_entitlements(
2903+
self,
2904+
application_id: Snowflake,
2905+
) -> Response[list[monetization.Entitlement]]:
2906+
r = Route(
2907+
"GET",
2908+
"/applications/{application_id}/entitlements",
2909+
application_id=application_id,
2910+
)
2911+
return self.request(r)
2912+
2913+
def create_test_entitlement(
2914+
self,
2915+
application_id: Snowflake,
2916+
payload: monetization.CreateTestEntitlementPayload,
2917+
) -> Response[monetization.Entitlement]:
2918+
r = Route(
2919+
"POST",
2920+
"/applications/{application_id}/entitlements",
2921+
application_id=application_id,
2922+
)
2923+
return self.request(r, json=payload)
2924+
2925+
def delete_test_entitlement(
2926+
self,
2927+
application_id: Snowflake,
2928+
entitlement_id: Snowflake,
2929+
) -> Response[None]:
2930+
r = Route(
2931+
"DELETE",
2932+
"/applications/{application_id}/entitlements/{entitlement_id}",
2933+
application_id=application_id,
2934+
entitlement_id=entitlement_id,
2935+
)
2936+
return self.request(r)
2937+
28882938
# Onboarding
28892939

28902940
def get_onboarding(self, guild_id: Snowflake) -> Response[onboarding.Onboarding]:

discord/interactions.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from .flags import MessageFlags
3737
from .member import Member
3838
from .message import Attachment, Message
39+
from .monetization import Entitlement
3940
from .object import Object
4041
from .permissions import Permissions
4142
from .user import User
@@ -183,6 +184,9 @@ def _from_data(self, data: InteractionPayload):
183184
self.data.get("custom_id") if self.data is not None else None
184185
)
185186
self._app_permissions: int = int(data.get("app_permissions", 0))
187+
self.entitlements: list[Entitlement] = [
188+
Entitlement(data=e, state=self._state) for e in data.get("entitlements", [])
189+
]
186190

187191
self.message: Message | None = None
188192
self.channel = None
@@ -1185,6 +1189,37 @@ async def send_modal(self, modal: Modal) -> Interaction:
11851189
self._parent._state.store_modal(modal, self._parent.user.id)
11861190
return self._parent
11871191

1192+
async def premium_required(self) -> Interaction:
1193+
"""|coro|
1194+
Responds to this interaction by sending a premium required message.
1195+
1196+
Raises
1197+
------
1198+
HTTPException
1199+
Sending the message failed.
1200+
InteractionResponded
1201+
This interaction has already been responded to before.
1202+
"""
1203+
if self._responded:
1204+
raise InteractionResponded(self._parent)
1205+
1206+
parent = self._parent
1207+
1208+
adapter = async_context.get()
1209+
http = parent._state.http
1210+
await self._locked_response(
1211+
adapter.create_interaction_response(
1212+
parent.id,
1213+
parent.token,
1214+
session=parent._session,
1215+
proxy=http.proxy,
1216+
proxy_auth=http.proxy_auth,
1217+
type=InteractionResponseType.premium_required.value,
1218+
)
1219+
)
1220+
self._responded = True
1221+
return self._parent
1222+
11881223
async def _locked_response(self, coro: Coroutine[Any]):
11891224
"""|coro|
11901225

0 commit comments

Comments
 (0)