Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update monetization #2438

Merged
merged 52 commits into from
Jun 29, 2024
Merged
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
10dddf5
Update enums.py
plun1331 Apr 26, 2024
52ee538
Update enums.rst
plun1331 Apr 26, 2024
ae35166
Update enums.py
plun1331 Apr 26, 2024
0503f40
Update enums.rst
plun1331 Apr 26, 2024
e2ecb7a
Update http.py
plun1331 Apr 26, 2024
05740a0
Update monetization.py
plun1331 Apr 26, 2024
7de9c5b
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 26, 2024
cb2051f
Update CHANGELOG.md
plun1331 Apr 26, 2024
1d6151f
Update CHANGELOG.md
plun1331 Apr 26, 2024
1ebcbf1
Merge branch 'master' into upd-monetization
plun1331 Apr 26, 2024
6d3a736
Merge branch 'master' into upd-monetization
plun1331 Apr 26, 2024
ab80811
Update http.py
plun1331 Apr 26, 2024
ea727a2
Update monetization.py
plun1331 Apr 26, 2024
b258a10
Update iterators.py
plun1331 Apr 26, 2024
6662f33
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 26, 2024
e0b1bad
Update client.py
plun1331 Apr 26, 2024
4a94647
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 26, 2024
f68e4ad
Update iterators.py
plun1331 Apr 26, 2024
1cfdf27
Update iterators.py
plun1331 Apr 26, 2024
98287eb
Update iterators.py
plun1331 Apr 26, 2024
ef403b8
Update client.py
plun1331 Apr 26, 2024
84af33d
Update guild.py
plun1331 Apr 26, 2024
c9299bd
Update user.py
plun1331 Apr 26, 2024
4a2ad7c
Update guild.py
plun1331 Apr 26, 2024
3ba1937
Update client.py
plun1331 Apr 26, 2024
5459de7
Update user.py
plun1331 Apr 26, 2024
c5da6e2
Update user.py
plun1331 Apr 26, 2024
9a74aa8
Merge branch 'master' into upd-monetization
Lulalaby Apr 26, 2024
e0c3760
Merge branch 'master' into upd-monetization
plun1331 Apr 27, 2024
4d14087
Merge branch 'master' into upd-monetization
plun1331 Apr 30, 2024
323ca29
Update interactions.py
plun1331 May 16, 2024
842c208
Update components.py
plun1331 May 16, 2024
d179f69
Update components.py
plun1331 May 16, 2024
e0d8975
Update components.py
plun1331 May 16, 2024
707507b
Update enums.py
plun1331 May 16, 2024
0d8a812
Update button.py
plun1331 May 16, 2024
3940579
Update enums.rst
plun1331 May 16, 2024
d22db63
Update button.py
plun1331 May 17, 2024
9110129
Merge branch 'master' into upd-monetization
plun1331 May 29, 2024
f67d614
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 29, 2024
2085cb0
Merge branch 'master' into upd-monetization
plun1331 Jun 6, 2024
4909dae
feat: add urls
plun1331 Jun 26, 2024
e1401bd
Merge branch 'master' into upd-monetization
plun1331 Jun 26, 2024
eee3671
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 26, 2024
2e74bd9
Apply suggestions from code review
plun1331 Jun 26, 2024
c9e7dd0
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 26, 2024
2d888fb
Apply suggestions from code review
Dorukyum Jun 28, 2024
9be4fae
Add spacing
Dorukyum Jun 28, 2024
94e9bfc
Add spacing
Dorukyum Jun 28, 2024
a63f008
Apply suggestions from code review
plun1331 Jun 28, 2024
df1bbdb
Merge branch 'master' into upd-monetization
plun1331 Jun 28, 2024
cd1c70a
button.py aktualisieren
Dorukyum Jun 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ These changes are available on the `master` branch, but have not yet been releas
([#2450](https://github.com/Pycord-Development/pycord/pull/2450))
- Added support for user-installable applications.
([#2409](https://github.com/Pycord-Development/pycord/pull/2409)
- Added support for one-time purchases for Discord monetization.
([#2438](https://github.com/Pycord-Development/pycord/pull/2438))

### Fixed

Expand Down
68 changes: 63 additions & 5 deletions discord/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
from .guild import Guild
from .http import HTTPClient
from .invite import Invite
from .iterators import GuildIterator
from .iterators import EntitlementIterator, GuildIterator
from .mentions import AllowedMentions
from .monetization import SKU, Entitlement
from .object import Object
Expand Down Expand Up @@ -2043,17 +2043,75 @@ async def fetch_skus(self) -> list[SKU]:
data = await self._connection.http.list_skus(self.application_id)
return [SKU(data=s) for s in data]

async def fetch_entitlements(self) -> list[Entitlement]:
async def fetch_entitlements(
self,
user: Snowflake | None = None,
skus: list[Snowflake] | None = None,
before: SnowflakeTime | None = None,
after: SnowflakeTime | None = None,
limit: int | None = 100,
guild: Snowflake | None = None,
exclude_ended: bool = False,
) -> EntitlementIterator:
"""|coro|

Fetches the bot's entitlements.

.. versionadded:: 2.5

.. versionchanged:: 2.6

Added new parameters
Dorukyum marked this conversation as resolved.
Show resolved Hide resolved

Dorukyum marked this conversation as resolved.
Show resolved Hide resolved
Parameters
----------
user: :class:`.abc.Snowflake` | None
Dorukyum marked this conversation as resolved.
Show resolved Hide resolved
Limit the fetched entitlements to entitlements owned by this user.
skus: list[:class:`.abc.Snowflake`] | None
Limit the fetched entitlements to entitlements that are for these SKUs.
before: :class:`.abc.Snowflake` | :class:`datetime.datetime` | None
Retrieves guilds before this date or object.
If a datetime is provided, it is recommended to use a UTC-aware datetime.
If the datetime is naive, it is assumed to be local time.
after: :class:`.abc.Snowflake` | :class:`datetime.datetime` | None
Retrieve guilds after this date or object.
If a datetime is provided, it is recommended to use a UTC-aware datetime.
If the datetime is naive, it is assumed to be local time.
limit: Optional[:class:`int`]
The number of entitlements to retrieve.
If ``None``, retrieves every entitlement, which may be slow.
Defaults to ``100``.
guild: :class:`.abc.Snowflake` | None
Limit the fetched entitlements to entitlements owned by this guild.
exclude_ended: :class:`bool`
Whether to limit the fetched entitlements to those that have not ended.
Defaults to ``False``.

Returns
-------
List[:class:`.Entitlement`]
The bot's entitlements.
The application's entitlements.

Raises
------
:exc:`HTTPException`
Retrieving the entitlements failed.
"""
return EntitlementIterator(
self._connection,
user_id=user.id,
sku_ids=[sku.id for sku in skus],
before=before,
after=after,
limit=limit,
guild_id=guild.id,
exclude_ended=exclude_ended,
)

@property
def store_url(self) -> str:
""":class:`str`: The URL that leads to the application's store page for monetization.

.. versionadded:: 2.6
"""
data = await self._connection.http.list_entitlements(self.application_id)
return [Entitlement(data=e, state=self._connection) for e in data]
return f"https://discord.com/application-directory/{self.application_id}/store"
7 changes: 7 additions & 0 deletions discord/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ class Button(Component):
The label of the button, if any.
emoji: Optional[:class:`PartialEmoji`]
The emoji of the button, if available.
sku_id: Optional[:class:`int`]
The ID of the SKU this button refers to.
"""

__slots__: tuple[str, ...] = (
Expand All @@ -243,6 +245,7 @@ class Button(Component):
"disabled",
"label",
"emoji",
"sku_id",
)

__repr_info__: ClassVar[tuple[str, ...]] = __slots__
Expand All @@ -259,6 +262,7 @@ def __init__(self, data: ButtonComponentPayload):
self.emoji = PartialEmoji.from_dict(data["emoji"])
except KeyError:
self.emoji = None
self.sku_id: str | None = data.get("sku_id")

def to_dict(self) -> ButtonComponentPayload:
payload = {
Expand All @@ -276,6 +280,9 @@ def to_dict(self) -> ButtonComponentPayload:
if self.emoji:
payload["emoji"] = self.emoji.to_dict()

if self.sku_id:
payload["sku_id"] = self.sku_id

return payload # type: ignore


Expand Down
10 changes: 10 additions & 0 deletions discord/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,7 @@ class ButtonStyle(Enum):
success = 3
danger = 4
link = 5
premium = 6

# Aliases
blurple = 1
Expand Down Expand Up @@ -1005,13 +1006,22 @@ class ReactionType(Enum):
class SKUType(Enum):
"""The SKU type"""

durable = 2
consumable = 3
subscription = 5
subscription_group = 6


class EntitlementType(Enum):
"""The entitlement type"""

purchase = 1
premium_subscription = 2
developer_gift = 3
test_mode_purchase = 4
free_purchase = 5
user_gift = 6
premium_purchase = 7
application_subscription = 8


Expand Down
56 changes: 56 additions & 0 deletions discord/guild.py
Original file line number Diff line number Diff line change
Expand Up @@ -4070,3 +4070,59 @@ async def create_test_entitlement(self, sku: Snowflake) -> Entitlement:
}
data = await self._state.http.create_test_entitlement(self.id, payload)
return Entitlement(data=data, state=self._state)

async def fetch_entitlements(
self,
skus: list[Snowflake] | None = None,
before: SnowflakeTime | None = None,
after: SnowflakeTime | None = None,
limit: int | None = 100,
exclude_ended: bool = False,
) -> EntitlementIterator:
"""|coro|

Fetches this guild's entitlements.

This is identical to :meth:`Client.fetch_entitlements` with the ``guild`` parameter.

.. versionadded:: 2.6

Parameters
----------
skus: list[:class:`.abc.Snowflake`] | None
Limit the fetched entitlements to entitlements that are for these SKUs.
before: :class:`.abc.Snowflake` | :class:`datetime.datetime` | None
Retrieves guilds before this date or object.
If a datetime is provided, it is recommended to use a UTC aware datetime.
Dorukyum marked this conversation as resolved.
Show resolved Hide resolved
If the datetime is naive, it is assumed to be local time.
after: :class:`.abc.Snowflake` | :class:`datetime.datetime` | None
Retrieve guilds after this date or object.
If a datetime is provided, it is recommended to use a UTC aware datetime.
Dorukyum marked this conversation as resolved.
Show resolved Hide resolved
If the datetime is naive, it is assumed to be local time.
limit: Optional[:class:`int`]
The number of entitlements to retrieve.
If ``None``, retrieves every entitlement, which may be slow.
Defaults to ``100``.
exclude_ended: :class:`bool`
Whether to limit the fetched entitlements to those that have not ended.
Defaults to ``False``.

Returns
-------
List[:class:`.Entitlement`]
The application's entitlements.

Raises
------
:exc:`HTTPException`
Retrieving the entitlements failed.
"""
return EntitlementIterator(
self._state,
sku_ids=[sku.id for sku in skus],
before=before,
after=after,
limit=limit,
guild_id=self.id,
exclude_ended=exclude_ended,
)
37 changes: 37 additions & 0 deletions discord/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -2957,12 +2957,49 @@ def list_skus(
def list_entitlements(
self,
application_id: Snowflake,
*,
user_id: Snowflake | None = None,
sku_ids: list[Snowflake] | None = None,
before: Snowflake | None = None,
after: Snowflake | None = None,
limit: int | None = None,
guild_id: Snowflake | None = None,
exclude_ended: bool | None = None,
) -> Response[list[monetization.Entitlement]]:
params: dict[str, Any] = {}
if user_id is not None:
params["user_id"] = user_id
if sku_ids is not None:
params["sku_ids"] = ",".join(sku_ids)
if before is not None:
params["before"] = before
if after is not None:
params["after"] = after
if limit is not None:
params["limit"] = limit
if guild_id is not None:
params["guild_id"] = guild_id
if exclude_ended is not None:
params["exclude_ended"] = exclude_ended

r = Route(
"GET",
"/applications/{application_id}/entitlements",
application_id=application_id,
)
return self.request(r, params=params)

def consume_entitlement(
self,
application_id: Snowflake,
entitlement_id: Snowflake,
) -> Response[None]:
r = Route(
"POST",
"/applications/{application_id}/entitlements/{entitlement_id}/consume",
application_id=application_id,
entitlement_id=entitlement_id,
)
return self.request(r)

def create_test_entitlement(
Expand Down
6 changes: 6 additions & 0 deletions discord/interactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1229,10 +1229,16 @@ async def send_modal(self, modal: Modal) -> Interaction:
self._parent._state.store_modal(modal, self._parent.user.id)
return self._parent

@utils.deprecated("a button with type ButtonType.premium", "2.6")
async def premium_required(self) -> Interaction:
"""|coro|

Responds to this interaction by sending a premium required message.

.. deprecated:: 2.6

A button with type :attr:`ButtonType.premium` should be used instead.

Raises
------
HTTPException
Expand Down
78 changes: 78 additions & 0 deletions discord/iterators.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

from .audit_logs import AuditLogEntry
from .errors import NoMoreItems
from .monetization import Entitlement
from .object import Object
from .utils import maybe_coroutine, snowflake_time, time_snowflake

Expand All @@ -50,6 +51,7 @@
"GuildIterator",
"MemberIterator",
"ScheduledEventSubscribersIterator",
"EntitlementIterator",
)

if TYPE_CHECKING:
Expand Down Expand Up @@ -988,3 +990,79 @@ async def fill_subs(self):
await self.subscribers.put(self.member_from_payload(element))
else:
await self.subscribers.put(self.user_from_payload(element))


class EntitlementIterator(_AsyncIterator["Entitlement"]):
def __init__(
self,
state,
user_id: int | None = None,
sku_ids: list[int] | None = None,
before: datetime.datetime | Object | None = None,
after: datetime.datetime | Object | None = None,
limit: int | None = None,
guild_id: int | None = None,
exclude_ended: bool | None = None,
):
self.user_id = user_id
self.sku_ids = sku_ids
if isinstance(before, datetime.datetime):
before = Object(id=time_snowflake(before, high=False))
if isinstance(after, datetime.datetime):
after = Object(id=time_snowflake(after, high=True))
self.before = before
self.after = after
self.limit = limit
self.guild_id = guild_id
self.exclude_ended = exclude_ended

self.state = state
self.get_entitlements = state.http.list_entitlements
self.entitlements = asyncio.Queue()

async def next(self) -> BanEntry:
if self.entitlements.empty():
await self.fill_entitlements()

try:
return self.entitlements.get_nowait()
except asyncio.QueueEmpty:
raise NoMoreItems()

def _get_retrieve(self):
l = self.limit
if l is None or l > 100:
r = 100
else:
r = l
self.retrieve = r
return r > 0

async def fill_entitlements(self):
if not self._get_retrieve():
return
before = self.before.id if self.before else None
after = self.after.id if self.after else None
data = await self.get_entitlements(
self.state.application_id,
before=before,
after=after,
limit=self.retrieve,
user_id=self.user_id,
guild_id=self.guild_id,
sku_ids=self.sku_ids,
exclude_ended=self.exclude_ended,
)
if not data:
# no data, terminate
return
if self.limit:
self.limit -= self.retrieve

if len(data) < 100:
self.limit = 0 # terminate loop

self.after = Object(id=int(data[-1]["id"]))

for element in reversed(data):
await self.entitlements.put(Entitlement(data=element, state=self.state))
Loading
Loading