mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
feat: add subscriptions (#10541)
* feat: add subscriptions * types: fix fetch options types * fix: correct properties in patch method * chore: requested changes Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com> * fix: correct export syntax * chore(Entitlement): mark `ends_at` as nullable` * types(FetchSubscriptionOptions): add missing `cache` option * Revert "types(FetchSubscriptionOptions): add missing `cache` option" This reverts commit ba472bdc599e1860754e59fce4806610f06ac682. * chore(Entitlement): mark `startsTimestamp` as nullable * fix: requested changes * docs(SubscriptionManager): correct return type --------- Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const Events = require('../../../util/Events');
|
||||
|
||||
module.exports = (client, { d: data }) => {
|
||||
const subscription = client.application.subscriptions._add(data);
|
||||
|
||||
/**
|
||||
* Emitted whenever a subscription is created.
|
||||
* @event Client#subscriptionCreate
|
||||
* @param {Subscription} subscription The subscription that was created
|
||||
*/
|
||||
client.emit(Events.SubscriptionCreate, subscription);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
const Events = require('../../../util/Events');
|
||||
|
||||
module.exports = (client, { d: data }) => {
|
||||
const subscription = client.application.subscriptions._add(data, false);
|
||||
|
||||
client.application.subscriptions.cache.delete(subscription.id);
|
||||
|
||||
/**
|
||||
* Emitted whenever a subscription is deleted.
|
||||
* @event Client#subscriptionDelete
|
||||
* @param {Subscription} subscription The subscription that was deleted
|
||||
*/
|
||||
client.emit(Events.SubscriptionDelete, subscription);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
const Events = require('../../../util/Events');
|
||||
|
||||
module.exports = (client, { d: data }) => {
|
||||
const oldSubscription = client.application.subscriptions.cache.get(data.id)?._clone() ?? null;
|
||||
const newSubscription = client.application.subscriptions._add(data);
|
||||
|
||||
/**
|
||||
* Emitted whenever a subscription is updated - i.e. when a user's subscription renews.
|
||||
* @event Client#subscriptionUpdate
|
||||
* @param {?Subscription} oldSubscription The subscription before the update
|
||||
* @param {Subscription} newSubscription The subscription after the update
|
||||
*/
|
||||
client.emit(Events.SubscriptionUpdate, oldSubscription, newSubscription);
|
||||
};
|
||||
@@ -52,6 +52,9 @@ const handlers = Object.fromEntries([
|
||||
['STAGE_INSTANCE_CREATE', require('./STAGE_INSTANCE_CREATE')],
|
||||
['STAGE_INSTANCE_DELETE', require('./STAGE_INSTANCE_DELETE')],
|
||||
['STAGE_INSTANCE_UPDATE', require('./STAGE_INSTANCE_UPDATE')],
|
||||
['SUBSCRIPTION_CREATE', require('./SUBSCRIPTION_CREATE')],
|
||||
['SUBSCRIPTION_DELETE', require('./SUBSCRIPTION_DELETE')],
|
||||
['SUBSCRIPTION_UPDATE', require('./SUBSCRIPTION_UPDATE')],
|
||||
['THREAD_CREATE', require('./THREAD_CREATE')],
|
||||
['THREAD_DELETE', require('./THREAD_DELETE')],
|
||||
['THREAD_LIST_SYNC', require('./THREAD_LIST_SYNC')],
|
||||
|
||||
@@ -83,6 +83,7 @@ exports.ReactionManager = require('./managers/ReactionManager');
|
||||
exports.ReactionUserManager = require('./managers/ReactionUserManager');
|
||||
exports.RoleManager = require('./managers/RoleManager');
|
||||
exports.StageInstanceManager = require('./managers/StageInstanceManager');
|
||||
exports.SubscriptionManager = require('./managers/SubscriptionManager').SubscriptionManager;
|
||||
exports.ThreadManager = require('./managers/ThreadManager');
|
||||
exports.ThreadMemberManager = require('./managers/ThreadMemberManager');
|
||||
exports.UserManager = require('./managers/UserManager');
|
||||
@@ -193,6 +194,7 @@ exports.SKU = require('./structures/SKU').SKU;
|
||||
exports.StringSelectMenuOptionBuilder = require('./structures/StringSelectMenuOptionBuilder');
|
||||
exports.StageChannel = require('./structures/StageChannel');
|
||||
exports.StageInstance = require('./structures/StageInstance').StageInstance;
|
||||
exports.Subscription = require('./structures/Subscription').Subscription;
|
||||
exports.Sticker = require('./structures/Sticker').Sticker;
|
||||
exports.StickerPack = require('./structures/StickerPack');
|
||||
exports.Team = require('./structures/Team');
|
||||
|
||||
81
packages/discord.js/src/managers/SubscriptionManager.js
Normal file
81
packages/discord.js/src/managers/SubscriptionManager.js
Normal file
@@ -0,0 +1,81 @@
|
||||
'use strict';
|
||||
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { makeURLSearchParams } = require('@discordjs/rest');
|
||||
const { Routes } = require('discord-api-types/v10');
|
||||
const CachedManager = require('./CachedManager');
|
||||
const { DiscordjsTypeError, ErrorCodes } = require('../errors/index');
|
||||
const { Subscription } = require('../structures/Subscription');
|
||||
const { resolveSKUId } = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Manages API methods for subscriptions and stores their cache.
|
||||
* @extends {CachedManager}
|
||||
*/
|
||||
class SubscriptionManager extends CachedManager {
|
||||
constructor(client, iterable) {
|
||||
super(client, Subscription, iterable);
|
||||
}
|
||||
|
||||
/**
|
||||
* The cache of this manager
|
||||
* @type {Collection<Snowflake, Subscription>}
|
||||
* @name SubscriptionManager#cache
|
||||
*/
|
||||
|
||||
/**
|
||||
* Options used to fetch a subscription
|
||||
* @typedef {BaseFetchOptions} FetchSubscriptionOptions
|
||||
* @property {SKUResolvable} sku The SKU to fetch the subscription for
|
||||
* @property {Snowflake} subscriptionId The id of the subscription to fetch
|
||||
*/
|
||||
|
||||
/**
|
||||
* Options used to fetch subscriptions
|
||||
* @typedef {Object} FetchSubscriptionsOptions
|
||||
* @property {Snowflake} [after] Consider only subscriptions after this subscription id
|
||||
* @property {Snowflake} [before] Consider only subscriptions before this subscription id
|
||||
* @property {number} [limit] The maximum number of subscriptions to fetch
|
||||
* @property {SKUResolvable} sku The SKU to fetch subscriptions for
|
||||
* @property {UserResolvable} user The user to fetch entitlements for
|
||||
* <warn>If both `before` and `after` are provided, only `before` is respected</warn>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fetches subscriptions for this application
|
||||
* @param {FetchSubscriptionOptions|FetchSubscriptionsOptions} [options={}] Options for fetching the subscriptions
|
||||
* @returns {Promise<Subscription|Collection<Snowflake, Subscription>>}
|
||||
*/
|
||||
async fetch(options = {}) {
|
||||
if (typeof options !== 'object') throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'options', 'object', true);
|
||||
|
||||
const { after, before, cache, limit, sku, subscriptionId, user } = options;
|
||||
|
||||
const skuId = resolveSKUId(sku);
|
||||
|
||||
if (!skuId) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'sku', 'SKUResolvable');
|
||||
|
||||
if (subscriptionId) {
|
||||
const subscription = await this.client.rest.get(Routes.skuSubscription(skuId, subscriptionId));
|
||||
|
||||
return this._add(subscription, cache);
|
||||
}
|
||||
|
||||
const query = makeURLSearchParams({
|
||||
limit,
|
||||
user_id: this.client.users.resolveId(user) ?? undefined,
|
||||
sku_id: skuId,
|
||||
before,
|
||||
after,
|
||||
});
|
||||
|
||||
const subscriptions = await this.client.rest.get(Routes.skuSubscriptions(skuId), { query });
|
||||
|
||||
return subscriptions.reduce(
|
||||
(coll, subscription) => coll.set(subscription.id, this._add(subscription, cache)),
|
||||
new Collection(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
exports.SubscriptionManager = SubscriptionManager;
|
||||
@@ -9,6 +9,7 @@ const Application = require('./interfaces/Application');
|
||||
const ApplicationCommandManager = require('../managers/ApplicationCommandManager');
|
||||
const ApplicationEmojiManager = require('../managers/ApplicationEmojiManager');
|
||||
const { EntitlementManager } = require('../managers/EntitlementManager');
|
||||
const { SubscriptionManager } = require('../managers/SubscriptionManager');
|
||||
const ApplicationFlagsBitField = require('../util/ApplicationFlagsBitField');
|
||||
const { resolveImage } = require('../util/DataResolver');
|
||||
const PermissionsBitField = require('../util/PermissionsBitField');
|
||||
@@ -44,6 +45,12 @@ class ClientApplication extends Application {
|
||||
* @type {EntitlementManager}
|
||||
*/
|
||||
this.entitlements = new EntitlementManager(this.client);
|
||||
|
||||
/**
|
||||
* The subscription manager for this application
|
||||
* @type {SubscriptionManager}
|
||||
*/
|
||||
this.subscriptions = new SubscriptionManager(this.client);
|
||||
}
|
||||
|
||||
_patch(data) {
|
||||
|
||||
@@ -73,10 +73,9 @@ class Entitlement extends Base {
|
||||
if ('starts_at' in data) {
|
||||
/**
|
||||
* The timestamp at which this entitlement is valid
|
||||
* <info>This is only `null` for test entitlements</info>
|
||||
* @type {?number}
|
||||
*/
|
||||
this.startsTimestamp = Date.parse(data.starts_at);
|
||||
this.startsTimestamp = data.starts_at ? Date.parse(data.starts_at) : null;
|
||||
} else {
|
||||
this.startsTimestamp ??= null;
|
||||
}
|
||||
@@ -84,10 +83,9 @@ class Entitlement extends Base {
|
||||
if ('ends_at' in data) {
|
||||
/**
|
||||
* The timestamp at which this entitlement is no longer valid
|
||||
* <info>This is only `null` for test entitlements</info>
|
||||
* @type {?number}
|
||||
*/
|
||||
this.endsTimestamp = Date.parse(data.ends_at);
|
||||
this.endsTimestamp = data.ends_at ? Date.parse(data.ends_at) : null;
|
||||
} else {
|
||||
this.endsTimestamp ??= null;
|
||||
}
|
||||
@@ -114,7 +112,6 @@ class Entitlement extends Base {
|
||||
|
||||
/**
|
||||
* The start date at which this entitlement is valid
|
||||
* <info>This is only `null` for test entitlements</info>
|
||||
* @type {?Date}
|
||||
*/
|
||||
get startsAt() {
|
||||
@@ -123,7 +120,6 @@ class Entitlement extends Base {
|
||||
|
||||
/**
|
||||
* The end date at which this entitlement is no longer valid
|
||||
* <info>This is only `null` for test entitlements</info>
|
||||
* @type {?Date}
|
||||
*/
|
||||
get endsAt() {
|
||||
|
||||
109
packages/discord.js/src/structures/Subscription.js
Normal file
109
packages/discord.js/src/structures/Subscription.js
Normal file
@@ -0,0 +1,109 @@
|
||||
'use strict';
|
||||
|
||||
const Base = require('./Base');
|
||||
|
||||
/**
|
||||
* Represents a Subscription
|
||||
* @extends {Base}
|
||||
*/
|
||||
class Subscription extends Base {
|
||||
constructor(client, data) {
|
||||
super(client);
|
||||
|
||||
/**
|
||||
* The id of the subscription
|
||||
* @type {Snowflake}
|
||||
*/
|
||||
this.id = data.id;
|
||||
|
||||
/**
|
||||
* The id of the user who subscribed
|
||||
* @type {Snowflake}
|
||||
*/
|
||||
this.userId = data.user_id;
|
||||
|
||||
this._patch(data);
|
||||
}
|
||||
|
||||
_patch(data) {
|
||||
/**
|
||||
* The SKU ids subscribed to
|
||||
* @type {Snowflake[]}
|
||||
*/
|
||||
this.skuIds = data.sku_ids;
|
||||
|
||||
/**
|
||||
* The entitlement ids granted for this subscription
|
||||
* @type {Snowflake[]}
|
||||
*/
|
||||
this.entitlementIds = data.entitlement_ids;
|
||||
|
||||
/**
|
||||
* The timestamp the current subscription period will start at
|
||||
* @type {number}
|
||||
*/
|
||||
this.currentPeriodStartTimestamp = Date.parse(data.current_period_start);
|
||||
|
||||
/**
|
||||
* The timestamp the current subscription period will end at
|
||||
* @type {number}
|
||||
*/
|
||||
this.currentPeriodEndTimestamp = Date.parse(data.current_period_end);
|
||||
|
||||
/**
|
||||
* The current status of the subscription
|
||||
* @type {SubscriptionStatus}
|
||||
*/
|
||||
this.status = data.status;
|
||||
|
||||
if ('canceled_at' in data) {
|
||||
/**
|
||||
* The timestamp of when the subscription was canceled
|
||||
* @type {?number}
|
||||
*/
|
||||
this.canceledTimestamp = data.canceled_at ? Date.parse(data.canceled_at) : null;
|
||||
} else {
|
||||
this.canceledTimestamp ??= null;
|
||||
}
|
||||
|
||||
if ('country' in data) {
|
||||
/**
|
||||
* ISO 3166-1 alpha-2 country code of the payment source used to purchase the subscription.
|
||||
* Missing unless queried with a private OAuth scope.
|
||||
* @type {?string}
|
||||
*/
|
||||
this.country = data.country;
|
||||
} else {
|
||||
this.country ??= null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The time the subscription was canceled
|
||||
* @type {?Date}
|
||||
* @readonly
|
||||
*/
|
||||
get canceledAt() {
|
||||
return this.canceledTimestamp && new Date(this.canceledTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* The time the current subscription period will start at
|
||||
* @type {Date}
|
||||
* @readonly
|
||||
*/
|
||||
get currentPeriodStartAt() {
|
||||
return new Date(this.currentPeriodStartTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* The time the current subscription period will end at
|
||||
* @type {Date}
|
||||
* @readonly
|
||||
*/
|
||||
get currentPeriodEndAt() {
|
||||
return new Date(this.currentPeriodEndTimestamp);
|
||||
}
|
||||
}
|
||||
|
||||
exports.Subscription = Subscription;
|
||||
@@ -64,6 +64,9 @@
|
||||
* @property {string} StageInstanceCreate stageInstanceCreate
|
||||
* @property {string} StageInstanceDelete stageInstanceDelete
|
||||
* @property {string} StageInstanceUpdate stageInstanceUpdate
|
||||
* @property {string} SubscriptionCreate subscriptionCreate
|
||||
* @property {string} SubscriptionUpdate subscriptionUpdate
|
||||
* @property {string} SubscriptionDelete subscriptionDelete
|
||||
* @property {string} ThreadCreate threadCreate
|
||||
* @property {string} ThreadDelete threadDelete
|
||||
* @property {string} ThreadListSync threadListSync
|
||||
@@ -147,6 +150,9 @@ module.exports = {
|
||||
StageInstanceCreate: 'stageInstanceCreate',
|
||||
StageInstanceDelete: 'stageInstanceDelete',
|
||||
StageInstanceUpdate: 'stageInstanceUpdate',
|
||||
SubscriptionCreate: 'subscriptionCreate',
|
||||
SubscriptionUpdate: 'subscriptionUpdate',
|
||||
SubscriptionDelete: 'subscriptionDelete',
|
||||
ThreadCreate: 'threadCreate',
|
||||
ThreadDelete: 'threadDelete',
|
||||
ThreadListSync: 'threadListSync',
|
||||
|
||||
45
packages/discord.js/typings/index.d.ts
vendored
45
packages/discord.js/typings/index.d.ts
vendored
@@ -164,6 +164,8 @@ import {
|
||||
GuildScheduledEventRecurrenceRuleWeekday,
|
||||
GuildScheduledEventRecurrenceRuleMonth,
|
||||
GuildScheduledEventRecurrenceRuleFrequency,
|
||||
APISubscription,
|
||||
SubscriptionStatus,
|
||||
GatewaySendPayload,
|
||||
GatewayDispatchPayload,
|
||||
VoiceChannelEffectSendAnimationType,
|
||||
@@ -1053,6 +1055,7 @@ export class ClientApplication extends Application {
|
||||
public commands: ApplicationCommandManager;
|
||||
public emojis: ApplicationEmojiManager;
|
||||
public entitlements: EntitlementManager;
|
||||
public subscriptions: SubscriptionManager;
|
||||
public guildId: Snowflake | null;
|
||||
public get guild(): Guild | null;
|
||||
public cover: string | null;
|
||||
@@ -3078,6 +3081,22 @@ export class SKUFlagsBitField extends BitField<SKUFlagsString> {
|
||||
public static resolve(bit?: BitFieldResolvable<SKUFlagsString, number>): number;
|
||||
}
|
||||
|
||||
export class Subscription extends Base {
|
||||
private constructor(client: Client<true>, data: APISubscription);
|
||||
public id: Snowflake;
|
||||
public userId: Snowflake;
|
||||
public skuIds: Snowflake[];
|
||||
public entitlementIds: Snowflake[];
|
||||
public currentPeriodStartTimestamp: number;
|
||||
public currentPeriodEndTimestamp: number;
|
||||
public status: SubscriptionStatus;
|
||||
public canceledTimestamp: number | null;
|
||||
public country: string | null;
|
||||
public get canceledAt(): Date | null;
|
||||
public get currentPeriodStartAt(): Date;
|
||||
public get currentPeriodEndAt(): Date;
|
||||
}
|
||||
|
||||
export class StageChannel extends BaseGuildVoiceChannel {
|
||||
public get stageInstance(): StageInstance | null;
|
||||
public topic: string | null;
|
||||
@@ -4069,6 +4088,7 @@ export class ChannelManager extends CachedManager<Snowflake, Channel, ChannelRes
|
||||
|
||||
export type EntitlementResolvable = Snowflake | Entitlement;
|
||||
export type SKUResolvable = Snowflake | SKU;
|
||||
export type SubscriptionResolvable = Snowflake | Subscription;
|
||||
|
||||
export interface GuildEntitlementCreateOptions {
|
||||
sku: SKUResolvable;
|
||||
@@ -4099,6 +4119,25 @@ export class EntitlementManager extends CachedManager<Snowflake, Entitlement, En
|
||||
public consume(entitlementId: Snowflake): Promise<void>;
|
||||
}
|
||||
|
||||
export interface FetchSubscriptionOptions extends BaseFetchOptions {
|
||||
sku: SKUResolvable;
|
||||
subscriptionId: Snowflake;
|
||||
}
|
||||
|
||||
export interface FetchSubscriptionsOptions {
|
||||
after?: Snowflake;
|
||||
before?: Snowflake;
|
||||
limit?: number;
|
||||
sku: SKUResolvable;
|
||||
user: UserResolvable;
|
||||
}
|
||||
|
||||
export class SubscriptionManager extends CachedManager<Snowflake, Subscription, SubscriptionResolvable> {
|
||||
private constructor(client: Client<true>, iterable?: Iterable<APISubscription>);
|
||||
public fetch(options: FetchSubscriptionOptions): Promise<Subscription>;
|
||||
public fetch(options: FetchSubscriptionsOptions): Promise<Collection<Snowflake, Subscription>>;
|
||||
}
|
||||
|
||||
export interface FetchGuildApplicationCommandFetchOptions extends Omit<FetchApplicationCommandOptions, 'guildId'> {}
|
||||
|
||||
export class GuildApplicationCommandManager extends ApplicationCommandManager<ApplicationCommand, {}, Guild> {
|
||||
@@ -5193,6 +5232,9 @@ export interface ClientEvents {
|
||||
stickerCreate: [sticker: Sticker];
|
||||
stickerDelete: [sticker: Sticker];
|
||||
stickerUpdate: [oldSticker: Sticker, newSticker: Sticker];
|
||||
subscriptionCreate: [subscription: Subscription];
|
||||
subscriptionDelete: [subscription: Subscription];
|
||||
subscriptionUpdate: [oldSubscription: Subscription | null, newSubscription: Subscription];
|
||||
guildScheduledEventCreate: [guildScheduledEvent: GuildScheduledEvent];
|
||||
guildScheduledEventUpdate: [
|
||||
oldGuildScheduledEvent: GuildScheduledEvent | PartialGuildScheduledEvent | null,
|
||||
@@ -5392,6 +5434,9 @@ export enum Events {
|
||||
StageInstanceCreate = 'stageInstanceCreate',
|
||||
StageInstanceUpdate = 'stageInstanceUpdate',
|
||||
StageInstanceDelete = 'stageInstanceDelete',
|
||||
SubscriptionCreate = 'subscriptionCreate',
|
||||
SubscriptionUpdate = 'subscriptionUpdate',
|
||||
SubscriptionDelete = 'subscriptionDelete',
|
||||
GuildStickerCreate = 'stickerCreate',
|
||||
GuildStickerDelete = 'stickerDelete',
|
||||
GuildStickerUpdate = 'stickerUpdate',
|
||||
|
||||
Reference in New Issue
Block a user