diff --git a/src/managers/ApplicationCommandManager.js b/src/managers/ApplicationCommandManager.js index a163fb497a40..3aecdc1105de 100644 --- a/src/managers/ApplicationCommandManager.js +++ b/src/managers/ApplicationCommandManager.js @@ -6,6 +6,7 @@ const CachedManager = require('./CachedManager'); const { TypeError } = require('../errors'); const ApplicationCommand = require('../structures/ApplicationCommand'); const { ApplicationCommandTypes } = require('../util/Constants'); +const Permissions = require('../util/Permissions'); /** * Manages API methods for application commands and stores their cache. @@ -162,7 +163,7 @@ class ApplicationCommandManager extends CachedManager { /** * Edits an application command. * @param {ApplicationCommandResolvable} command The command to edit - * @param {ApplicationCommandData|APIApplicationCommand} data The data to update the command with + * @param {Partial} data The data to update the command with * @param {Snowflake} [guildId] The guild's id where the command registered, * ignored when using a {@link GuildApplicationCommandManager} * @returns {Promise} @@ -214,6 +215,20 @@ class ApplicationCommandManager extends CachedManager { * @private */ static transformCommand(command) { + let default_member_permissions; + + if ('default_member_permissions' in command) { + default_member_permissions = command.default_member_permissions + ? new Permissions(BigInt(command.default_member_permissions)).bitfield.toString() + : command.default_member_permissions; + } + + if ('defaultMemberPermissions' in command) { + default_member_permissions = command.defaultMemberPermissions + ? new Permissions(command.defaultMemberPermissions).bitfield.toString() + : command.defaultMemberPermissions; + } + return { name: command.name, name_localizations: command.nameLocalizations ?? command.name_localizations, @@ -222,6 +237,8 @@ class ApplicationCommandManager extends CachedManager { type: typeof command.type === 'number' ? command.type : ApplicationCommandTypes[command.type], options: command.options?.map(o => ApplicationCommand.transformOption(o)), default_permission: command.defaultPermission ?? command.default_permission, + default_member_permissions, + dm_permission: command.dmPermission ?? command.dm_permission, }; } } diff --git a/src/structures/ApplicationCommand.js b/src/structures/ApplicationCommand.js index e9276beea637..778df2f9deeb 100644 --- a/src/structures/ApplicationCommand.js +++ b/src/structures/ApplicationCommand.js @@ -3,6 +3,7 @@ const Base = require('./Base'); const ApplicationCommandPermissionsManager = require('../managers/ApplicationCommandPermissionsManager'); const { ApplicationCommandOptionTypes, ApplicationCommandTypes, ChannelTypes } = require('../util/Constants'); +const Permissions = require('../util/Permissions'); const SnowflakeUtil = require('../util/SnowflakeUtil'); /** @@ -120,13 +121,39 @@ class ApplicationCommand extends Base { this.options ??= []; } + /* eslint-disable max-len */ if ('default_permission' in data) { /** * Whether the command is enabled by default when the app is added to a guild * @type {boolean} + * @deprecated Use {@link ApplicationCommand.defaultMemberPermissions} and {@link ApplicationCommand.dmPermission} instead. */ this.defaultPermission = data.default_permission; } + /* eslint-disable max-len */ + + if ('default_member_permissions' in data) { + /** + * The default bitfield used to determine whether this command be used in a guild + * @type {?Readonly} + */ + this.defaultMemberPermissions = data.default_member_permissions + ? new Permissions(BigInt(data.default_member_permissions)).freeze() + : null; + } else { + this.defaultMemberPermissions ??= null; + } + + if ('dm_permission' in data) { + /** + * Whether the command can be used in DMs + * This property is always `null` on guild commands + * @type {?boolean} + */ + this.dmPermission = data.dm_permission; + } else { + this.dmPermission ??= null; + } if ('version' in data) { /** @@ -174,6 +201,9 @@ class ApplicationCommand extends Base { * @property {ApplicationCommandType} [type] The type of the command * @property {ApplicationCommandOptionData[]} [options] Options for the command * @property {boolean} [defaultPermission] Whether the command is enabled by default when the app is added to a guild + * @property {?PermissionResolvable} [defaultMemberPermissions] The bitfield used to determine the default permissions + * a member needs in order to run the command + * @property {boolean} [dmPermission] Whether the command is enabled in DMs */ /** @@ -207,7 +237,7 @@ class ApplicationCommand extends Base { /** * Edits this application command. - * @param {ApplicationCommandData} data The data to update the command with + * @param {Partial} data The data to update the command with * @returns {Promise} * @example * // Edit the description of this command @@ -273,14 +303,35 @@ class ApplicationCommand extends Base { return this.edit({ descriptionLocalizations }); } + /* eslint-disable max-len */ /** * Edits the default permission of this ApplicationCommand * @param {boolean} [defaultPermission=true] The default permission for this command * @returns {Promise} + * @deprecated Use {@link ApplicationCommand#setDefaultMemberPermissions} and {@link ApplicationCommand#setDMPermission} instead. */ setDefaultPermission(defaultPermission = true) { return this.edit({ defaultPermission }); } + /* eslint-enable max-len */ + + /** + * Edits the default member permissions of this ApplicationCommand + * @param {?PermissionResolvable} defaultMemberPermissions The default member permissions required to run this command + * @returns {Promise} + */ + setDefaultMemberPermissions(defaultMemberPermissions) { + return this.edit({ defaultMemberPermissions }); + } + + /** + * Edits the DM permission of this ApplicationCommand + * @param {boolean} [dmPermission=true] Whether the command can be used in DMs + * @returns {Promise} + */ + setDMPermission(dmPermission = true) { + return this.edit({ dmPermission }); + } /** * Edits the options of this ApplicationCommand @@ -317,6 +368,21 @@ class ApplicationCommand extends Base { // If given an id, check if the id matches if (command.id && this.id !== command.id) return false; + let defaultMemberPermissions = null; + let dmPermission = command.dmPermission ?? command.dm_permission; + + if ('default_member_permissions' in command) { + defaultMemberPermissions = command.default_member_permissions + ? new Permissions(BigInt(command.default_member_permissions)).bitfield + : null; + } + + if ('defaultMemberPermissions' in command) { + defaultMemberPermissions = command.defaultMemberPermissions + ? new Permissions(command.defaultMemberPermissions).bitfield + : null; + } + // Check top level parameters const commandType = typeof command.type === 'string' ? command.type : ApplicationCommandTypes[command.type]; if ( @@ -325,6 +391,8 @@ class ApplicationCommand extends Base { ('version' in command && command.version !== this.version) || ('autocomplete' in command && command.autocomplete !== this.autocomplete) || (commandType && commandType !== this.type) || + defaultMemberPermissions !== (this.defaultMemberPermissions?.bitfield ?? null) || + (typeof dmPermission !== 'undefined' && dmPermission !== this.dmPermission) || // Future proof for options being nullable // TODO: remove ?? 0 on each when nullable (command.options?.length ?? 0) !== (this.options?.length ?? 0) || diff --git a/typings/index.d.ts b/typings/index.d.ts index 2cc0936dfeac..a260d1662a0d 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -223,10 +223,13 @@ export class ApplicationCommand extends Base { public applicationId: Snowflake; public readonly createdAt: Date; public readonly createdTimestamp: number; + /** @deprecated Use {@link defaultMemberPermissions} and {@link dmPermission} instead. */ public defaultPermission: boolean; + public defaultMemberPermissions: Readonly | null; public description: string; public descriptionLocalizations: LocalizationMap | null; public descriptionLocalized: string | null; + public dmPermission: boolean | null; public guild: Guild | null; public guildId: Snowflake | null; public readonly manager: ApplicationCommandManager; @@ -245,14 +248,19 @@ export class ApplicationCommand extends Base { public type: ApplicationCommandType; public version: Snowflake; public delete(): Promise>; - public edit(data: ApplicationCommandData): Promise>; + public edit(data: Partial): Promise>; public setName(name: string): Promise>; public setNameLocalizations(nameLocalizations: LocalizationMap): Promise>; public setDescription(description: string): Promise>; public setDescriptionLocalizations( descriptionLocalizations: LocalizationMap, ): Promise>; + /** @deprecated Use {@link setDefaultMemberPermissions} and {@link setDMPermission} instead. */ public setDefaultPermission(defaultPermission?: boolean): Promise>; + public setDefaultMemberPermissions( + defaultMemberPermissions: PermissionResolvable | null, + ): Promise>; + public setDMPermission(dmPermission?: boolean): Promise>; public setOptions(options: ApplicationCommandOptionData[]): Promise>; public equals( command: ApplicationCommand | ApplicationCommandData | RawApplicationCommandData, @@ -3074,11 +3082,11 @@ export class ApplicationCommandManager< public delete(command: ApplicationCommandResolvable, guildId?: Snowflake): Promise; public edit( command: ApplicationCommandResolvable, - data: ApplicationCommandDataResolvable, + data: Partial, ): Promise; public edit( command: ApplicationCommandResolvable, - data: ApplicationCommandDataResolvable, + data: Partial, guildId: Snowflake, ): Promise; public fetch( @@ -3771,7 +3779,10 @@ export interface ApplicationAsset { export interface BaseApplicationCommandData { name: string; nameLocalizations?: LocalizationMap; + /** @deprecated Use {@link defaultMemberPermissions} and {@link dmPermission} instead. */ defaultPermission?: boolean; + defaultMemberPermissions?: PermissionResolvable | null; + dmPermission?: boolean; } export type CommandOptionDataTypeResolvable = ApplicationCommandOptionType | ApplicationCommandOptionTypes; diff --git a/typings/index.test-d.ts b/typings/index.test-d.ts index 519f650efd32..4151e31f6858 100644 --- a/typings/index.test-d.ts +++ b/typings/index.test-d.ts @@ -143,6 +143,11 @@ client.on('ready', async () => { const guildCommandFromGlobal = await client.application?.commands.fetch(guildCommandId, { guildId: testGuildId }); const guildCommandFromGuild = await client.guilds.cache.get(testGuildId)?.commands.fetch(guildCommandId); + await client.application?.commands.edit(globalCommandId, { defaultMemberPermissions: null }); + await globalCommand?.edit({ defaultMemberPermissions: null }); + await globalCommand?.setDefaultMemberPermissions(null); + await guildCommandFromGlobal?.edit({ dmPermission: false }); + // @ts-expect-error await client.guilds.cache.get(testGuildId)?.commands.fetch(guildCommandId, { guildId: testGuildId }); @@ -831,6 +836,14 @@ declare const applicationCommandManager: ApplicationCommandManager; expectType>>( applicationCommandManager.set([applicationCommandData], '0'), ); + + applicationCommandManager.create({ + name: 'yeet', + description: 'abc', + defaultMemberPermissions: 1n, + dmPermission: false, + type: 'CHAT_INPUT', + }); } declare const applicationNonChoiceOptionData: ApplicationCommandOptionData & {