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: subscriptions & related changes #2564

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
978e0c8
feat: subscriptions & related changes
plun1331 Aug 28, 2024
e16c72a
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 28, 2024
af1b084
feat: changelog
plun1331 Aug 28, 2024
725cbaa
Merge remote-tracking branch 'origin/feat/subscriptions' into feat/su…
plun1331 Aug 28, 2024
7d35a67
feat: changelog
plun1331 Aug 28, 2024
1e317a8
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 28, 2024
efdb972
fix: move abc import to TYPE_CHECKING
plun1331 Aug 28, 2024
2227382
Merge remote-tracking branch 'origin/feat/subscriptions' into feat/su…
plun1331 Aug 28, 2024
635c4b5
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 28, 2024
32ebc62
fix: circular import from Entitlement
plun1331 Aug 28, 2024
c65b3ff
Merge remote-tracking branch 'origin/feat/subscriptions' into feat/su…
plun1331 Aug 28, 2024
9f00a5a
docs: correct directives from notice to note
plun1331 Aug 28, 2024
3ad15d6
Update discord/enums.py
Lulalaby Aug 28, 2024
3b1a695
despite what it looks like, this is a lazily written commit message
plun1331 Aug 28, 2024
1811cb7
Merge remote-tracking branch 'origin/feat/subscriptions' into feat/su…
plun1331 Aug 28, 2024
f3dc1a4
Merge branch 'master' into feat/subscriptions
plun1331 Aug 28, 2024
f31487e
despite what it looks like, this is a lazily written commit message 2
plun1331 Aug 28, 2024
44896c0
Merge remote-tracking branch 'origin/feat/subscriptions' into feat/su…
plun1331 Aug 28, 2024
121268b
fix bugs
plun1331 Sep 19, 2024
e870a2e
Merge branch 'master' into feat/subscriptions
plun1331 Sep 19, 2024
74a96c8
style(pre-commit): auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 19, 2024
e4aaaec
Update CHANGELOG.md
plun1331 Sep 19, 2024
b8a6bb7
Update iterators.py
plun1331 Sep 19, 2024
b4429fc
Merge branch 'master' into feat/subscriptions
plun1331 Sep 21, 2024
4477b80
apply review suggestions
plun1331 Sep 22, 2024
782af39
Merge branch 'master' into feat/subscriptions
plun1331 Sep 26, 2024
946b6bf
Merge branch 'master' into feat/subscriptions
plun1331 Oct 2, 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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,20 @@ These changes are available on the `master` branch, but have not yet been releas
([#2496](https://github.com/Pycord-Development/pycord/pull/2496))
- ⚠️ **This Version Removes Support For Python 3.8** ⚠️
([#2521](https://github.com/Pycord-Development/pycord/pull/2521))
- `Entitlement.ends_at` can now be `None`.
([#2564](https://github.com/Pycord-Development/pycord/pull/2564))

### Added

- Added `Guild.fetch_role` method.
([#2528](https://github.com/Pycord-Development/pycord/pull/2528))
- Added new `Subscription` object and related methods/events.
([#2564](https://github.com/Pycord-Development/pycord/pull/2564))

### Fixed

- Fixed `AttributeError` when trying to consume a consumable entitlement.
([#2564](https://github.com/Pycord-Development/pycord/pull/2564))

## [2.6.0] - 2024-07-09

Expand Down
2 changes: 1 addition & 1 deletion discord/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2048,7 +2048,7 @@ async def fetch_skus(self) -> list[SKU]:
The bot's SKUs.
"""
data = await self._connection.http.list_skus(self.application_id)
return [SKU(data=s) for s in data]
return [SKU(state=self._connection, data=s) for s in data]

def entitlements(
self,
Expand Down
8 changes: 8 additions & 0 deletions discord/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,14 @@ class PollLayoutType(Enum):
default = 1


class SubscriptionStatus(Enum):
"""The status of a subscription."""

active = 1
ending = 2
inactive = 3
plun1331 marked this conversation as resolved.
Show resolved Hide resolved


T = TypeVar("T")


Expand Down
37 changes: 37 additions & 0 deletions discord/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -3027,6 +3027,43 @@ def delete_test_entitlement(
)
return self.request(r)

async def list_sku_subscriptions(
self,
sku_id: Snowflake,
*,
before: Snowflake | None = None,
after: Snowflake | None = None,
limit: int = 50,
user_id: Snowflake | None = None,
) -> Response[list[monetization.Subscription]]:
params: dict[str, Any] = {}
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 user_id is not None:
params["user_id"] = user_id
return self.request(
Route("GET", "/skus/{sku_id}/subscriptions", sku_id=sku_id),
params=params,
)

async def get_subscription(
self,
sku_id: Snowflake,
subscription_id: Snowflake,
) -> Response[monetization.Subscription]:
return self.request(
Route(
"GET",
"/skus/{sku_id}/subscriptions/{subscription_id}",
sku_id=sku_id,
subscription_id=subscription_id,
)
)

# Onboarding

def get_onboarding(self, guild_id: Snowflake) -> Response[onboarding.Onboarding]:
Expand Down
112 changes: 110 additions & 2 deletions discord/iterators.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@

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 @@ -52,6 +51,7 @@
"MemberIterator",
"ScheduledEventSubscribersIterator",
"EntitlementIterator",
"SubscriptionIterator",
)

if TYPE_CHECKING:
Expand All @@ -64,6 +64,7 @@
from .types.audit_log import AuditLog as AuditLogPayload
from .types.guild import Guild as GuildPayload
from .types.message import Message as MessagePayload
from .types.monetization import Subscription as SubscriptionPayload
from .types.threads import Thread as ThreadPayload
from .types.user import PartialUser as PartialUserPayload
from .user import User
Expand Down Expand Up @@ -1010,6 +1011,11 @@ def _get_retrieve(self):
self.retrieve = r
return r > 0

async def create_entitlement(self, data):
from .monetization import Entitlement

return Entitlement(data=data, state=self.state)

async def fill_entitlements(self):
if not self._get_retrieve():
return
Expand Down Expand Up @@ -1040,4 +1046,106 @@ async def fill_entitlements(self):
self.after = Object(id=int(data[-1]["id"]))

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


class SubscriptionIterator(_AsyncIterator["Subscription"]):
def __init__(
self,
state,
sku_id: int,
limit: int = None,
before: datetime.datetime | None = None,
after: datetime.datetime | None = None,
user_id: int | None = None,
):
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.state = state
self.sku_id = sku_id
self.limit = limit
self.before = before
self.after = after
self.user_id = user_id

self._filter = None

self.get_subscriptions = state.http.list_sku_subscriptions
self.subscriptions = asyncio.Queue()

if self.before and self.after:
self._retrieve_subscriptions = self._retrieve_subscriptions_before_strategy # type: ignore
plun1331 marked this conversation as resolved.
Show resolved Hide resolved
self._filter = lambda m: int(m["id"]) > self.after.id
elif self.after:
self._retrieve_subscriptions = self._retrieve_subscriptions_after_strategy # type: ignore
plun1331 marked this conversation as resolved.
Show resolved Hide resolved
else:
self._retrieve_subscriptions = self._retrieve_subscriptions_before_strategy # type: ignore
plun1331 marked this conversation as resolved.
Show resolved Hide resolved

async def next(self) -> Guild:
if self.subscriptions.empty():
await self.fill_subscriptions()

try:
return self.subscriptions.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

def create_subscription(self, data):
from .monetization import Subscription

return Subscription(state=self.state, data=data)

async def fill_subscriptions(self):
if self._get_retrieve():
data = await self._retrieve_subscriptions(self.retrieve)
if self.limit is None or len(data) < 100:
self.limit = 0

if self._filter:
data = filter(self._filter, data)

for element in data:
await self.subscriptions.put(self.create_subscription(element))

async def _retrieve_subscriptions(self, retrieve) -> list[SubscriptionPayload]:
raise NotImplementedError

async def _retrieve_subscriptions_before_strategy(self, retrieve):
before = self.before.id if self.before else None
data: list[SubscriptionPayload] = await self.get_subscriptions(
self.sku_id,
limit=retrieve,
before=before,
user_id=self.user_id,
)
if len(data):
if self.limit is not None:
self.limit -= retrieve
self.before = Object(id=int(data[-1]["id"]))
return data

async def _retrieve_subscriptions_after_strategy(self, retrieve):
after = self.after.id if self.after else None
data: list[SubscriptionPayload] = await self.get_subscriptions(
self.sku_id,
limit=retrieve,
after=after,
user_id=self.user_id,
)
if len(data):
if self.limit is not None:
self.limit -= retrieve
self.after = Object(id=int(data[0]["id"]))
return data
Loading