mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-16 11:33:30 +01:00
feat: premium application subscriptions (#9907)
* feat: premium application subscriptions * types: readonly array Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com> * fix: requested changes Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com> * fix: core client types --------- 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:
@@ -19,6 +19,9 @@ class ActionsManager {
|
||||
this.register(require('./ChannelCreate'));
|
||||
this.register(require('./ChannelDelete'));
|
||||
this.register(require('./ChannelUpdate'));
|
||||
this.register(require('./EntitlementCreate'));
|
||||
this.register(require('./EntitlementDelete'));
|
||||
this.register(require('./EntitlementUpdate'));
|
||||
this.register(require('./GuildAuditLogEntryCreate'));
|
||||
this.register(require('./GuildBanAdd'));
|
||||
this.register(require('./GuildBanRemove'));
|
||||
|
||||
23
packages/discord.js/src/client/actions/EntitlementCreate.js
Normal file
23
packages/discord.js/src/client/actions/EntitlementCreate.js
Normal file
@@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
const Action = require('./Action');
|
||||
const Events = require('../../util/Events');
|
||||
|
||||
class EntitlementCreateAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
|
||||
const entitlement = client.application.entitlements._add(data);
|
||||
|
||||
/**
|
||||
* Emitted whenever an entitlement is created.
|
||||
* @event Client#entitlementCreate
|
||||
* @param {Entitlement} entitlement The entitlement that was created
|
||||
*/
|
||||
client.emit(Events.EntitlementCreate, entitlement);
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EntitlementCreateAction;
|
||||
27
packages/discord.js/src/client/actions/EntitlementDelete.js
Normal file
27
packages/discord.js/src/client/actions/EntitlementDelete.js
Normal file
@@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
const Action = require('./Action');
|
||||
const Events = require('../../util/Events');
|
||||
|
||||
class EntitlementDeleteAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
|
||||
const entitlement = client.application.entitlements._add(data, false);
|
||||
|
||||
client.application.entitlements.cache.delete(entitlement.id);
|
||||
|
||||
/**
|
||||
* Emitted whenever an entitlement is deleted.
|
||||
* <warn>Entitlements are not deleted when they expire.
|
||||
* This is only triggered when Discord issues a refund or deletes the entitlement manually.</warn>
|
||||
* @event Client#entitlementDelete
|
||||
* @param {Entitlement} entitlement The entitlement that was deleted
|
||||
*/
|
||||
client.emit(Events.EntitlementDelete, entitlement);
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EntitlementDeleteAction;
|
||||
25
packages/discord.js/src/client/actions/EntitlementUpdate.js
Normal file
25
packages/discord.js/src/client/actions/EntitlementUpdate.js
Normal file
@@ -0,0 +1,25 @@
|
||||
'use strict';
|
||||
|
||||
const Action = require('./Action');
|
||||
const Events = require('../../util/Events');
|
||||
|
||||
class EntitlementUpdateAction extends Action {
|
||||
handle(data) {
|
||||
const client = this.client;
|
||||
|
||||
const oldEntitlement = client.application.entitlements.cache.get(data.id)?._clone() ?? null;
|
||||
const newEntitlement = client.application.entitlements._add(data);
|
||||
|
||||
/**
|
||||
* Emitted whenever an entitlement is updated - i.e. when a user's subscription renews.
|
||||
* @event Client#entitlementUpdate
|
||||
* @param {?Entitlement} oldEntitlement The entitlement before the update
|
||||
* @param {Entitlement} newEntitlement The entitlement after the update
|
||||
*/
|
||||
client.emit(Events.EntitlementUpdate, oldEntitlement, newEntitlement);
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EntitlementUpdateAction;
|
||||
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = (client, packet) => {
|
||||
client.actions.EntitlementCreate.handle(packet.d);
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = (client, packet) => {
|
||||
client.actions.EntitlementDelete.handle(packet.d);
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = (client, packet) => {
|
||||
client.actions.EntitlementUpdate.handle(packet.d);
|
||||
};
|
||||
@@ -10,6 +10,9 @@ const handlers = Object.fromEntries([
|
||||
['CHANNEL_DELETE', require('./CHANNEL_DELETE')],
|
||||
['CHANNEL_PINS_UPDATE', require('./CHANNEL_PINS_UPDATE')],
|
||||
['CHANNEL_UPDATE', require('./CHANNEL_UPDATE')],
|
||||
['ENTITLEMENT_CREATE', require('./ENTITLEMENT_CREATE')],
|
||||
['ENTITLEMENT_DELETE', require('./ENTITLEMENT_DELETE')],
|
||||
['ENTITLEMENT_UPDATE', require('./ENTITLEMENT_UPDATE')],
|
||||
['GUILD_AUDIT_LOG_ENTRY_CREATE', require('./GUILD_AUDIT_LOG_ENTRY_CREATE')],
|
||||
['GUILD_BAN_ADD', require('./GUILD_BAN_ADD')],
|
||||
['GUILD_BAN_REMOVE', require('./GUILD_BAN_REMOVE')],
|
||||
|
||||
@@ -173,6 +173,8 @@
|
||||
* @property {'GuildForumMessageRequired'} GuildForumMessageRequired
|
||||
|
||||
* @property {'SweepFilterReturn'} SweepFilterReturn
|
||||
|
||||
* @property {'EntitlementCreateInvalidOwner'} EntitlementCreateInvalidOwner
|
||||
*/
|
||||
|
||||
const keys = [
|
||||
@@ -323,6 +325,8 @@ const keys = [
|
||||
'SweepFilterReturn',
|
||||
|
||||
'GuildForumMessageRequired',
|
||||
|
||||
'EntitlementCreateInvalidOwner',
|
||||
];
|
||||
|
||||
// JSDoc for IntelliSense purposes
|
||||
|
||||
@@ -165,6 +165,9 @@ const Messages = {
|
||||
[DjsErrorCodes.SweepFilterReturn]: 'The return value of the sweepFilter function was not false or a Function',
|
||||
|
||||
[DjsErrorCodes.GuildForumMessageRequired]: 'You must provide a message to create a guild forum thread',
|
||||
|
||||
[DjsErrorCodes.EntitlementCreateInvalidOwner]:
|
||||
'You must provide either a guild or a user to create an entitlement, but not both',
|
||||
};
|
||||
|
||||
module.exports = Messages;
|
||||
|
||||
@@ -38,6 +38,7 @@ exports.Partials = require('./util/Partials');
|
||||
exports.PermissionsBitField = require('./util/PermissionsBitField');
|
||||
exports.RoleFlagsBitField = require('./util/RoleFlagsBitField');
|
||||
exports.ShardEvents = require('./util/ShardEvents');
|
||||
exports.SKUFlagsBitField = require('./util/SKUFlagsBitField').SKUFlagsBitField;
|
||||
exports.Status = require('./util/Status');
|
||||
exports.SnowflakeUtil = require('@sapphire/snowflake').DiscordSnowflake;
|
||||
exports.Sweepers = require('./util/Sweepers');
|
||||
@@ -58,6 +59,7 @@ exports.ChannelManager = require('./managers/ChannelManager');
|
||||
exports.ClientVoiceManager = require('./client/voice/ClientVoiceManager');
|
||||
exports.DataManager = require('./managers/DataManager');
|
||||
exports.DMMessageManager = require('./managers/DMMessageManager');
|
||||
exports.EntitlementManager = require('./managers/EntitlementManager').EntitlementManager;
|
||||
exports.GuildApplicationCommandManager = require('./managers/GuildApplicationCommandManager');
|
||||
exports.GuildBanManager = require('./managers/GuildBanManager');
|
||||
exports.GuildChannelManager = require('./managers/GuildChannelManager');
|
||||
@@ -121,6 +123,7 @@ exports.DMChannel = require('./structures/DMChannel');
|
||||
exports.Embed = require('./structures/Embed');
|
||||
exports.EmbedBuilder = require('./structures/EmbedBuilder');
|
||||
exports.Emoji = require('./structures/Emoji').Emoji;
|
||||
exports.Entitlement = require('./structures/Entitlement').Entitlement;
|
||||
exports.ForumChannel = require('./structures/ForumChannel');
|
||||
exports.Guild = require('./structures/Guild').Guild;
|
||||
exports.GuildAuditLogs = require('./structures/GuildAuditLogs');
|
||||
@@ -188,6 +191,7 @@ exports.RoleSelectMenuInteraction = require('./structures/RoleSelectMenuInteract
|
||||
exports.StringSelectMenuInteraction = require('./structures/StringSelectMenuInteraction');
|
||||
exports.UserSelectMenuInteraction = require('./structures/UserSelectMenuInteraction');
|
||||
exports.SelectMenuOptionBuilder = require('./structures/SelectMenuOptionBuilder');
|
||||
exports.SKU = require('./structures/SKU').SKU;
|
||||
exports.StringSelectMenuOptionBuilder = require('./structures/StringSelectMenuOptionBuilder');
|
||||
exports.StageChannel = require('./structures/StageChannel');
|
||||
exports.StageInstance = require('./structures/StageInstance').StageInstance;
|
||||
|
||||
129
packages/discord.js/src/managers/EntitlementManager.js
Normal file
129
packages/discord.js/src/managers/EntitlementManager.js
Normal file
@@ -0,0 +1,129 @@
|
||||
'use strict';
|
||||
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { makeURLSearchParams } = require('@discordjs/rest');
|
||||
const { Routes, EntitlementOwnerType } = require('discord-api-types/v10');
|
||||
const CachedManager = require('./CachedManager');
|
||||
const { ErrorCodes, DiscordjsTypeError } = require('../errors/index');
|
||||
const { Entitlement } = require('../structures/Entitlement');
|
||||
const { resolveSKUId } = require('../util/Util');
|
||||
|
||||
/**
|
||||
* Manages API methods for entitlements and stores their cache.
|
||||
* @extends {CachedManager}
|
||||
*/
|
||||
class EntitlementManager extends CachedManager {
|
||||
constructor(client, iterable) {
|
||||
super(client, Entitlement, iterable);
|
||||
}
|
||||
|
||||
/**
|
||||
* The cache of this manager
|
||||
* @type {Collection<Snowflake, Entitlement>}
|
||||
* @name EntitlementManager#cache
|
||||
*/
|
||||
|
||||
/**
|
||||
* Data that resolves to give an Entitlement object. This can be:
|
||||
* * An Entitlement object
|
||||
* * A Snowflake
|
||||
* @typedef {Entitlement|Snowflake} EntitlementResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Data that resolves to give a SKU object. This can be:
|
||||
* * A SKU object
|
||||
* * A Snowflake
|
||||
* @typedef {SKU|Snowflake} SKUResolvable
|
||||
*/
|
||||
|
||||
/**
|
||||
* Options used to fetch entitlements
|
||||
* @typedef {Object} FetchEntitlementsOptions
|
||||
* @property {number} [limit] The maximum number of entitlements to fetch
|
||||
* @property {GuildResolvable} [guild] The guild to fetch entitlements for
|
||||
* @property {UserResolvable} [user] The user to fetch entitlements for
|
||||
* @property {SKUResolvable[]} [skus] The SKUs to fetch entitlements for
|
||||
* @property {boolean} [excludeEnded] Whether to exclude ended entitlements
|
||||
* @property {boolean} [cache=true] Whether to cache the fetched entitlements
|
||||
* @property {Snowflake} [before] Consider only entitlements before this entitlement id
|
||||
* @property {Snowflake} [after] Consider only entitlements after this entitlement id
|
||||
* <warn>If both `before` and `after` are provided, only `before` is respected</warn>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Fetches entitlements for this application
|
||||
* @param {FetchEntitlementsOptions} [options={}] Options for fetching the entitlements
|
||||
* @returns {Promise<Collection<Snowflake, Entitlement>>}
|
||||
*/
|
||||
async fetch({ limit, guild, user, skus, excludeEnded, cache = true, before, after } = {}) {
|
||||
const query = makeURLSearchParams({
|
||||
limit,
|
||||
guild_id: guild && this.client.guilds.resolveId(guild),
|
||||
user_id: user && this.client.users.resolveId(user),
|
||||
sku_ids: skus?.map(sku => resolveSKUId(sku)).join(','),
|
||||
exclude_ended: excludeEnded,
|
||||
before,
|
||||
after,
|
||||
});
|
||||
|
||||
const entitlements = await this.client.rest.get(Routes.entitlements(this.client.application.id), { query });
|
||||
return entitlements.reduce(
|
||||
(coll, entitlement) => coll.set(entitlement.id, this._add(entitlement, cache)),
|
||||
new Collection(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Options used to create a test entitlement
|
||||
* <info>Either `guild` or `user` must be provided, but not both</info>
|
||||
* @typedef {Object} EntitlementCreateOptions
|
||||
* @property {SKUResolvable} sku The id of the SKU to create the entitlement for
|
||||
* @property {GuildResolvable} [guild] The guild to create the entitlement for
|
||||
* @property {UserResolvable} [user] The user to create the entitlement for
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a test entitlement
|
||||
* @param {EntitlementCreateOptions} options Options for creating the test entitlement
|
||||
* @returns {Promise<Entitlement>}
|
||||
*/
|
||||
async createTest({ sku, guild, user }) {
|
||||
const skuId = resolveSKUId(sku);
|
||||
if (!skuId) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'sku', 'SKUResolvable');
|
||||
|
||||
if ((guild && user) || (!guild && !user)) {
|
||||
throw new DiscordjsTypeError(ErrorCodes.EntitlementCreateInvalidOwner);
|
||||
}
|
||||
|
||||
const resolved = guild ? this.client.guilds.resolveId(guild) : this.client.users.resolveId(user);
|
||||
if (!resolved) {
|
||||
const name = guild ? 'guild' : 'user';
|
||||
const type = guild ? 'GuildResolvable' : 'UserResolvable';
|
||||
throw new DiscordjsTypeError(ErrorCodes.InvalidType, name, type);
|
||||
}
|
||||
|
||||
const entitlement = await this.client.rest.post(Routes.entitlements(this.client.application.id), {
|
||||
body: {
|
||||
sku_id: skuId,
|
||||
owner_id: resolved,
|
||||
owner_type: guild ? EntitlementOwnerType.Guild : EntitlementOwnerType.User,
|
||||
},
|
||||
});
|
||||
return new Entitlement(this.client, entitlement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a test entitlement
|
||||
* @param {EntitlementResolvable} entitlement The entitlement to delete
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async deleteTest(entitlement) {
|
||||
const resolved = this.resolveId(entitlement);
|
||||
if (!resolved) throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'entitlement', 'EntitlementResolvable');
|
||||
|
||||
await this.client.rest.delete(Routes.entitlement(this.client.application.id, resolved));
|
||||
}
|
||||
}
|
||||
|
||||
exports.EntitlementManager = EntitlementManager;
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { deprecate } = require('node:util');
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { DiscordSnowflake } = require('@sapphire/snowflake');
|
||||
const { InteractionType, ApplicationCommandType, ComponentType } = require('discord-api-types/v10');
|
||||
const Base = require('./Base');
|
||||
@@ -133,6 +134,15 @@ class BaseInteraction extends Base {
|
||||
* @type {?Locale}
|
||||
*/
|
||||
this.guildLocale = data.guild_locale ?? null;
|
||||
|
||||
/**
|
||||
* The entitlements for the invoking user, representing access to premium SKUs
|
||||
* @type {Collection<Snowflake, Entitlement>}
|
||||
*/
|
||||
this.entitlements = data.entitlements.reduce(
|
||||
(coll, entitlement) => coll.set(entitlement.id, this.client.application.entitlements._add(entitlement)),
|
||||
new Collection(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const { Collection } = require('@discordjs/collection');
|
||||
const { Routes } = require('discord-api-types/v10');
|
||||
const { ApplicationRoleConnectionMetadata } = require('./ApplicationRoleConnectionMetadata');
|
||||
const { SKU } = require('./SKU');
|
||||
const Team = require('./Team');
|
||||
const Application = require('./interfaces/Application');
|
||||
const ApplicationCommandManager = require('../managers/ApplicationCommandManager');
|
||||
const { EntitlementManager } = require('../managers/EntitlementManager');
|
||||
const ApplicationFlagsBitField = require('../util/ApplicationFlagsBitField');
|
||||
const { resolveImage } = require('../util/DataResolver');
|
||||
const PermissionsBitField = require('../util/PermissionsBitField');
|
||||
@@ -28,6 +31,12 @@ class ClientApplication extends Application {
|
||||
* @type {ApplicationCommandManager}
|
||||
*/
|
||||
this.commands = new ApplicationCommandManager(this.client);
|
||||
|
||||
/**
|
||||
* The entitlement manager for this application
|
||||
* @type {EntitlementManager}
|
||||
*/
|
||||
this.entitlements = new EntitlementManager(this.client);
|
||||
}
|
||||
|
||||
_patch(data) {
|
||||
@@ -287,6 +296,15 @@ class ClientApplication extends Application {
|
||||
|
||||
return newRecords.map(data => new ApplicationRoleConnectionMetadata(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this application's SKUs
|
||||
* @returns {Promise<Collection<Snowflake, SKU>>}
|
||||
*/
|
||||
async fetchSKUs() {
|
||||
const skus = await this.client.rest.get(Routes.skus(this.id));
|
||||
return skus.reduce((coll, sku) => coll.set(sku.id, new SKU(this.client, sku)), new Collection());
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ClientApplication;
|
||||
|
||||
@@ -153,6 +153,7 @@ class CommandInteraction extends BaseInteraction {
|
||||
deleteReply() {}
|
||||
followUp() {}
|
||||
showModal() {}
|
||||
sendPremiumRequired() {}
|
||||
awaitModalSubmit() {}
|
||||
}
|
||||
|
||||
|
||||
164
packages/discord.js/src/structures/Entitlement.js
Normal file
164
packages/discord.js/src/structures/Entitlement.js
Normal file
@@ -0,0 +1,164 @@
|
||||
'use strict';
|
||||
|
||||
const Base = require('./Base');
|
||||
|
||||
/**
|
||||
* Represents an Entitlement
|
||||
* @extends {Base}
|
||||
*/
|
||||
class Entitlement extends Base {
|
||||
constructor(client, data) {
|
||||
super(client);
|
||||
|
||||
/**
|
||||
* The id of the entitlement
|
||||
* @type {Snowflake}
|
||||
*/
|
||||
this.id = data.id;
|
||||
|
||||
this._patch(data);
|
||||
}
|
||||
|
||||
_patch(data) {
|
||||
if ('sku_id' in data) {
|
||||
/**
|
||||
* The id of the associated SKU
|
||||
* @type {Snowflake}
|
||||
*/
|
||||
this.skuId = data.sku_id;
|
||||
}
|
||||
|
||||
if ('user_id' in data) {
|
||||
/**
|
||||
* The id of the user that is granted access to this entitlement's SKU
|
||||
* @type {Snowflake}
|
||||
*/
|
||||
this.userId = data.user_id;
|
||||
}
|
||||
|
||||
if ('guild_id' in data) {
|
||||
/**
|
||||
* The id of the guild that is granted access to this entitlement's SKU
|
||||
* @type {?Snowflake}
|
||||
*/
|
||||
this.guildId = data.guild_id;
|
||||
} else {
|
||||
this.guildId ??= null;
|
||||
}
|
||||
|
||||
if ('application_id' in data) {
|
||||
/**
|
||||
* The id of the parent application
|
||||
* @type {Snowflake}
|
||||
*/
|
||||
this.applicationId = data.application_id;
|
||||
}
|
||||
|
||||
if ('type' in data) {
|
||||
/**
|
||||
* The type of this entitlement
|
||||
* @type {EntitlementType}
|
||||
*/
|
||||
this.type = data.type;
|
||||
}
|
||||
|
||||
if ('deleted' in data) {
|
||||
/**
|
||||
* Whether this entitlement was deleted
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.deleted = data.deleted;
|
||||
}
|
||||
|
||||
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);
|
||||
} else {
|
||||
this.startsTimestamp ??= null;
|
||||
}
|
||||
|
||||
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);
|
||||
} else {
|
||||
this.endsTimestamp ??= null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The guild that is granted access to this entitlement's SKU
|
||||
* @type {?Guild}
|
||||
*/
|
||||
get guild() {
|
||||
if (!this.guildId) return null;
|
||||
return this.client.guilds.cache.get(this.guildId) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The start date at which this entitlement is valid
|
||||
* <info>This is only `null` for test entitlements</info>
|
||||
* @type {?Date}
|
||||
*/
|
||||
get startsAt() {
|
||||
return this.startsTimestamp && new Date(this.startsTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* The end date at which this entitlement is no longer valid
|
||||
* <info>This is only `null` for test entitlements</info>
|
||||
* @type {?Date}
|
||||
*/
|
||||
get endsAt() {
|
||||
return this.endsTimestamp && new Date(this.endsTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this entitlement is active
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isActive() {
|
||||
return !this.deleted && (!this.endsTimestamp || this.endsTimestamp > Date.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this entitlement is a test entitlement
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isTest() {
|
||||
return this.startsTimestamp === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this entitlement is a user subscription
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isUserSubscription() {
|
||||
return this.guildId === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this entitlement is a guild subscription
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isGuildSubscription() {
|
||||
return this.guildId !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the user that is granted access to this entitlement's SKU
|
||||
* @returns {Promise<User>}
|
||||
*/
|
||||
fetchUser() {
|
||||
return this.client.users.fetch(this.userId);
|
||||
}
|
||||
}
|
||||
|
||||
exports.Entitlement = Entitlement;
|
||||
@@ -99,6 +99,7 @@ class MessageComponentInteraction extends BaseInteraction {
|
||||
deferUpdate() {}
|
||||
update() {}
|
||||
showModal() {}
|
||||
sendPremiumRequired() {}
|
||||
awaitModalSubmit() {}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ class ModalSubmitInteraction extends BaseInteraction {
|
||||
followUp() {}
|
||||
deferUpdate() {}
|
||||
update() {}
|
||||
sendPremiumRequired() {}
|
||||
}
|
||||
|
||||
InteractionResponses.applyToClass(ModalSubmitInteraction, 'showModal');
|
||||
|
||||
52
packages/discord.js/src/structures/SKU.js
Normal file
52
packages/discord.js/src/structures/SKU.js
Normal file
@@ -0,0 +1,52 @@
|
||||
'use strict';
|
||||
|
||||
const Base = require('./Base');
|
||||
const { SKUFlagsBitField } = require('../util/SKUFlagsBitField');
|
||||
|
||||
/**
|
||||
* Represents a premium application SKU.
|
||||
* @extends {Base}
|
||||
*/
|
||||
class SKU extends Base {
|
||||
constructor(client, data) {
|
||||
super(client);
|
||||
|
||||
/**
|
||||
* The id of the SKU
|
||||
* @type {Snowflake}
|
||||
*/
|
||||
this.id = data.id;
|
||||
|
||||
/**
|
||||
* The type of the SKU
|
||||
* @type {SKUType}
|
||||
*/
|
||||
this.type = data.type;
|
||||
|
||||
/**
|
||||
* The id of the parent application
|
||||
* @type {Snowflake}
|
||||
*/
|
||||
this.applicationId = data.application_id;
|
||||
|
||||
/**
|
||||
* The customer-facing name of the premium offering
|
||||
* @type {string}
|
||||
*/
|
||||
this.name = data.name;
|
||||
|
||||
/**
|
||||
* The system-generated URL slug based on this SKU's name
|
||||
* @type {string}
|
||||
*/
|
||||
this.slug = data.slug;
|
||||
|
||||
/**
|
||||
* Flags that describe the SKU
|
||||
* @type {Readonly<SKUFlagsBitField>}
|
||||
*/
|
||||
this.flags = new SKUFlagsBitField(data.flags).freeze();
|
||||
}
|
||||
}
|
||||
|
||||
exports.SKU = SKU;
|
||||
@@ -262,6 +262,22 @@ class InteractionResponses {
|
||||
this.replied = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to the interaction with an upgrade button.
|
||||
* <info>Only available for applications with monetization enabled.</info>
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async sendPremiumRequired() {
|
||||
if (this.deferred || this.replied) throw new DiscordjsError(ErrorCodes.InteractionAlreadyReplied);
|
||||
await this.client.rest.post(Routes.interactionCallback(this.id, this.token), {
|
||||
body: {
|
||||
type: InteractionResponseType.PremiumRequired,
|
||||
},
|
||||
auth: false,
|
||||
});
|
||||
this.replied = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* An object containing the same properties as {@link CollectorOptions}, but a few less:
|
||||
* @typedef {Object} AwaitModalSubmitOptions
|
||||
@@ -305,6 +321,7 @@ class InteractionResponses {
|
||||
'deferUpdate',
|
||||
'update',
|
||||
'showModal',
|
||||
'sendPremiumRequired',
|
||||
'awaitModalSubmit',
|
||||
];
|
||||
|
||||
|
||||
@@ -280,6 +280,11 @@
|
||||
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/ComponentType}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @external EntitlementType
|
||||
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/EntitlementType}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @external ForumLayoutType
|
||||
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/ForumLayoutType}
|
||||
@@ -460,6 +465,16 @@
|
||||
* @see {@link https://discord-api-types.dev/api/discord-api-types-rest/common/enum/RESTJSONErrorCodes}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @external SKUFlags
|
||||
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/SKUFlags}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @external SKUType
|
||||
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/SKUType}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @external SortOrderType
|
||||
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/SortOrderType}
|
||||
|
||||
@@ -14,6 +14,7 @@ exports.MaxBulkDeletableMessageAge = 1_209_600_000;
|
||||
* * `applicationCommands` - both global and guild commands
|
||||
* * `bans`
|
||||
* * `emojis`
|
||||
* * `entitlements`
|
||||
* * `invites` - accepts the `lifetime` property, using it will sweep based on expires timestamp
|
||||
* * `guildMembers`
|
||||
* * `messages` - accepts the `lifetime` property, using it will sweep based on edited or created timestamp
|
||||
@@ -32,6 +33,7 @@ exports.SweeperKeys = [
|
||||
'applicationCommands',
|
||||
'bans',
|
||||
'emojis',
|
||||
'entitlements',
|
||||
'invites',
|
||||
'guildMembers',
|
||||
'messages',
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
* @property {string} ChannelUpdate channelUpdate
|
||||
* @property {string} ClientReady ready
|
||||
* @property {string} Debug debug
|
||||
* @property {string} EntitlementCreate entitlementCreate
|
||||
* @property {string} EntitlementUpdate entitlementUpdate
|
||||
* @property {string} EntitlementDelete entitlementDelete
|
||||
* @property {string} Error error
|
||||
* @property {string} GuildAuditLogEntryCreate guildAuditLogEntryCreate
|
||||
* @property {string} GuildAvailable guildAvailable
|
||||
@@ -96,6 +99,9 @@ module.exports = {
|
||||
ChannelUpdate: 'channelUpdate',
|
||||
ClientReady: 'ready',
|
||||
Debug: 'debug',
|
||||
EntitlementCreate: 'entitlementCreate',
|
||||
EntitlementUpdate: 'entitlementUpdate',
|
||||
EntitlementDelete: 'entitlementDelete',
|
||||
Error: 'error',
|
||||
GuildAuditLogEntryCreate: 'guildAuditLogEntryCreate',
|
||||
GuildAvailable: 'guildAvailable',
|
||||
|
||||
26
packages/discord.js/src/util/SKUFlagsBitField.js
Normal file
26
packages/discord.js/src/util/SKUFlagsBitField.js
Normal file
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
const { SKUFlags } = require('discord-api-types/v10');
|
||||
const BitField = require('./BitField');
|
||||
|
||||
/**
|
||||
* Data structure that makes it easy to interact with an {@link SKU#flags} bitfield.
|
||||
* @extends {BitField}
|
||||
*/
|
||||
class SKUFlagsBitField extends BitField {
|
||||
/**
|
||||
* Numeric SKU flags.
|
||||
* @type {SKUFlags}
|
||||
* @memberof SKUFlagsBitField
|
||||
*/
|
||||
static Flags = SKUFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name SKUFlagsBitField
|
||||
* @kind constructor
|
||||
* @memberof SKUFlagsBitField
|
||||
* @param {BitFieldResolvable} [bits=0] Bit(s) to read from
|
||||
*/
|
||||
|
||||
exports.SKUFlagsBitField = SKUFlagsBitField;
|
||||
@@ -106,6 +106,23 @@ class Sweepers {
|
||||
return this._sweepGuildDirectProp('emojis', filter).items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sweeps all client application entitlements and removes the ones which are indicated by the filter.
|
||||
* @param {Function} filter The function used to determine which entitlements will be removed from the caches.
|
||||
* @returns {number} Amount of entitlements that were removed from the caches
|
||||
*/
|
||||
sweepEntitlements(filter) {
|
||||
if (typeof filter !== 'function') {
|
||||
throw new DiscordjsTypeError(ErrorCodes.InvalidType, 'filter', 'function');
|
||||
}
|
||||
|
||||
const entitlements = this.client.application.entitlements.cache.sweep(filter);
|
||||
|
||||
this.client.emit(Events.CacheSweep, `Swept ${entitlements} entitlements.`);
|
||||
|
||||
return entitlements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sweeps all guild invites and removes the ones which are indicated by the filter.
|
||||
* @param {Function} filter The function used to determine which invites will be removed from the caches.
|
||||
|
||||
@@ -479,6 +479,17 @@ function transformResolved(
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a SKU id from a SKU resolvable.
|
||||
* @param {SKUResolvable} resolvable The SKU resolvable to resolve
|
||||
* @returns {?Snowflake} The resolved SKU id, or `null` if the resolvable was invalid
|
||||
*/
|
||||
function resolveSKUId(resolvable) {
|
||||
if (typeof resolvable === 'string') return resolvable;
|
||||
if (resolvable instanceof SKU) return resolvable.id;
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
flatten,
|
||||
fetchRecommendedShardCount,
|
||||
@@ -497,8 +508,10 @@ module.exports = {
|
||||
cleanCodeBlockContent,
|
||||
parseWebhookURL,
|
||||
transformResolved,
|
||||
resolveSKUId,
|
||||
};
|
||||
|
||||
// Fixes Circular
|
||||
const Attachment = require('../structures/Attachment');
|
||||
const GuildChannel = require('../structures/GuildChannel');
|
||||
const { SKU } = require('../structures/SKU.js');
|
||||
|
||||
Reference in New Issue
Block a user