From 4d984e28ca34d400e33220fd2369a989656c903e Mon Sep 17 00:00:00 2001 From: Areodot Date: Sat, 28 Sep 2024 21:36:26 +0000 Subject: [PATCH 01/15] Added support for premium apps --- esm.mjs | 2 + index.d.ts | 70 ++++++++++++++++++++++++++++++++++- index.js | 2 + lib/Client.js | 64 ++++++++++++++++++++++++++++++++ lib/Constants.d.ts | 28 ++++++++++++++ lib/Constants.js | 32 ++++++++++++++++ lib/gateway/Shard.js | 28 ++++++++++++++ lib/rest/Endpoints.js | 4 ++ lib/structures/Entitlement.js | 47 +++++++++++++++++++++++ lib/structures/SKU.js | 59 +++++++++++++++++++++++++++++ 10 files changed, 334 insertions(+), 2 deletions(-) create mode 100644 lib/structures/Entitlement.js create mode 100644 lib/structures/SKU.js diff --git a/esm.mjs b/esm.mjs index d475feaf6..229b0684f 100644 --- a/esm.mjs +++ b/esm.mjs @@ -21,6 +21,7 @@ export const { DiscordHTTPError, DiscordRESTError, DMChannel, + Entitlement, ExtendedUser, ForumChannel, GroupChannel, @@ -46,6 +47,7 @@ export const { PublicThreadChannel, RequestHandler, Role, + SKU, SequentialBucket, Shard, SharedStream, diff --git a/index.d.ts b/index.d.ts index 72c76a272..e7b3fe196 100644 --- a/index.d.ts +++ b/index.d.ts @@ -153,7 +153,7 @@ declare namespace Eris { // Message type ActionRowComponents = Button | SelectMenu; - type Button = InteractionButton | URLButton; + type Button = InteractionButton | URLButton | PremiumButton; type ButtonStyles = Constants["ButtonStyles"][keyof Constants["ButtonStyles"]]; type Component = ActionRow | ActionRowComponents; type ImageFormat = Constants["ImageFormats"][number]; @@ -196,6 +196,11 @@ declare namespace Eris { type WebhookPayloadEdit = Pick; type WebhookTypes = Constants["WebhookTypes"][keyof Constants["WebhookTypes"]]; + // Subscriptions + type EntitlementTypes = Constants["EntitlementTypes"][keyof Constants["EntitlementTypes"]]; + type EntitlementOwnerTypes = Constants["EntitlementOwnerTypes"][keyof Constants["EntitlementOwnerTypes"]]; + type SKUTypes = Constants["SKUTypes"][keyof Constants["SKUTypes"]]; + // INTERFACES // Internals type JSONCache = Record; @@ -944,6 +949,9 @@ declare namespace Eris { voiceStateUpdate: [member: Member, oldState: OldVoiceState]; warn: [message: string, id?: number]; webhooksUpdate: [data: WebhookData]; + entitlementCreate: [entitlement: Entitlement]; + entitlementUpdate: [entitlement: Entitlement]; + entitlementDelete: [entitlement: Entitlement]; } interface ClientEvents extends EventListeners { shardDisconnect: [err: Error | undefined, id: number]; @@ -1297,6 +1305,32 @@ declare namespace Eris { reason?: string; } + // Subscriptions + interface CreateTestEntitlementOptions { + skuID: string; + ownerID: string; + ownerType: EntitlementOwnerTypes; + } + + interface GetSKUEntitlementsOptions { + userID?: string; + before?: number; + after?: number; + limit?: number; + guildID?: string; + excludeEnded?: boolean; + } + + interface GetEntitlementsOptions { + userID?: string; + skuIDs?: string[]; + before?: number; + after?: number; + limit?: number; + guildID?: string; + excludeEnded?: boolean; + } + // Interaction interface AutocompleteInteractionData { id: string; @@ -1521,7 +1555,7 @@ declare namespace Eris { } interface InteractionButton extends ButtonBase { custom_id: string; - style: Exclude; + style: Exclude; } interface MessageActivity { party_id?: string; @@ -1655,6 +1689,10 @@ declare namespace Eris { style: Constants["ButtonStyles"]["LINK"]; url: string; } + interface PremiumButton extends ButtonBase { + style: Constants["ButtonStyles"]["PREMIUM"]; + sku_id: string; + } // Presence interface Activity extends ActivityPartial { @@ -2103,7 +2141,9 @@ declare namespace Eris { createThreadWithMessage(channelID: string, messageID: string, options: CreateThreadOptions): Promise; /** @deprecated */ createThreadWithoutMessage(channelID: string, options: CreateThreadWithoutMessageOptions): Promise; + createTestEntitlement(options: CreateTestEntitlementOptions): Promise; crosspostMessage(channelID: string, messageID: string): Promise; + consumeEntitlement(entitlementID: string): Promise; deleteAutoModerationRule(guildID: string, ruleID: string, reason?: string): Promise; deleteChannel(channelID: string, reason?: string): Promise; deleteChannelPermission(channelID: string, overwriteID: string, reason?: string): Promise; @@ -2121,6 +2161,7 @@ declare namespace Eris { deleteMessages(channelID: string, messageIDs: string[], reason?: string): Promise; deleteRole(guildID: string, roleID: string, reason?: string): Promise; deleteStageInstance(channelID: string): Promise; + deleteTestEntitlement(entitlementID: string): Promise; deleteWebhook(webhookID: string, token?: string, reason?: string): Promise; deleteWebhookMessage(webhookID: string, token: string, messageID: string): Promise; disconnect(options: { reconnect?: boolean | "auto" }): void; @@ -2207,6 +2248,7 @@ declare namespace Eris { getDiscoveryCategories(): Promise; getDMChannel(userID: string): Promise; getEmojiGuild(emojiID: string): Promise; + getEntitlements(options?: GetEntitlementsOptions): Promise; getGateway(): Promise<{ url: string }>; getGuildAuditLog(guildID: string, options?: GetGuildAuditLogOptions): Promise; /** @deprecated */ @@ -2269,6 +2311,7 @@ declare namespace Eris { getRoleConnectionMetadataRecords(): Promise; getSelf(): Promise; getStageInstance(channelID: string): Promise; + getSKUs(): Promise; getThreadMember(channelID: string, userID: string, withMember?: boolean): Promise; getThreadMembers(channelID: string, options?: GetThreadMembersOptions): Promise; getVoiceRegions(guildID?: string): Promise; @@ -2443,6 +2486,29 @@ declare namespace Eris { unsendMessage(messageID: string): Promise; } + export class Entitlement extends Base { + applicationID: string; + consumed: boolean; + deleted: boolean; + endsAt?: number | null; + guildID?: string; + skuID: string; + startsAt?: number | null; + type: EntitlementTypes; + userID?: string; + consume(): Promise; + } + + export class SKU extends Base { + applicationID: string; + flags: number; + name: string; + slug: string; + type: SKUTypes; + createTestEntitlement(ownerID: string, ownerType: EntitlementOwnerTypes): Promise; + getEntitlements(options?: GetSKUEntitlementsOptions): Promise; + } + export class ExtendedUser extends User { email: string; mfaEnabled: boolean; diff --git a/index.js b/index.js index d8a1b6f8e..eaa667cb4 100644 --- a/index.js +++ b/index.js @@ -22,6 +22,7 @@ Eris.Constants = require("./lib/Constants"); Eris.DiscordHTTPError = require("./lib/errors/DiscordHTTPError"); Eris.DiscordRESTError = require("./lib/errors/DiscordRESTError"); Eris.DMChannel = require("./lib/structures/DMChannel.js"); +Eris.Entitlement = require("./lib/structures/Entitlement.js"); Eris.ExtendedUser = require("./lib/structures/ExtendedUser"); Eris.ForumChannel = require("./lib/structures/ForumChannel"); Eris.GroupChannel = require("./lib/structures/GroupChannel"); @@ -48,6 +49,7 @@ Eris.PrivateThreadChannel = require("./lib/structures/PrivateThreadChannel"); Eris.PublicThreadChannel = require("./lib/structures/PublicThreadChannel"); Eris.RequestHandler = require("./lib/rest/RequestHandler"); Eris.Role = require("./lib/structures/Role"); +Eris.SKU = require("./lib/structures/SKU.js"); Eris.SequentialBucket = require("./lib/util/SequentialBucket"); Eris.Shard = require("./lib/gateway/Shard"); Eris.SharedStream = require("./lib/voice/SharedStream"); diff --git a/lib/Client.js b/lib/Client.js index aef51e565..cc3c67d8a 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -28,6 +28,8 @@ const UnavailableGuild = require("./structures/UnavailableGuild"); const User = require("./structures/User"); const VoiceConnectionManager = require("./voice/VoiceConnectionManager"); const StageInstance = require("./structures/StageInstance"); +const Entitlement = require("./structures/Entitlement"); +const SKU = require("./structures/SKU"); let EventEmitter; try { @@ -1146,6 +1148,18 @@ class Client extends EventEmitter { }).then((channel) => Channel.from(channel, this)); } + /** + * Create a test entitlement + * @arg {Object} options The options for creating a test entitlement + * @arg {String} options.skuID The ID of the SKU to grant the entitlement to + * @arg {String} options.ownerID The ID of the owner to create the entitlement for + * @arg {Number} options.ownerType The type of the owner to create the entitlement for + * @returns {Promise} + */ + createTestEntitlement(options) { + return this.requestHandler.request("POST", Endpoints.ENTITLEMENTS(this.application.id), true, options).then((entitlement) => new Entitlement(entitlement, this)); + } + /** * Crosspost (publish) a message to subscribed channels * @arg {String} channelID The ID of the NewsChannel @@ -1156,6 +1170,15 @@ class Client extends EventEmitter { return this.requestHandler.request("POST", Endpoints.CHANNEL_CROSSPOST(channelID, messageID), true).then((message) => new Message(message, this)); } + /** + * For One-Time Purchase consumable SKUs, marks a given entitlement for the user as consumed + * @arg {String} entitlementID The ID of the the entitlement + * @returns {Promise} + */ + consumeEntitlement(entitlementID) { + return this.requestHandler.request("POST", Endpoints.ENTITLEMENT_CONSUME(this.application.id, entitlementID), true); + } + /** * Delete an auto moderation rule * @arg {String} guildID The guildID to delete the rule from @@ -1378,6 +1401,15 @@ class Client extends EventEmitter { return this.requestHandler.request("DELETE", Endpoints.STAGE_INSTANCE(channelID), true); } + /** + * Delete a test entitlement + * @arg {String} entitlementID The ID of the entitlement + * @returns {Promise} + */ + deleteTestEntitlement(entitlementID) { + return this.requestHandler.request("DELETE", Endpoints.ENTITLEMENT(this.application.id, entitlementID), true); + } + /** * Delete a webhook * @arg {String} webhookID The ID of the webhook @@ -2541,6 +2573,30 @@ class Client extends EventEmitter { return this.requestHandler.request("GET", Endpoints.CUSTOM_EMOJI_GUILD(emojiID), true).then((result) => new Guild(result, this)); } + /** + * Get a list of entitlements for this SKU + * @arg {Object} [options] The options for the request + * @arg {String} [options.userID] The user ID to look up entitlements for + * @arg {Array} [options.skuIDs] An optional list of SKU IDs to check entitlements for + * @arg {Number} [options.before] Retrieve entitlements before this entitlement ID + * @arg {Number} [options.after] Retrieve entitlements after this entitlement ID + * @arg {Number} [options.limit=100] The number of entitlements to return, 1-100, default 100 + * @arg {String} [options.guildID] The guild ID to look up entitlements for + * @arg {Boolean} [options.excludeEnded] Whether or not ended entitlements should be omitted + * @returns {Promise>} + */ + getEntitlements(options = {}) { + return this.requestHandler.request("GET", Endpoints.ENTITLEMENTS(this.application.id), true, { + user_id: options.userID, + sku_ids: options.skuIDs, + before: options.before, + after: options.after, + limit: options.limit, + guild_id: options.guildID, + exclude_ended: options.excludeEnded, + }).then((entitlements) => entitlements.map((entitlement) => new Entitlement(entitlement, this))); + } + /** * Get info on connecting to the Discord gateway * @returns {Promise} Resolves with an object containing gateway connection info @@ -3292,6 +3348,14 @@ class Client extends EventEmitter { return this.requestHandler.request("GET", Endpoints.STAGE_INSTANCE(channelID), true).then((instance) => new StageInstance(instance, this)); } + /** + * Get a list of SKUs for a given application + * @returns {Promise>} + */ + getSKUs() { + return this.requestHandler.request("GET", Endpoints.SKUS(this.application.id), true); + } + /** * Get a member that is part of a thread channel * @arg {String} channelID The ID of the thread channel diff --git a/lib/Constants.d.ts b/lib/Constants.d.ts index 955ed5559..5fbb11ae0 100644 --- a/lib/Constants.d.ts +++ b/lib/Constants.d.ts @@ -140,6 +140,7 @@ export default interface Constants { SUCCESS: 3; DANGER: 4; LINK: 5; + PREMIUM: 6; }; ChannelFlags: { PINNED: 1; @@ -183,6 +184,20 @@ export default interface Constants { LATEST_ACTIVITY: 0; CREATION_DATE: 1; }; + EntitlementTypes: { + PURCHASE: 1; + PREMIUM_SUBSCRIPTION: 2; + DEVELOPER_GIFT: 3; + TEST_MODE_PURCHASE: 4; + FREE_PURCHASE: 5; + USER_GIFT: 6; + PREMIUM_PURCHASE: 7; + APPLICATION_SUBSCRIPTION: 8; + }; + EntitlementOwnerTypes: { + GUILD: 1; + USER: 2; + }; ExplicitContentFilterLevels: { DISABLED: 0; MEMBERS_WITHOUT_ROLES: 1; @@ -583,6 +598,19 @@ export default interface Constants { NORMAL: 0; BURST: 1; }; + SKUTypes: { + DURABLE_PRIMARY: 1; + DURABLE: 2; + CONSUMABLE: 3; + BUNDLE: 4; + SUBSCRIPTION: 5; + SUBSCRIPTION_GROUP: 6; + }; + SKUFlags: { + AVAILABLE: 4; + GUILD_SUBSCRIPTION: 128; + USER_SUBSCRIPTION: 256; + }; StageInstancePrivacyLevel: { PUBLIC: 1; GUILD_ONLY: 2; diff --git a/lib/Constants.js b/lib/Constants.js index 471d8e6e3..716b8d88e 100644 --- a/lib/Constants.js +++ b/lib/Constants.js @@ -165,6 +165,7 @@ module.exports.ButtonStyles = { SUCCESS: 3, DANGER: 4, LINK: 5, + PREMIUM: 6, }; module.exports.ChannelFlags = { @@ -220,6 +221,22 @@ module.exports.SortOrderTypes = { CREATION_DATE: 1, }; +module.exports.EntitlementTypes = { + PURCHASE: 1, + PREMIUM_SUBSCRIPTION: 2, + DEVELOPER_GIFT: 3, + TEST_MODE_PURCHASE: 4, + FREE_PURCHASE: 5, + USER_GIFT: 6, + PREMIUM_PURCHASE: 7, + APPLICATION_SUBSCRIPTION: 8, +} + +module.exports.EntitlementOwnerTypes = { + GUILD: 1, + USER: 2, +} + module.exports.ExplicitContentFilterLevels = { DISABLED: 0, MEMBERS_WITHOUT_ROLES: 1, @@ -734,6 +751,21 @@ module.exports.RoleFlags = { IN_PROMPT: 1 << 0, }; +module.exports.SKUTypes = { + DURABLE_PRIMARY: 1, + DURABLE: 2, + CONSUMABLE: 3, + BUNDLE: 4, + SUBSCRIPTION: 5, + SUBSCRIPTION_GROUP: 6, +} + +module.exports.SKUFlags = { + AVAILABLE: 1 << 2, + GUILD_SUBSCRIPTION: 1 << 7, + USER_SUBSCRIPTION: 1 << 8, +} + module.exports.StageInstancePrivacyLevel = { PUBLIC: 1, GUILD_ONLY: 2, diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 795af9b12..7e4fe8285 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -18,6 +18,7 @@ const ThreadChannel = require("../structures/ThreadChannel"); const StageInstance = require("../structures/StageInstance"); const GuildScheduledEvent = require("../structures/GuildScheduledEvent"); const GuildAuditLogEntry = require("../structures/GuildAuditLogEntry"); +const Entitlement = require("../structures/Entitlement"); const WebSocket = typeof window !== "undefined" ? require("../util/BrowserWebSocket") : require("ws"); @@ -2462,6 +2463,33 @@ class Shard extends EventEmitter { this.emit("guildIntegrationsUpdate", this.client.guilds.get(packet.d.guild_id) || { id: packet.d.guild_id }); break; } + case "ENTITLEMENT_CREATE ": { + /** + * Fired when an entitlement is created + * @event Client#entitlementCreate + * @prop {Entitlement} entitlement The new entitlement + */ + this.emit("entitlementCreate", new Entitlement(packet.d, this.client)); + break; + } + case "ENTITLEMENT_UPDATE": { + /** + * Fired when an entitlement is updated + * @event Client#entitlementUpdate + * @prop {Entitlement} entitlement The updated entitlement + */ + this.emit("entitlementUpdate", new Entitlement(packet.d, this.client)); + break; + } + case "ENTITLEMENT_DELETE": { + /** + * Fired when an entitlement is deleted + * @event Client#entitlementDelete + * @prop {Entitlement} entitlement The deleted entitlement + */ + this.emit("entitlementDelete", new Entitlement(packet.d, this.client)); + break; + } default: { /** * Fired when the shard encounters an unknown packet diff --git a/lib/rest/Endpoints.js b/lib/rest/Endpoints.js index 4fc8ff383..c9f184248 100644 --- a/lib/rest/Endpoints.js +++ b/lib/rest/Endpoints.js @@ -33,6 +33,9 @@ module.exports.CHANNELS = module.exports.CUSTOM_EMOJI_GUILD = (emojiID) => `/emojis/${emojiID}/guild`; module.exports.DISCOVERY_CATEGORIES = "/discovery/categories"; module.exports.DISCOVERY_VALIDATION = "/discovery/valid-term"; +module.exports.ENTITLEMENT = (applicationID, entitlementID) => `/applications/${applicationID}/entitlements/${entitlementID}`; +module.exports.ENTITLEMENT_CONSUME = (applicationID, entitlementID) => `/applications/${applicationID}/entitlements/${entitlementID}/consume`; +module.exports.ENTITLEMENTS = (applicationID) => `/applications/${applicationID}/entitlements`; module.exports.GATEWAY = "/gateway"; module.exports.GATEWAY_BOT = "/gateway/bot"; module.exports.GUILD = (guildID) => `/guilds/${guildID}`; @@ -87,6 +90,7 @@ module.exports.ORIGINAL_INTERACTION_RESPONSE = (appID, interactToken) module.exports.POLL_ANSWER_VOTERS = (channelID, msgID, answerID) => `/channels/${channelID}/polls/${msgID}/answers/${answerID}`; module.exports.POLL_END = (channelID, msgID) => `/channels/${channelID}/polls/${msgID}/expire`; module.exports.ROLE_CONNECTION_METADATA_RECORDS = (appID) => `/applications/${appID}/role-connections/metadata`; +module.exports.SKUS = (applicationID) => `/applications/${applicationID}/skus`; module.exports.STAGE_INSTANCE = (channelID) => `/stage-instances/${channelID}`; module.exports.STAGE_INSTANCES = "/stage-instances"; module.exports.STICKER = (stickerID) => `/stickers/${stickerID}`; diff --git a/lib/structures/Entitlement.js b/lib/structures/Entitlement.js new file mode 100644 index 000000000..13d7f7f68 --- /dev/null +++ b/lib/structures/Entitlement.js @@ -0,0 +1,47 @@ +"use strict"; + +const Base = require("./Base.js"); + +/** + * Represents an entitlement + * @prop {String} applicationID The ID of the parent application + * @prop {Boolean} consumed For consumable items, whether or not the entitlement has been consumed + * @prop {Boolean} deleted Whether or not the entitlement was deleted + * @prop {Number?} endsAt The date at which the entitlement is no longer valid. Not present when using test entitlements + * @prop {String?} guildID The ID of the guild that is granted access to the entitlement's sku + * @prop {String} skuID The ID of the SKU + * @prop {Number?} startsAt The start date at which the entitlement is valid. Not present when using test entitlements + * @prop {Number} type The type of entitlement + * @prop {String?} userID The ID of the user that is granted access to the entitlement's sku + */ +class Entitlement extends Base { + constructor(data, client) { + super(data.id); + this._client = client; + + this.applicationID = data.application_id; + this.consumed = data.consumed; + this.deleted = data.deleted; + this.guildID = data.guild_id; + this.skuID = data.sku_id; + this.type = data.type; + this.userID = data.user_id; + + if (data.ends_at !== undefined) { + this.endsAt = data.ends_at ? Date.parse(data.ends_at) : null; + } + if (data.starts_at !== undefined) { + this.startsAt = data.starts_at ? Date.parse(data.starts_at) : null; + } + } + + /** + * Mark's this entitlement as consumed + * @returns {Promise} + */ + consume() { + return this._client.consumeEntitlement.call(this._client, this.id); + } +} + +module.exports = Entitlement; \ No newline at end of file diff --git a/lib/structures/SKU.js b/lib/structures/SKU.js new file mode 100644 index 000000000..d79a117c6 --- /dev/null +++ b/lib/structures/SKU.js @@ -0,0 +1,59 @@ +"use strict"; + +const Base = require("./Base.js"); +const Entitlement = require("./Entitlement.js"); + +/** + * Represents an SKU + * @prop {String} applicationID The ID of the parent application + * @prop {Number} flags The SKU flags combined as a bitfield + * @prop {String} name The name of the SKU + * @prop {String} slug System-generated URL slug based on the SKU's name + * @prop {Number} type The type of SKU + */ +class SKU extends Base { + constructor(data, client) { + super(data.id); + this._client = client; + + this.applicationID = data.application_id; + this.flags = data.flags; + this.name = data.name; + this.slug = data.slug; + this.type = data.type; + } + + /** + * Create a test entitlement for this SKU + * @arg {String} ownerID The ID of the owner to create the entitlement for + * @arg {Number} ownerType The type of the owner to create the entitlement for + * @returns {Promise} + */ + createTestEntitlement(ownerID, ownerType) { + return this._client.createTestEntitlement.call(this._client, { + ownerID, + ownerType, + skuID: this.id + }); + } + + /** + * Get a list of entitlements for this SKU + * @arg {Object} [options] The options for the request + * @arg {String} [options.userID] The user ID to look up entitlements for + * @arg {Number} [options.before] Retrieve entitlements before this entitlement ID + * @arg {Number} [options.after] Retrieve entitlements after this entitlement ID + * @arg {Number} [options.limit=100] The number of entitlements to return, 1-100, default 100 + * @arg {String} [options.guildID] The guild ID to look up entitlements for + * @arg {Boolean} [options.excludeEnded] Whether or not ended entitlements should be omitted + * @returns {Promise>} + */ + getEntitlements(options = {}) { + return this._client.getEntitlements.call(this._client, { + skuIDs: [this.id], + ...options + }); + } +} + +module.exports = SKU; \ No newline at end of file From e9fc828385e546e17eeaceae5506fcea5a67b89d Mon Sep 17 00:00:00 2001 From: Areodot Date: Sat, 28 Sep 2024 22:57:27 +0000 Subject: [PATCH 02/15] Unnecessary type --- index.d.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/index.d.ts b/index.d.ts index e7b3fe196..64dc967ba 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1312,15 +1312,6 @@ declare namespace Eris { ownerType: EntitlementOwnerTypes; } - interface GetSKUEntitlementsOptions { - userID?: string; - before?: number; - after?: number; - limit?: number; - guildID?: string; - excludeEnded?: boolean; - } - interface GetEntitlementsOptions { userID?: string; skuIDs?: string[]; @@ -2506,7 +2497,7 @@ declare namespace Eris { slug: string; type: SKUTypes; createTestEntitlement(ownerID: string, ownerType: EntitlementOwnerTypes): Promise; - getEntitlements(options?: GetSKUEntitlementsOptions): Promise; + getEntitlements(options?: Omit): Promise; } export class ExtendedUser extends User { From c572302d9611f43651aa60489d9bf7382b619e81 Mon Sep 17 00:00:00 2001 From: Areodot Date: Sat, 28 Sep 2024 22:58:36 +0000 Subject: [PATCH 03/15] Fixed a comment --- lib/Client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Client.js b/lib/Client.js index cc3c67d8a..580346edc 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -2574,7 +2574,7 @@ class Client extends EventEmitter { } /** - * Get a list of entitlements for this SKU + * Get a list of entitlements for this application * @arg {Object} [options] The options for the request * @arg {String} [options.userID] The user ID to look up entitlements for * @arg {Array} [options.skuIDs] An optional list of SKU IDs to check entitlements for From 3ee28b96e0cbd9f51c7bd192901eb2b7e756d1a1 Mon Sep 17 00:00:00 2001 From: Areodot Date: Sat, 28 Sep 2024 23:03:56 +0000 Subject: [PATCH 04/15] Added createTestEntitlement and getEntitlements to guild and user --- index.d.ts | 3 +++ lib/structures/Guild.js | 34 +++++++++++++++++++++++++++++++++- lib/structures/User.js | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index 64dc967ba..435f0d3ed 100644 --- a/index.d.ts +++ b/index.d.ts @@ -2611,6 +2611,7 @@ declare namespace Eris { createRole(options: Role, reason?: string): Promise; createScheduledEvent(event: GuildScheduledEventOptions, reason?: string): Promise>; createSticker(options: CreateStickerOptions, reason?: string): Promise; + createTestEntitlement(skuID: string): Promise; createTemplate(name: string, description?: string | null): Promise; delete(): Promise; deleteAutoModerationRule(ruleID: string, reason?: string): Promise; @@ -2660,6 +2661,7 @@ declare namespace Eris { getCommandPermissions(): Promise; getCommands(): Promise[]>; getDiscovery(): Promise; + getEntitlements(options?: Omit): Promise; /** @deprecated */ getEmbed(): Promise; getIntegrations(): Promise; @@ -3436,6 +3438,7 @@ declare namespace Eris { dynamicAvatarURL(format?: ImageFormat, size?: number): string; dynamicBannerURL(format?: ImageFormat, size?: number): string | null; getDMChannel(): Promise; + getEntitlements(options?: Omit): Promise; } export class VoiceChannel extends GuildTextableChannel implements Invitable, Permissionable { diff --git a/lib/structures/Guild.js b/lib/structures/Guild.js index ec375d38b..1d1894cd2 100644 --- a/lib/structures/Guild.js +++ b/lib/structures/Guild.js @@ -10,9 +10,10 @@ const Role = require("./Role"); const VoiceState = require("./VoiceState"); const Permission = require("./Permission"); const GuildScheduledEvent = require("./GuildScheduledEvent"); -const { Permissions } = require("../Constants"); +const { Permissions, EntitlementOwnerTypes } = require("../Constants"); const StageInstance = require("./StageInstance"); const ThreadChannel = require("./ThreadChannel"); +const Entitlement = require("./Entitlement"); /** * Represents a guild @@ -556,6 +557,19 @@ class Guild extends Base { return this._client.createGuildSticker.call(this._client, this.id, options, reason); } + /** + * Create a test entitlement for this guild + * @arg {String} skuID The ID of the SKU to grant the entitlement to + * @returns {Promise} + */ + createTestEntitlement(skuID) { + return this._client.createTestEntitlement.call(this._client, this.id, { + skuID, + ownerID: this.id, + ownerType: EntitlementOwnerTypes.GUILD, + }) + } + /** * Create a template for this guild * @arg {String} name The name of the template @@ -1116,6 +1130,24 @@ class Guild extends Base { return this._client.getGuildDiscovery.call(this._client, this.id); } + /** + * Get a list of entitlements for this guild + * @arg {Object} [options] The options for the request + * @arg {String} [options.userID] The user ID to look up entitlements for + * @arg {Array} [options.skuIDs] An optional list of SKU IDs to check entitlements for + * @arg {Number} [options.before] Retrieve entitlements before this entitlement ID + * @arg {Number} [options.after] Retrieve entitlements after this entitlement ID + * @arg {Number} [options.limit=100] The number of entitlements to return, 1-100, default 100 + * @arg {Boolean} [options.excludeEnded] Whether or not ended entitlements should be omitted + * @returns {Promise>} + */ + getEntitlements(options = {}) { + return this._client.getEntitlements.call(this._client, this.id, { + guildID: this.id, + ...options + }) + } + /** * Get the all of a guild's application command permissions * @returns {Promise>} Resolves with an array of guild application command permissions objects. diff --git a/lib/structures/User.js b/lib/structures/User.js index 524f30b20..cbc1360fb 100644 --- a/lib/structures/User.js +++ b/lib/structures/User.js @@ -2,6 +2,8 @@ const Base = require("./Base"); const Endpoints = require("../rest/Endpoints"); +const { EntitlementOwnerTypes } = require("../Constants"); +const Entitlement = require("../structures/Entitlement"); /** * Represents a user @@ -142,6 +144,19 @@ class User extends Base { return this._client._formatImage(Endpoints.BANNER(this.id, this.banner), format, size); } + /** + * Create a test entitlement for this user + * @arg {String} skuID The ID of the SKU to grant the entitlement to + * @returns {Promise} + */ + createTestEntitlement(skuID) { + return this._client.createTestEntitlement.call(this._client, this.id, { + skuID, + ownerID: this.id, + ownerType: EntitlementOwnerTypes.USER, + }) + } + /** * Get a DM channel with the user, or create one if it does not exist * @returns {Promise} @@ -150,6 +165,24 @@ class User extends Base { return this._client.getDMChannel.call(this._client, this.id); } + /** + * Get a list of entitlements for this user + * @arg {Object} [options] The options for the request + * @arg {Array} [options.skuIDs] An optional list of SKU IDs to check entitlements for + * @arg {Number} [options.before] Retrieve entitlements before this entitlement ID + * @arg {Number} [options.after] Retrieve entitlements after this entitlement ID + * @arg {Number} [options.limit=100] The number of entitlements to return, 1-100, default 100 + * @arg {String} [options.guildID] The guild ID to look up entitlements for + * @arg {Boolean} [options.excludeEnded] Whether or not ended entitlements should be omitted + * @returns {Promise>} + */ + getEntitlements(options = {}) { + return this._client.getEntitlements.call(this._client, this.id, { + userID: this.id, + ...options + }) + } + toJSON(props = []) { return super.toJSON([ "accentColor", From 2adf700a3745666331dc1e3d3973200847da5c28 Mon Sep 17 00:00:00 2001 From: Areodot Date: Sat, 28 Sep 2024 23:08:39 +0000 Subject: [PATCH 05/15] Forgot to add createTestEntitlement to the user type --- index.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/index.d.ts b/index.d.ts index 435f0d3ed..c4ce29ce7 100644 --- a/index.d.ts +++ b/index.d.ts @@ -3437,6 +3437,7 @@ declare namespace Eris { constructor(data: BaseData, client: Client); dynamicAvatarURL(format?: ImageFormat, size?: number): string; dynamicBannerURL(format?: ImageFormat, size?: number): string | null; + createTestEntitlement(skuID: string): Promise; getDMChannel(): Promise; getEntitlements(options?: Omit): Promise; } From 5aba560a12c251c1409376823da2f11fd2fb9e59 Mon Sep 17 00:00:00 2001 From: Areodot Date: Sun, 29 Sep 2024 17:21:46 +0000 Subject: [PATCH 06/15] Added a more accurate type for sku flags --- index.d.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index c4ce29ce7..31847849e 100644 --- a/index.d.ts +++ b/index.d.ts @@ -200,6 +200,7 @@ declare namespace Eris { type EntitlementTypes = Constants["EntitlementTypes"][keyof Constants["EntitlementTypes"]]; type EntitlementOwnerTypes = Constants["EntitlementOwnerTypes"][keyof Constants["EntitlementOwnerTypes"]]; type SKUTypes = Constants["SKUTypes"][keyof Constants["SKUTypes"]]; + type SKUFlags = Constants["SKUFlags"][keyof Constants["SKUFlags"]]; // INTERFACES // Internals @@ -2492,7 +2493,7 @@ declare namespace Eris { export class SKU extends Base { applicationID: string; - flags: number; + flags: SKUFlags; name: string; slug: string; type: SKUTypes; From 3baf80db1e37813067da356d7261e59d9725b260 Mon Sep 17 00:00:00 2001 From: Areodot Date: Sun, 29 Sep 2024 17:42:14 +0000 Subject: [PATCH 07/15] Fix an error in createTestEntitlement --- lib/Client.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Client.js b/lib/Client.js index 580346edc..07f706f23 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -1157,7 +1157,11 @@ class Client extends EventEmitter { * @returns {Promise} */ createTestEntitlement(options) { - return this.requestHandler.request("POST", Endpoints.ENTITLEMENTS(this.application.id), true, options).then((entitlement) => new Entitlement(entitlement, this)); + return this.requestHandler.request("POST", Endpoints.ENTITLEMENTS(this.application.id), true, { + sku_id: options.skuID, + owner_id: options.ownerID, + owner_type: options.ownerType, + }).then((entitlement) => new Entitlement(entitlement, this)); } /** From 337a6193ffb2d55238d80676028ceb7476f9e1c2 Mon Sep 17 00:00:00 2001 From: Areodot Date: Sun, 29 Sep 2024 17:51:36 +0000 Subject: [PATCH 08/15] Should not have passed guild and user id's in the request, my bad --- lib/structures/Guild.js | 4 ++-- lib/structures/User.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/structures/Guild.js b/lib/structures/Guild.js index 1d1894cd2..bf114ac1e 100644 --- a/lib/structures/Guild.js +++ b/lib/structures/Guild.js @@ -563,7 +563,7 @@ class Guild extends Base { * @returns {Promise} */ createTestEntitlement(skuID) { - return this._client.createTestEntitlement.call(this._client, this.id, { + return this._client.createTestEntitlement.call(this._client, { skuID, ownerID: this.id, ownerType: EntitlementOwnerTypes.GUILD, @@ -1142,7 +1142,7 @@ class Guild extends Base { * @returns {Promise>} */ getEntitlements(options = {}) { - return this._client.getEntitlements.call(this._client, this.id, { + return this._client.getEntitlements.call(this._client, { guildID: this.id, ...options }) diff --git a/lib/structures/User.js b/lib/structures/User.js index cbc1360fb..ff72ee6d0 100644 --- a/lib/structures/User.js +++ b/lib/structures/User.js @@ -150,7 +150,7 @@ class User extends Base { * @returns {Promise} */ createTestEntitlement(skuID) { - return this._client.createTestEntitlement.call(this._client, this.id, { + return this._client.createTestEntitlement.call(this._client, { skuID, ownerID: this.id, ownerType: EntitlementOwnerTypes.USER, @@ -177,7 +177,7 @@ class User extends Base { * @returns {Promise>} */ getEntitlements(options = {}) { - return this._client.getEntitlements.call(this._client, this.id, { + return this._client.getEntitlements.call(this._client, { userID: this.id, ...options }) From 9684a55e224e4da9640cb9024747024372b90c12 Mon Sep 17 00:00:00 2001 From: Areodot Date: Fri, 25 Oct 2024 14:32:41 +0000 Subject: [PATCH 09/15] Removed .js from the import --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index f38b25997..d8c0ed09b 100644 --- a/index.js +++ b/index.js @@ -22,7 +22,7 @@ Eris.Constants = require("./lib/Constants"); Eris.DiscordHTTPError = require("./lib/errors/DiscordHTTPError"); Eris.DiscordRESTError = require("./lib/errors/DiscordRESTError"); Eris.DMChannel = require("./lib/structures/DMChannel"); -Eris.Entitlement = require("./lib/structures/Entitlement.js"); +Eris.Entitlement = require("./lib/structures/Entitlement"); Eris.ExtendedUser = require("./lib/structures/ExtendedUser"); Eris.ForumChannel = require("./lib/structures/ForumChannel"); Eris.GroupChannel = require("./lib/structures/GroupChannel"); @@ -49,7 +49,7 @@ Eris.PrivateThreadChannel = require("./lib/structures/PrivateThreadChannel"); Eris.PublicThreadChannel = require("./lib/structures/PublicThreadChannel"); Eris.RequestHandler = require("./lib/rest/RequestHandler"); Eris.Role = require("./lib/structures/Role"); -Eris.SKU = require("./lib/structures/SKU.js"); +Eris.SKU = require("./lib/structures/SKU"); Eris.SequentialBucket = require("./lib/util/SequentialBucket"); Eris.Shard = require("./lib/gateway/Shard"); Eris.SharedStream = require("./lib/voice/SharedStream"); From a289cd1d6e3fc2218918c8e3ad658e3a8d359b8a Mon Sep 17 00:00:00 2001 From: mrurxab Date: Fri, 25 Oct 2024 15:01:33 +0000 Subject: [PATCH 10/15] Lint and format --- examples/components.js | 4 +- index.d.ts | 18 ++++++--- lib/Client.js | 69 +++++++++++++++++------------------ lib/Constants.js | 8 ++-- lib/structures/Entitlement.js | 2 +- lib/structures/Guild.js | 33 ++++++++--------- lib/structures/SKU.js | 17 ++++----- lib/structures/User.js | 33 ++++++++--------- 8 files changed, 93 insertions(+), 91 deletions(-) diff --git a/examples/components.js b/examples/components.js index 529d37ae4..3d36188a7 100644 --- a/examples/components.js +++ b/examples/components.js @@ -75,8 +75,8 @@ bot.on("messageCreate", (msg) => { // When a message is created default_values: [ // List of default values for auto-populated select menus https://discord.com/developers/docs/interactions/message-components#select-menu-object-select-default-value-structure { id: "[Insert default channel id here]", - type: "channel" - } + type: "channel", + }, ], min_values: 1, max_values: 1, diff --git a/index.d.ts b/index.d.ts index 15ca072e8..f2563dff6 100644 --- a/index.d.ts +++ b/index.d.ts @@ -2175,6 +2175,7 @@ declare namespace Eris { bulkEditGuildCommands(guildID: string, commands: ApplicationCommandBulkEditOptions[]): Promise[]>; closeVoiceConnection(guildID: string): void; connect(): Promise; + consumeEntitlement(entitlementID: string): Promise; createAutoModerationRule(guildID: string, rule: CreateAutoModerationRuleOptions): Promise; createChannel(guildID: string, name: string): Promise; createChannel(guildID: string, name: string, type: T, options?: CreateChannelOptions): Promise>; @@ -2204,14 +2205,15 @@ declare namespace Eris { createMessage(channelID: string, content: MessageContent, file?: FileContent | FileContent[]): Promise; createRole(guildID: string, options?: Role | RoleOptions, reason?: string): Promise; createStageInstance(channelID: string, options: StageInstanceOptions): Promise; + createTestEntitlement(options: CreateTestEntitlementOptions): Promise; createThread(channelID: string, options: CreateForumThreadOptions, file?: FileContent | FileContent[]): Promise>; createThread(channelID: string, options: CreateThreadWithoutMessageOptions, file?: FileContent | FileContent[]): Promise; createThreadWithMessage(channelID: string, messageID: string, options: CreateThreadOptions): Promise; /** @deprecated */ createThreadWithoutMessage(channelID: string, options: CreateThreadWithoutMessageOptions): Promise; - createTestEntitlement(options: CreateTestEntitlementOptions): Promise; + crosspostMessage(channelID: string, messageID: string): Promise; - consumeEntitlement(entitlementID: string): Promise; + deleteAutoModerationRule(guildID: string, ruleID: string, reason?: string): Promise; deleteChannel(channelID: string, reason?: string): Promise; deleteChannelPermission(channelID: string, overwriteID: string, reason?: string): Promise; @@ -2382,9 +2384,10 @@ declare namespace Eris { getRESTUser(userID: string): Promise; getRoleConnectionMetadataRecords(): Promise; getSelf(): Promise; + getSKUs(): Promise; getSoundboardSounds(): Promise[]>; getStageInstance(channelID: string): Promise; - getSKUs(): Promise; + getThreadMember(channelID: string, userID: string, withMember?: boolean): Promise; getThreadMembers(channelID: string, options?: GetThreadMembersOptions): Promise; getVoiceRegions(guildID?: string): Promise; @@ -2696,8 +2699,9 @@ declare namespace Eris { createScheduledEvent(event: GuildScheduledEventOptions, reason?: string): Promise>; createSoundboardSound(sound: GuildSoundboardSoundCreate, reason?: string): Promise; createSticker(options: CreateStickerOptions, reason?: string): Promise; - createTestEntitlement(skuID: string): Promise; createTemplate(name: string, description?: string | null): Promise; + createTestEntitlement(skuID: string): Promise; + delete(): Promise; deleteAutoModerationRule(ruleID: string, reason?: string): Promise; deleteCommand(commandID: string): Promise; @@ -2748,9 +2752,10 @@ declare namespace Eris { getCommandPermissions(): Promise; getCommands(): Promise[]>; getDiscovery(): Promise; - getEntitlements(options?: Omit): Promise; /** @deprecated */ getEmbed(): Promise; + getEntitlements(options?: Omit): Promise; + getIntegrations(): Promise; getInvites(): Promise; getOnboarding(): Promise; @@ -3540,9 +3545,10 @@ declare namespace Eris { system: boolean; username: string; constructor(data: BaseData, client: Client); + createTestEntitlement(skuID: string): Promise; dynamicAvatarURL(format?: ImageFormat, size?: number): string; dynamicBannerURL(format?: ImageFormat, size?: number): string | null; - createTestEntitlement(skuID: string): Promise; + getDMChannel(): Promise; getEntitlements(options?: Omit): Promise; } diff --git a/lib/Client.js b/lib/Client.js index 861aef01d..46725f06f 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -30,7 +30,6 @@ const UnavailableGuild = require("./structures/UnavailableGuild"); const User = require("./structures/User"); const VoiceConnectionManager = require("./voice/VoiceConnectionManager"); const Entitlement = require("./structures/Entitlement"); -const SKU = require("./structures/SKU"); let EventEmitter; try { @@ -516,6 +515,15 @@ class Client extends EventEmitter { } } + /** + * For One-Time Purchase consumable SKUs, marks a given entitlement for the user as consumed + * @arg {String} entitlementID The ID of the the entitlement + * @returns {Promise} + */ + consumeEntitlement(entitlementID) { + return this.requestHandler.request("POST", Endpoints.ENTITLEMENT_CONSUME(this.application.id, entitlementID), true); + } + /** * Create an auto moderation rule * @arg {String} guildID the ID of the guild to create the rule in @@ -1063,6 +1071,22 @@ class Client extends EventEmitter { }).then((instance) => new StageInstance(instance, this)); } + /** + * Create a test entitlement + * @arg {Object} options The options for creating a test entitlement + * @arg {String} options.skuID The ID of the SKU to grant the entitlement to + * @arg {String} options.ownerID The ID of the owner to create the entitlement for + * @arg {Number} options.ownerType The type of the owner to create the entitlement for + * @returns {Promise} + */ + createTestEntitlement(options) { + return this.requestHandler.request("POST", Endpoints.ENTITLEMENTS(this.application.id), true, { + sku_id: options.skuID, + owner_id: options.ownerID, + owner_type: options.ownerType, + }).then((entitlement) => new Entitlement(entitlement, this)); + } + /** * Create a thread in a channel * @arg {String} channelID The ID of the channel @@ -1176,22 +1200,6 @@ class Client extends EventEmitter { }).then((channel) => Channel.from(channel, this)); } - /** - * Create a test entitlement - * @arg {Object} options The options for creating a test entitlement - * @arg {String} options.skuID The ID of the SKU to grant the entitlement to - * @arg {String} options.ownerID The ID of the owner to create the entitlement for - * @arg {Number} options.ownerType The type of the owner to create the entitlement for - * @returns {Promise} - */ - createTestEntitlement(options) { - return this.requestHandler.request("POST", Endpoints.ENTITLEMENTS(this.application.id), true, { - sku_id: options.skuID, - owner_id: options.ownerID, - owner_type: options.ownerType, - }).then((entitlement) => new Entitlement(entitlement, this)); - } - /** * Crosspost (publish) a message to subscribed channels * @arg {String} channelID The ID of the NewsChannel @@ -1202,15 +1210,6 @@ class Client extends EventEmitter { return this.requestHandler.request("POST", Endpoints.CHANNEL_CROSSPOST(channelID, messageID), true).then((message) => new Message(message, this)); } - /** - * For One-Time Purchase consumable SKUs, marks a given entitlement for the user as consumed - * @arg {String} entitlementID The ID of the the entitlement - * @returns {Promise} - */ - consumeEntitlement(entitlementID) { - return this.requestHandler.request("POST", Endpoints.ENTITLEMENT_CONSUME(this.application.id, entitlementID), true); - } - /** * Delete an auto moderation rule * @arg {String} guildID The guildID to delete the rule from @@ -2654,7 +2653,7 @@ class Client extends EventEmitter { * @arg {Number} [options.limit=100] The number of entitlements to return, 1-100, default 100 * @arg {String} [options.guildID] The guild ID to look up entitlements for * @arg {Boolean} [options.excludeEnded] Whether or not ended entitlements should be omitted - * @returns {Promise>} + * @returns {Promise>} */ getEntitlements(options = {}) { return this.requestHandler.request("GET", Endpoints.ENTITLEMENTS(this.application.id), true, { @@ -3429,6 +3428,14 @@ class Client extends EventEmitter { return this.requestHandler.request("GET", Endpoints.USER("@me"), true).then((data) => new ExtendedUser(data, this)); } + /** + * Get a list of SKUs for a given application + * @returns {Promise>} + */ + getSKUs() { + return this.requestHandler.request("GET", Endpoints.SKUS(this.application.id), true); + } + /** * Get the default soundboard sounds * @returns {Promise>} @@ -3446,14 +3453,6 @@ class Client extends EventEmitter { return this.requestHandler.request("GET", Endpoints.STAGE_INSTANCE(channelID), true).then((instance) => new StageInstance(instance, this)); } - /** - * Get a list of SKUs for a given application - * @returns {Promise>} - */ - getSKUs() { - return this.requestHandler.request("GET", Endpoints.SKUS(this.application.id), true); - } - /** * Get a member that is part of a thread channel * @arg {String} channelID The ID of the thread channel diff --git a/lib/Constants.js b/lib/Constants.js index 0f34fe6d2..a59e12926 100644 --- a/lib/Constants.js +++ b/lib/Constants.js @@ -236,12 +236,12 @@ module.exports.EntitlementTypes = { USER_GIFT: 6, PREMIUM_PURCHASE: 7, APPLICATION_SUBSCRIPTION: 8, -} +}; module.exports.EntitlementOwnerTypes = { GUILD: 1, USER: 2, -} +}; module.exports.ExplicitContentFilterLevels = { DISABLED: 0, @@ -769,13 +769,13 @@ module.exports.SKUTypes = { BUNDLE: 4, SUBSCRIPTION: 5, SUBSCRIPTION_GROUP: 6, -} +}; module.exports.SKUFlags = { AVAILABLE: 1 << 2, GUILD_SUBSCRIPTION: 1 << 7, USER_SUBSCRIPTION: 1 << 8, -} +}; module.exports.StageInstancePrivacyLevel = { PUBLIC: 1, diff --git a/lib/structures/Entitlement.js b/lib/structures/Entitlement.js index 13d7f7f68..ffac9a1bd 100644 --- a/lib/structures/Entitlement.js +++ b/lib/structures/Entitlement.js @@ -44,4 +44,4 @@ class Entitlement extends Base { } } -module.exports = Entitlement; \ No newline at end of file +module.exports = Entitlement; diff --git a/lib/structures/Guild.js b/lib/structures/Guild.js index 16e8b4919..548cfaed0 100644 --- a/lib/structures/Guild.js +++ b/lib/structures/Guild.js @@ -14,7 +14,6 @@ const { Permissions, EntitlementOwnerTypes } = require("../Constants"); const StageInstance = require("./StageInstance"); const ThreadChannel = require("./ThreadChannel"); const SoundboardSound = require("./SoundboardSound"); -const Entitlement = require("./Entitlement"); /** * Represents a guild @@ -583,19 +582,6 @@ class Guild extends Base { return this._client.createGuildSticker.call(this._client, this.id, options, reason); } - /** - * Create a test entitlement for this guild - * @arg {String} skuID The ID of the SKU to grant the entitlement to - * @returns {Promise} - */ - createTestEntitlement(skuID) { - return this._client.createTestEntitlement.call(this._client, { - skuID, - ownerID: this.id, - ownerType: EntitlementOwnerTypes.GUILD, - }) - } - /** * Create a template for this guild * @arg {String} name The name of the template @@ -606,6 +592,19 @@ class Guild extends Base { return this._client.createGuildTemplate.call(this._client, this.id, name, description); } + /** + * Create a test entitlement for this guild + * @arg {String} skuID The ID of the SKU to grant the entitlement to + * @returns {Promise} + */ + createTestEntitlement(skuID) { + return this._client.createTestEntitlement.call(this._client, { + skuID: skuID, + ownerID: this.id, + ownerType: EntitlementOwnerTypes.GUILD, + }); + } + /** * Delete the guild (bot user must be owner) * @returns {Promise} @@ -1188,13 +1187,13 @@ class Guild extends Base { * @arg {Number} [options.after] Retrieve entitlements after this entitlement ID * @arg {Number} [options.limit=100] The number of entitlements to return, 1-100, default 100 * @arg {Boolean} [options.excludeEnded] Whether or not ended entitlements should be omitted - * @returns {Promise>} + * @returns {Promise>} */ getEntitlements(options = {}) { return this._client.getEntitlements.call(this._client, { guildID: this.id, - ...options - }) + ...options, + }); } /** diff --git a/lib/structures/SKU.js b/lib/structures/SKU.js index d79a117c6..206fca450 100644 --- a/lib/structures/SKU.js +++ b/lib/structures/SKU.js @@ -1,7 +1,6 @@ "use strict"; const Base = require("./Base.js"); -const Entitlement = require("./Entitlement.js"); /** * Represents an SKU @@ -27,13 +26,13 @@ class SKU extends Base { * Create a test entitlement for this SKU * @arg {String} ownerID The ID of the owner to create the entitlement for * @arg {Number} ownerType The type of the owner to create the entitlement for - * @returns {Promise} + * @returns {Promise} */ createTestEntitlement(ownerID, ownerType) { return this._client.createTestEntitlement.call(this._client, { - ownerID, - ownerType, - skuID: this.id + ownerID: ownerID, + ownerType: ownerType, + skuID: this.id, }); } @@ -46,14 +45,14 @@ class SKU extends Base { * @arg {Number} [options.limit=100] The number of entitlements to return, 1-100, default 100 * @arg {String} [options.guildID] The guild ID to look up entitlements for * @arg {Boolean} [options.excludeEnded] Whether or not ended entitlements should be omitted - * @returns {Promise>} + * @returns {Promise>} */ getEntitlements(options = {}) { return this._client.getEntitlements.call(this._client, { - skuIDs: [this.id], - ...options + skuIDs: [this.id], + ...options, }); } } -module.exports = SKU; \ No newline at end of file +module.exports = SKU; diff --git a/lib/structures/User.js b/lib/structures/User.js index ff72ee6d0..aef335bdf 100644 --- a/lib/structures/User.js +++ b/lib/structures/User.js @@ -3,7 +3,6 @@ const Base = require("./Base"); const Endpoints = require("../rest/Endpoints"); const { EntitlementOwnerTypes } = require("../Constants"); -const Entitlement = require("../structures/Entitlement"); /** * Represents a user @@ -112,6 +111,19 @@ class User extends Base { return this.avatar ? this._client._formatImage(Endpoints.USER_AVATAR(this.id, this.avatar), "jpg") : this.defaultAvatarURL; } + /** + * Create a test entitlement for this user + * @arg {String} skuID The ID of the SKU to grant the entitlement to + * @returns {Promise} + */ + createTestEntitlement(skuID) { + return this._client.createTestEntitlement.call(this._client, { + skuID: skuID, + ownerID: this.id, + ownerType: EntitlementOwnerTypes.USER, + }); + } + /** * Get the user's avatar with the given format and size * @arg {String} [format] The filetype of the avatar ("jpg", "jpeg", "png", "gif", or "webp") @@ -144,19 +156,6 @@ class User extends Base { return this._client._formatImage(Endpoints.BANNER(this.id, this.banner), format, size); } - /** - * Create a test entitlement for this user - * @arg {String} skuID The ID of the SKU to grant the entitlement to - * @returns {Promise} - */ - createTestEntitlement(skuID) { - return this._client.createTestEntitlement.call(this._client, { - skuID, - ownerID: this.id, - ownerType: EntitlementOwnerTypes.USER, - }) - } - /** * Get a DM channel with the user, or create one if it does not exist * @returns {Promise} @@ -174,13 +173,13 @@ class User extends Base { * @arg {Number} [options.limit=100] The number of entitlements to return, 1-100, default 100 * @arg {String} [options.guildID] The guild ID to look up entitlements for * @arg {Boolean} [options.excludeEnded] Whether or not ended entitlements should be omitted - * @returns {Promise>} + * @returns {Promise>} */ getEntitlements(options = {}) { return this._client.getEntitlements.call(this._client, { userID: this.id, - ...options - }) + ...options, + }); } toJSON(props = []) { From a7c15c647e92f26ae7f3ee603caf7c63a90454ab Mon Sep 17 00:00:00 2001 From: Areodot Date: Sun, 10 Nov 2024 16:17:56 +0000 Subject: [PATCH 11/15] Removed undocumented types --- lib/Constants.d.ts | 2 -- lib/Constants.js | 2 -- 2 files changed, 4 deletions(-) diff --git a/lib/Constants.d.ts b/lib/Constants.d.ts index f1b12a8aa..d56450a17 100644 --- a/lib/Constants.d.ts +++ b/lib/Constants.d.ts @@ -608,10 +608,8 @@ export default interface Constants { BURST: 1; }; SKUTypes: { - DURABLE_PRIMARY: 1; DURABLE: 2; CONSUMABLE: 3; - BUNDLE: 4; SUBSCRIPTION: 5; SUBSCRIPTION_GROUP: 6; }; diff --git a/lib/Constants.js b/lib/Constants.js index a59e12926..9b21730e6 100644 --- a/lib/Constants.js +++ b/lib/Constants.js @@ -763,10 +763,8 @@ module.exports.RoleFlags = { }; module.exports.SKUTypes = { - DURABLE_PRIMARY: 1, DURABLE: 2, CONSUMABLE: 3, - BUNDLE: 4, SUBSCRIPTION: 5, SUBSCRIPTION_GROUP: 6, }; From 6ce811a4b48742a0b5bc9ca32fd179f0556f62b5 Mon Sep 17 00:00:00 2001 From: Areodot Date: Sun, 10 Nov 2024 17:53:39 +0000 Subject: [PATCH 12/15] Added subscriptions --- index.d.ts | 30 ++++++++++++++++++++++++++++++ index.js | 1 + lib/Client.js | 30 ++++++++++++++++++++++++++++++ lib/Constants.d.ts | 5 +++++ lib/Constants.js | 6 ++++++ lib/gateway/Shard.js | 28 ++++++++++++++++++++++++++++ lib/rest/Endpoints.js | 2 ++ lib/structures/SKU.js | 22 ++++++++++++++++++++++ lib/structures/Subscription.js | 32 ++++++++++++++++++++++++++++++++ 9 files changed, 156 insertions(+) create mode 100644 lib/structures/Subscription.js diff --git a/index.d.ts b/index.d.ts index 781daa7cc..aa6cfa5b1 100644 --- a/index.d.ts +++ b/index.d.ts @@ -207,6 +207,7 @@ declare namespace Eris { type EntitlementOwnerTypes = Constants["EntitlementOwnerTypes"][keyof Constants["EntitlementOwnerTypes"]]; type SKUTypes = Constants["SKUTypes"][keyof Constants["SKUTypes"]]; type SKUFlags = Constants["SKUFlags"][keyof Constants["SKUFlags"]]; + type SubscriptionStatuses = Constants["SubscriptionStatuses"][keyof Constants["SubscriptionStatuses"]]; // INTERFACES // Internals @@ -973,6 +974,9 @@ declare namespace Eris { entitlementCreate: [entitlement: Entitlement]; entitlementUpdate: [entitlement: Entitlement]; entitlementDelete: [entitlement: Entitlement]; + subscriptionCreate: [subscription: Subscription]; + subscriptionDelete: [subscription: Subscription]; + subscriptionUpdate: [subscription: Subscription]; } interface ClientEvents extends EventListeners { shardDisconnect: [err: Error | undefined, id: number]; @@ -1366,6 +1370,13 @@ declare namespace Eris { excludeEnded?: boolean; } + interface GetSKUSubscriptionsOptions { + userID: string; + before?: number; + after?: number; + limit?: number; + } + // Interaction interface AutocompleteInteractionData { id: string; @@ -2386,6 +2397,10 @@ declare namespace Eris { getRoleConnectionMetadataRecords(): Promise; getSelf(): Promise; getSKUs(): Promise; + getSKUSubscription(skuID: string, subscriptionID: string): Promise; + + getSKUSubscriptions(skuID: string, options: GetSKUSubscriptionsOptions): Promise; + getSoundboardSounds(): Promise[]>; getStageInstance(channelID: string): Promise; @@ -2585,6 +2600,21 @@ declare namespace Eris { type: SKUTypes; createTestEntitlement(ownerID: string, ownerType: EntitlementOwnerTypes): Promise; getEntitlements(options?: Omit): Promise; + getSKUSubscription(subscriptionID: string): Promise; + getSKUSubscriptions(): Promise; + } + + export class Subscription extends Base { + canceledAt: number | null; + country?: string; + currentPeriodEnd: number; + + currentPeriodStart: number; + + entitlementIDs: string[]; + skuIDs: string[]; + status: SubscriptionStatuses; + userID: string; } export class ExtendedUser extends User { diff --git a/index.js b/index.js index d8c0ed09b..98c75582c 100644 --- a/index.js +++ b/index.js @@ -50,6 +50,7 @@ Eris.PublicThreadChannel = require("./lib/structures/PublicThreadChannel"); Eris.RequestHandler = require("./lib/rest/RequestHandler"); Eris.Role = require("./lib/structures/Role"); Eris.SKU = require("./lib/structures/SKU"); +Eris.Subscription = require("./lib/structures/Subscription"); Eris.SequentialBucket = require("./lib/util/SequentialBucket"); Eris.Shard = require("./lib/gateway/Shard"); Eris.SharedStream = require("./lib/voice/SharedStream"); diff --git a/lib/Client.js b/lib/Client.js index 46725f06f..0adb6650f 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -30,6 +30,7 @@ const UnavailableGuild = require("./structures/UnavailableGuild"); const User = require("./structures/User"); const VoiceConnectionManager = require("./voice/VoiceConnectionManager"); const Entitlement = require("./structures/Entitlement"); +const Subscription = require("./structures/Subscription"); let EventEmitter; try { @@ -3436,6 +3437,35 @@ class Client extends EventEmitter { return this.requestHandler.request("GET", Endpoints.SKUS(this.application.id), true); } + /** + * Get a subscription by its ID from an SKU + * @arg {String} [skuID] The id of the sku + * @arg {String} [subscriptionID] The id of the subscription + * @returns {Promise} + */ + getSKUSubscription(skuID, subscriptionID) { + return this.requestHandler.request("GET", Endpoints.Endpoints.SKU_SUBSCRIPTION(skuID, subscriptionID), true).then((subscription) => new Subscription(subscription)); + } + + /** + * Get a subscription by its ID from an SKU + * @arg {String} [skuID] The id of the sku + * @arg {Object} [options] The options for the request + * @arg {String} [options.userID] The user ID to look up subscriptions for. Required except for OAuth queries. + * @arg {Number} [options.before] Retrieve subscriptions before this ID + * @arg {Number} [options.after] Retrieve subscriptions after this ID + * @arg {Number} [options.limit=50] The number of subscriptions to return, 1-100, default 50 + * @returns {Promise} + */ + getSKUSubscriptions(skuID, options = {}) { + return this.requestHandler.request("GET", Endpoints.SKU_SUBSCRIPTIONS(skuID), true, { + user_id: options.userID, + before: options.before, + after: options.after, + limit: options.limit, + }).then((subscriptions) => subscriptions.map((subscription) => new Subscription(subscription))); + } + /** * Get the default soundboard sounds * @returns {Promise>} diff --git a/lib/Constants.d.ts b/lib/Constants.d.ts index d56450a17..d91454aab 100644 --- a/lib/Constants.d.ts +++ b/lib/Constants.d.ts @@ -655,6 +655,11 @@ export default interface Constants { "Good to see you, %user%.", "Yay you made it, %user%!", ]; + SubscriptionStatuses: { + ACTIVE: 0; + ENDING: 1; + INACTIVE: 2; + }; ThreadMemberFlags: { HAS_INTERACTED: 1; ALL_MESSAGES: 2; diff --git a/lib/Constants.js b/lib/Constants.js index 9b21730e6..e43fdcd55 100644 --- a/lib/Constants.js +++ b/lib/Constants.js @@ -817,6 +817,12 @@ module.exports.SystemJoinMessages = [ "Yay you made it, %user%!", ]; +module.exports.SubscriptionStatuses = { + ACTIVE: 0, + ENDING: 1, + INACTIVE: 2, +}; + module.exports.ThreadMemberFlags = { HAS_INTERACTED: 1 << 0, ALL_MESSAGES: 1 << 1, diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 96e1eacbb..46acd0ae4 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -19,6 +19,7 @@ const GuildScheduledEvent = require("../structures/GuildScheduledEvent"); const GuildAuditLogEntry = require("../structures/GuildAuditLogEntry"); const SoundboardSound = require("../structures/SoundboardSound"); const Entitlement = require("../structures/Entitlement"); +const Subscription = require("../structures/Subscription"); const WebSocket = typeof window !== "undefined" ? require("../util/BrowserWebSocket") : require("ws"); @@ -2675,6 +2676,33 @@ class Shard extends EventEmitter { this.emit("entitlementDelete", new Entitlement(packet.d, this.client)); break; } + case "SUBSCRIPTION_CREATE ": { + /** + * Fired when an subscription is created + * @event Client#subscriptionCreate + * @prop {Subscription} subscription The new subscription + */ + this.emit("subscriptionCreate", new Subscription(packet.d)); + break; + } + case "SUBSCRIPTION_UPDATE": { + /** + * Fired when an subscription is updated + * @event Client#subscriptionUpdate + * @prop {Subscription} subscription The updated subscription + */ + this.emit("subscriptionUpdate", new Subscription(packet.d)); + break; + } + case "SUBSCRIPTION_DELETE": { + /** + * Fired when an subscription is deleted + * @event Client#subscriptionDelete + * @prop {Subscription} subscription The deleted subscription + */ + this.emit("subscriptionDelete", new Subscription(packet.d)); + break; + } default: { /** * Fired when the shard encounters an unknown packet diff --git a/lib/rest/Endpoints.js b/lib/rest/Endpoints.js index 80d3fc52f..d4a29b659 100644 --- a/lib/rest/Endpoints.js +++ b/lib/rest/Endpoints.js @@ -91,6 +91,8 @@ module.exports.POLL_ANSWER_VOTERS = (channelID, msgID, answerID) module.exports.POLL_END = (channelID, msgID) => `/channels/${channelID}/polls/${msgID}/expire`; module.exports.ROLE_CONNECTION_METADATA_RECORDS = (appID) => `/applications/${appID}/role-connections/metadata`; module.exports.SKUS = (applicationID) => `/applications/${applicationID}/skus`; +module.exports.SKU_SUBSCRIPTION = (skuID, subscriptionID) => `/skus/${skuID}/subscriptions/${subscriptionID}`; +module.exports.SKU_SUBSCRIPTIONS = (skuID) => `/skus/${skuID}/subscriptions`; module.exports.SOUNDBOARD_SOUND_GUILD = (guildID, soundID) => `/guilds/${guildID}/soundboard-sounds/${soundID}`; module.exports.SOUNDBOARD_SOUNDS_DEFAULT = "/soundboard-default-sounds"; module.exports.SOUNDBOARD_SOUNDS_GUILD = (guildID) => `/guilds/${guildID}/soundboard-sounds`; diff --git a/lib/structures/SKU.js b/lib/structures/SKU.js index 206fca450..767404647 100644 --- a/lib/structures/SKU.js +++ b/lib/structures/SKU.js @@ -53,6 +53,28 @@ class SKU extends Base { ...options, }); } + + /** + * Get a subscription by its ID from this SKU + * @arg {String} [subscriptionID] The id of the subscription + * @returns {Promise} + */ + getSKUSubscription(subscriptionID) { + return this._client.getSKUSubscription.call(this._client, this.id, subscriptionID); + } + + /** + * Get a list of subscriptions containing this SKU, filtered by user. + * @arg {Object} [options] The options for the request + * @arg {String} [options.userID] The user ID to look up subscriptions for. Required except for OAuth queries. + * @arg {Number} [options.before] Retrieve subscriptions before this ID + * @arg {Number} [options.after] Retrieve subscriptions after this ID + * @arg {Number} [options.limit=50] The number of subscriptions to return, 1-100, default 50 + * @returns {Promise>} + */ + getSKUSubscriptions(options = {}) { + return this._client.getSKUSubscriptions.call(this._client, this.id, options); + } } module.exports = SKU; diff --git a/lib/structures/Subscription.js b/lib/structures/Subscription.js new file mode 100644 index 000000000..4011cf11c --- /dev/null +++ b/lib/structures/Subscription.js @@ -0,0 +1,32 @@ +"use strict"; + +const Base = require("./Base.js"); + +/** + * Represents a subscription + * @prop {Number} currentPeriodEnd The end date of the current subscription period + * @prop {Number} currentPeriodStart The start date of the current subscription period + * @prop {Number} canceledAt When the subscription was canceled + * @prop {String?} country ISO3166-1 alpha-2 country code of the payment source used to purchase the subscription. Missing unless queried with a private OAuth scope. + * @prop {Array} entitlementIDs A list of entitlements granted for this subscription + * @prop {Array} skuIDs A list of SKUs subscribed to + * @prop {String} status The current status of the subscription + * @prop {String} userID The ID of the user who is subscribed + */ +class Subscription extends Base { + constructor(data) { + super(data.id); + + this.country = data.country; + this.entitlementIDs = data.entitlement_ids; + this.skuIDs = data.sku_ids; + this.status = data.status; + this.userID = data.user_id; + + this.currentPeriodEnd = data.current_period_end ? Date.parse(data.current_period_end) : null; + this.currentPeriodStart = data.current_period_start ? Date.parse(data.current_period_start) : null; + this.canceledAt = data.canceled_at ? Date.parse(data.canceled_at) : null; + } +} + +module.exports = Subscription; From 2739bd559c7a993a14e515fd8c1d7257c620f86e Mon Sep 17 00:00:00 2001 From: Areodot <179389942+areodot@users.noreply.github.com> Date: Sun, 22 Dec 2024 11:13:09 +0000 Subject: [PATCH 13/15] feat(Subscriptions): Multiple subscription tiers --- index.d.ts | 1 + lib/structures/Subscription.js | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index 5b2ed2990..bdf151a34 100644 --- a/index.d.ts +++ b/index.d.ts @@ -2652,6 +2652,7 @@ declare namespace Eris { currentPeriodStart: number; entitlementIDs: string[]; + renewalSKUIDs: string[] | null; skuIDs: string[]; status: SubscriptionStatuses; userID: string; diff --git a/lib/structures/Subscription.js b/lib/structures/Subscription.js index 4011cf11c..e10ca703d 100644 --- a/lib/structures/Subscription.js +++ b/lib/structures/Subscription.js @@ -8,6 +8,7 @@ const Base = require("./Base.js"); * @prop {Number} currentPeriodStart The start date of the current subscription period * @prop {Number} canceledAt When the subscription was canceled * @prop {String?} country ISO3166-1 alpha-2 country code of the payment source used to purchase the subscription. Missing unless queried with a private OAuth scope. + * @prop {String} renewalSKUIDs A list of SKUs that this user will be subscribed to at renewal * @prop {Array} entitlementIDs A list of entitlements granted for this subscription * @prop {Array} skuIDs A list of SKUs subscribed to * @prop {String} status The current status of the subscription @@ -18,6 +19,7 @@ class Subscription extends Base { super(data.id); this.country = data.country; + this.renewalSKUIDs = data.renewal_sku_ids; this.entitlementIDs = data.entitlement_ids; this.skuIDs = data.sku_ids; this.status = data.status; @@ -25,7 +27,10 @@ class Subscription extends Base { this.currentPeriodEnd = data.current_period_end ? Date.parse(data.current_period_end) : null; this.currentPeriodStart = data.current_period_start ? Date.parse(data.current_period_start) : null; - this.canceledAt = data.canceled_at ? Date.parse(data.canceled_at) : null; + + if (data.canceled_at !== undefined) { + this.canceledAt = data.canceled_at ? Date.parse(data.canceled_at) : null; + } } } From 74abe544a8cedb70f6be790a546a83efca2a869f Mon Sep 17 00:00:00 2001 From: Areodot <179389942+areodot@users.noreply.github.com> Date: Sun, 22 Dec 2024 11:21:42 +0000 Subject: [PATCH 14/15] fix: Export Subscription in "esm.mjs" --- esm.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/esm.mjs b/esm.mjs index 8de74b373..f236a63b7 100644 --- a/esm.mjs +++ b/esm.mjs @@ -49,6 +49,7 @@ export const { RequestHandler, Role, SKU, + Subscription, SequentialBucket, Shard, SharedStream, From 375c43835933f2fbf5c8712d481ce2e39647c081 Mon Sep 17 00:00:00 2001 From: bsian03 Date: Sat, 27 Sep 2025 16:38:42 +0100 Subject: [PATCH 15/15] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- lib/Client.js | 6 +++--- lib/gateway/Shard.js | 4 ++-- lib/structures/Entitlement.js | 2 +- lib/structures/Subscription.js | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/Client.js b/lib/Client.js index b38354b5e..cd698daa7 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -3534,18 +3534,18 @@ class Client extends EventEmitter { * @returns {Promise} */ getSKUSubscription(skuID, subscriptionID) { - return this.requestHandler.request("GET", Endpoints.Endpoints.SKU_SUBSCRIPTION(skuID, subscriptionID), true).then((subscription) => new Subscription(subscription)); + return this.requestHandler.request("GET", Endpoints.SKU_SUBSCRIPTION(skuID, subscriptionID), true).then((subscription) => new Subscription(subscription)); } /** - * Get a subscription by its ID from an SKU + * Get a list of subscriptions from an SKU * @arg {String} [skuID] The id of the sku * @arg {Object} [options] The options for the request * @arg {String} [options.userID] The user ID to look up subscriptions for. Required except for OAuth queries. * @arg {Number} [options.before] Retrieve subscriptions before this ID * @arg {Number} [options.after] Retrieve subscriptions after this ID * @arg {Number} [options.limit=50] The number of subscriptions to return, 1-100, default 50 - * @returns {Promise} + * @returns {Promise>} */ getSKUSubscriptions(skuID, options = {}) { return this.requestHandler.request("GET", Endpoints.SKU_SUBSCRIPTIONS(skuID), true, { diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index a84ab2f4c..ff2b01bdd 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2603,7 +2603,7 @@ class Shard extends EventEmitter { } break; } - case "ENTITLEMENT_CREATE ": { + case "ENTITLEMENT_CREATE": { /** * Fired when an entitlement is created * @event Client#entitlementCreate @@ -2630,7 +2630,7 @@ class Shard extends EventEmitter { this.emit("entitlementDelete", new Entitlement(packet.d, this.client)); break; } - case "SUBSCRIPTION_CREATE ": { + case "SUBSCRIPTION_CREATE": { /** * Fired when an subscription is created * @event Client#subscriptionCreate diff --git a/lib/structures/Entitlement.js b/lib/structures/Entitlement.js index ffac9a1bd..e70a8b91c 100644 --- a/lib/structures/Entitlement.js +++ b/lib/structures/Entitlement.js @@ -36,7 +36,7 @@ class Entitlement extends Base { } /** - * Mark's this entitlement as consumed + * Marks this entitlement as consumed * @returns {Promise} */ consume() { diff --git a/lib/structures/Subscription.js b/lib/structures/Subscription.js index e10ca703d..a79903a83 100644 --- a/lib/structures/Subscription.js +++ b/lib/structures/Subscription.js @@ -4,11 +4,11 @@ const Base = require("./Base.js"); /** * Represents a subscription - * @prop {Number} currentPeriodEnd The end date of the current subscription period - * @prop {Number} currentPeriodStart The start date of the current subscription period - * @prop {Number} canceledAt When the subscription was canceled + * @prop {Number?} canceledAt When the subscription was canceled + * @prop {Number?} currentPeriodEnd The end date of the current subscription period + * @prop {Number?} currentPeriodStart The start date of the current subscription period * @prop {String?} country ISO3166-1 alpha-2 country code of the payment source used to purchase the subscription. Missing unless queried with a private OAuth scope. - * @prop {String} renewalSKUIDs A list of SKUs that this user will be subscribed to at renewal + * @prop {Array?} renewalSKUIDs A list of SKUs that this user will be subscribed to at renewal * @prop {Array} entitlementIDs A list of entitlements granted for this subscription * @prop {Array} skuIDs A list of SKUs subscribed to * @prop {String} status The current status of the subscription