-
Notifications
You must be signed in to change notification settings - Fork 138
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: application subscriptions (#1113)
- Loading branch information
Showing
20 changed files
with
994 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
Support application subscriptions (see the :ddocs:`official docs <monetization/overview>` for more info). | ||
- New types: :class:`SKU`, :class:`Entitlement`. | ||
- New :attr:`Interaction.entitlements` attribute, and :meth:`InteractionResponse.require_premium` response type. | ||
- New events: :func:`on_entitlement_create`, :func:`on_entitlement_update`, :func:`on_entitlement_delete`. | ||
- New :class:`Client` methods: :meth:`~Client.skus`, :meth:`~Client.entitlements`, :meth:`~Client.create_entitlement`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
# SPDX-License-Identifier: MIT | ||
|
||
from __future__ import annotations | ||
|
||
import datetime | ||
from typing import TYPE_CHECKING, Optional | ||
|
||
from .enums import EntitlementType, try_enum | ||
from .mixins import Hashable | ||
from .utils import _get_as_snowflake, parse_time, snowflake_time, utcnow | ||
|
||
if TYPE_CHECKING: | ||
from .guild import Guild | ||
from .state import ConnectionState | ||
from .types.entitlement import Entitlement as EntitlementPayload | ||
from .user import User | ||
|
||
|
||
__all__ = ("Entitlement",) | ||
|
||
|
||
class Entitlement(Hashable): | ||
"""Represents an entitlement. | ||
This is usually retrieved using :meth:`Client.entitlements`, from | ||
:attr:`Interaction.entitlements` when using interactions, or provided by | ||
events (e.g. :func:`on_entitlement_create`). | ||
Note that some entitlements may have ended already; consider using | ||
:meth:`is_active` to check whether a given entitlement is considered active at the current time, | ||
or use ``exclude_ended=True`` when fetching entitlements using :meth:`Client.entitlements`. | ||
You may create new entitlements for testing purposes using :meth:`Client.create_entitlement`. | ||
.. collapse:: operations | ||
.. describe:: x == y | ||
Checks if two :class:`Entitlement`\\s are equal. | ||
.. describe:: x != y | ||
Checks if two :class:`Entitlement`\\s are not equal. | ||
.. describe:: hash(x) | ||
Returns the entitlement's hash. | ||
.. versionadded:: 2.10 | ||
Attributes | ||
---------- | ||
id: :class:`int` | ||
The entitlement's ID. | ||
type: :class:`EntitlementType` | ||
The entitlement's type. | ||
sku_id: :class:`int` | ||
The ID of the associated SKU. | ||
user_id: Optional[:class:`int`] | ||
The ID of the user that is granted access to the entitlement's SKU. | ||
See also :attr:`user`. | ||
guild_id: Optional[:class:`int`] | ||
The ID of the guild that is granted access to the entitlement's SKU. | ||
See also :attr:`guild`. | ||
application_id: :class:`int` | ||
The parent application's ID. | ||
deleted: :class:`bool` | ||
Whether the entitlement was deleted. | ||
starts_at: Optional[:class:`datetime.datetime`] | ||
The time at which the entitlement starts being active. | ||
Set to ``None`` when this is a test entitlement. | ||
ends_at: Optional[:class:`datetime.datetime`] | ||
The time at which the entitlement stops being active. | ||
Set to ``None`` when this is a test entitlement. | ||
You can use :meth:`is_active` to check whether this entitlement is still active. | ||
""" | ||
|
||
__slots__ = ( | ||
"_state", | ||
"id", | ||
"sku_id", | ||
"user_id", | ||
"guild_id", | ||
"application_id", | ||
"type", | ||
"deleted", | ||
"starts_at", | ||
"ends_at", | ||
) | ||
|
||
def __init__(self, *, data: EntitlementPayload, state: ConnectionState) -> None: | ||
self._state: ConnectionState = state | ||
|
||
self.id: int = int(data["id"]) | ||
self.sku_id: int = int(data["sku_id"]) | ||
self.user_id: Optional[int] = _get_as_snowflake(data, "user_id") | ||
self.guild_id: Optional[int] = _get_as_snowflake(data, "guild_id") | ||
self.application_id: int = int(data["application_id"]) | ||
self.type: EntitlementType = try_enum(EntitlementType, data["type"]) | ||
self.deleted: bool = data.get("deleted", False) | ||
self.starts_at: Optional[datetime.datetime] = parse_time(data.get("starts_at")) | ||
self.ends_at: Optional[datetime.datetime] = parse_time(data.get("ends_at")) | ||
|
||
def __repr__(self) -> str: | ||
# presumably one of these is set | ||
if self.user_id: | ||
grant_repr = f"user_id={self.user_id!r}" | ||
else: | ||
grant_repr = f"guild_id={self.guild_id!r}" | ||
return ( | ||
f"<Entitlement id={self.id!r} sku_id={self.sku_id!r} type={self.type!r} {grant_repr}>" | ||
) | ||
|
||
@property | ||
def created_at(self) -> datetime.datetime: | ||
""":class:`datetime.datetime`: Returns the entitlement's creation time in UTC.""" | ||
return snowflake_time(self.id) | ||
|
||
@property | ||
def guild(self) -> Optional[Guild]: | ||
"""Optional[:class:`Guild`]: The guild that is granted access to | ||
this entitlement's SKU, if applicable. | ||
""" | ||
return self._state._get_guild(self.guild_id) | ||
|
||
@property | ||
def user(self) -> Optional[User]: | ||
"""Optional[:class:`User`]: The user that is granted access to | ||
this entitlement's SKU, if applicable. | ||
Requires the user to be cached. | ||
See also :attr:`user_id`. | ||
""" | ||
return self._state.get_user(self.user_id) | ||
|
||
def is_active(self) -> bool: | ||
"""Whether the entitlement is currently active, | ||
based on :attr:`starts_at` and :attr:`ends_at`. | ||
Always returns ``True`` for test entitlements. | ||
:return type: :class:`bool` | ||
""" | ||
if self.deleted: | ||
return False | ||
|
||
now = utcnow() | ||
if self.starts_at is not None and now < self.starts_at: | ||
return False | ||
if self.ends_at is not None and now >= self.ends_at: | ||
return False | ||
|
||
return True | ||
|
||
async def delete(self) -> None: | ||
"""|coro| | ||
Deletes the entitlement. | ||
This is only valid for test entitlements; you cannot use this to | ||
delete entitlements that users purchased. | ||
Raises | ||
------ | ||
NotFound | ||
The entitlement does not exist. | ||
HTTPException | ||
Deleting the entitlement failed. | ||
""" | ||
await self._state.http.delete_test_entitlement(self.application_id, self.id) |
Oops, something went wrong.