diff --git a/packages/discord.js/src/client/actions/ActionsManager.js b/packages/discord.js/src/client/actions/ActionsManager.js index 584177719..0ab0be1a0 100644 --- a/packages/discord.js/src/client/actions/ActionsManager.js +++ b/packages/discord.js/src/client/actions/ActionsManager.js @@ -4,6 +4,7 @@ class ActionsManager { constructor(client) { this.client = client; + this.register(require('./ApplicationCommandPermissionsUpdate')); this.register(require('./ChannelCreate')); this.register(require('./ChannelDelete')); this.register(require('./ChannelUpdate')); diff --git a/packages/discord.js/src/client/actions/ApplicationCommandPermissionsUpdate.js b/packages/discord.js/src/client/actions/ApplicationCommandPermissionsUpdate.js new file mode 100644 index 000000000..dbda6c505 --- /dev/null +++ b/packages/discord.js/src/client/actions/ApplicationCommandPermissionsUpdate.js @@ -0,0 +1,34 @@ +'use strict'; + +const Action = require('./Action'); +const Events = require('../../util/Events'); + +/** + * The data received in the {@link Client#event:applicationCommandPermissionsUpdate} event + * @typedef {Object} ApplicationCommandPermissionsUpdateData + * @property {Snowflake} id The id of the command or global entity that was updated + * @property {Snowflake} guildId The id of the guild in which permissions were updated + * @property {Snowflake} applicationId The id of the application that owns the command or entity being updated + * @property {ApplicationCommandPermissions} permissions The updated permissions + */ + +class ApplicationCommandPermissionsUpdateAction extends Action { + handle(data) { + const client = this.client; + /** + * Emitted whenever permissions for an application command in a guild were updated. + * This includes permission updates for other applications in addition to the logged in client, + * check `data.applicationId` to verify which application the update is for + * @event Client#applicationCommandPermissionsUpdate + * @param {ApplicationCommandPermissionsUpdateData} data The updated permissions + */ + client.emit(Events.ApplicationCommandPermissionsUpdate, { + permissions: data.permissions, + id: data.id, + guildId: data.guild_id, + applicationId: data.application_id, + }); + } +} + +module.exports = ApplicationCommandPermissionsUpdateAction; diff --git a/packages/discord.js/src/client/websocket/handlers/APPLICATION_COMMAND_PERMISSIONS_UPDATE.js b/packages/discord.js/src/client/websocket/handlers/APPLICATION_COMMAND_PERMISSIONS_UPDATE.js new file mode 100644 index 000000000..73d4ec47f --- /dev/null +++ b/packages/discord.js/src/client/websocket/handlers/APPLICATION_COMMAND_PERMISSIONS_UPDATE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.ApplicationCommandPermissionsUpdate.handle(packet.d); +}; diff --git a/packages/discord.js/src/client/websocket/handlers/index.js b/packages/discord.js/src/client/websocket/handlers/index.js index d7739c138..53892eafa 100644 --- a/packages/discord.js/src/client/websocket/handlers/index.js +++ b/packages/discord.js/src/client/websocket/handlers/index.js @@ -1,53 +1,57 @@ 'use strict'; const handlers = Object.fromEntries([ - ['READY', require('./READY')], - ['RESUMED', require('./RESUMED')], + ['APPLICATION_COMMAND_PERMISSIONS_UPDATE', require('./APPLICATION_COMMAND_PERMISSIONS_UPDATE')], + ['CHANNEL_CREATE', require('./CHANNEL_CREATE')], + ['CHANNEL_DELETE', require('./CHANNEL_DELETE')], + ['CHANNEL_PINS_UPDATE', require('./CHANNEL_PINS_UPDATE')], + ['CHANNEL_UPDATE', require('./CHANNEL_UPDATE')], + ['GUILD_BAN_ADD', require('./GUILD_BAN_ADD')], + ['GUILD_BAN_REMOVE', require('./GUILD_BAN_REMOVE')], ['GUILD_CREATE', require('./GUILD_CREATE')], ['GUILD_DELETE', require('./GUILD_DELETE')], - ['GUILD_UPDATE', require('./GUILD_UPDATE')], - ['INVITE_CREATE', require('./INVITE_CREATE')], - ['INVITE_DELETE', require('./INVITE_DELETE')], + ['GUILD_EMOJIS_UPDATE', require('./GUILD_EMOJIS_UPDATE')], + ['GUILD_INTEGRATIONS_UPDATE', require('./GUILD_INTEGRATIONS_UPDATE')], ['GUILD_MEMBER_ADD', require('./GUILD_MEMBER_ADD')], ['GUILD_MEMBER_REMOVE', require('./GUILD_MEMBER_REMOVE')], - ['GUILD_MEMBER_UPDATE', require('./GUILD_MEMBER_UPDATE')], ['GUILD_MEMBERS_CHUNK', require('./GUILD_MEMBERS_CHUNK')], - ['GUILD_INTEGRATIONS_UPDATE', require('./GUILD_INTEGRATIONS_UPDATE')], + ['GUILD_MEMBER_UPDATE', require('./GUILD_MEMBER_UPDATE')], ['GUILD_ROLE_CREATE', require('./GUILD_ROLE_CREATE')], ['GUILD_ROLE_DELETE', require('./GUILD_ROLE_DELETE')], ['GUILD_ROLE_UPDATE', require('./GUILD_ROLE_UPDATE')], - ['GUILD_BAN_ADD', require('./GUILD_BAN_ADD')], - ['GUILD_BAN_REMOVE', require('./GUILD_BAN_REMOVE')], - ['GUILD_EMOJIS_UPDATE', require('./GUILD_EMOJIS_UPDATE')], - ['CHANNEL_CREATE', require('./CHANNEL_CREATE')], - ['CHANNEL_DELETE', require('./CHANNEL_DELETE')], - ['CHANNEL_UPDATE', require('./CHANNEL_UPDATE')], - ['CHANNEL_PINS_UPDATE', require('./CHANNEL_PINS_UPDATE')], + ['GUILD_STICKERS_UPDATE', require('./GUILD_STICKERS_UPDATE')], + ['GUILD_UPDATE', require('./GUILD_UPDATE')], + ['INTEGRATION_CREATE', require('./INTEGRATION_CREATE')], + ['INTEGRATION_DELETE', require('./INTEGRATION_DELETE')], + ['INTEGRATION_UPDATE', require('./INTEGRATION_UPDATE')], + ['INTERACTION_CREATE', require('./INTERACTION_CREATE')], + ['INVITE_CREATE', require('./INVITE_CREATE')], + ['INVITE_DELETE', require('./INVITE_DELETE')], ['MESSAGE_CREATE', require('./MESSAGE_CREATE')], ['MESSAGE_DELETE', require('./MESSAGE_DELETE')], - ['MESSAGE_UPDATE', require('./MESSAGE_UPDATE')], ['MESSAGE_DELETE_BULK', require('./MESSAGE_DELETE_BULK')], ['MESSAGE_REACTION_ADD', require('./MESSAGE_REACTION_ADD')], ['MESSAGE_REACTION_REMOVE', require('./MESSAGE_REACTION_REMOVE')], ['MESSAGE_REACTION_REMOVE_ALL', require('./MESSAGE_REACTION_REMOVE_ALL')], ['MESSAGE_REACTION_REMOVE_EMOJI', require('./MESSAGE_REACTION_REMOVE_EMOJI')], + ['MESSAGE_UPDATE', require('./MESSAGE_UPDATE')], + ['PRESENCE_UPDATE', require('./PRESENCE_UPDATE')], + ['STAGE_INSTANCE_CREATE', require('./STAGE_INSTANCE_CREATE')], + ['STAGE_INSTANCE_DELETE', require('./STAGE_INSTANCE_DELETE')], + ['STAGE_INSTANCE_UPDATE', require('./STAGE_INSTANCE_UPDATE')], + ['READY', require('./READY')], + ['RESUMED', require('./RESUMED')], ['THREAD_CREATE', require('./THREAD_CREATE')], - ['THREAD_UPDATE', require('./THREAD_UPDATE')], ['THREAD_DELETE', require('./THREAD_DELETE')], ['THREAD_LIST_SYNC', require('./THREAD_LIST_SYNC')], - ['THREAD_MEMBER_UPDATE', require('./THREAD_MEMBER_UPDATE')], ['THREAD_MEMBERS_UPDATE', require('./THREAD_MEMBERS_UPDATE')], - ['USER_UPDATE', require('./USER_UPDATE')], - ['PRESENCE_UPDATE', require('./PRESENCE_UPDATE')], + ['THREAD_MEMBER_UPDATE', require('./THREAD_MEMBER_UPDATE')], + ['THREAD_UPDATE', require('./THREAD_UPDATE')], ['TYPING_START', require('./TYPING_START')], - ['VOICE_STATE_UPDATE', require('./VOICE_STATE_UPDATE')], + ['USER_UPDATE', require('./USER_UPDATE')], ['VOICE_SERVER_UPDATE', require('./VOICE_SERVER_UPDATE')], + ['VOICE_STATE_UPDATE', require('./VOICE_STATE_UPDATE')], ['WEBHOOKS_UPDATE', require('./WEBHOOKS_UPDATE')], - ['INTERACTION_CREATE', require('./INTERACTION_CREATE')], - ['STAGE_INSTANCE_CREATE', require('./STAGE_INSTANCE_CREATE')], - ['STAGE_INSTANCE_UPDATE', require('./STAGE_INSTANCE_UPDATE')], - ['STAGE_INSTANCE_DELETE', require('./STAGE_INSTANCE_DELETE')], - ['GUILD_STICKERS_UPDATE', require('./GUILD_STICKERS_UPDATE')], ['GUILD_SCHEDULED_EVENT_CREATE', require('./GUILD_SCHEDULED_EVENT_CREATE')], ['GUILD_SCHEDULED_EVENT_UPDATE', require('./GUILD_SCHEDULED_EVENT_UPDATE')], ['GUILD_SCHEDULED_EVENT_DELETE', require('./GUILD_SCHEDULED_EVENT_DELETE')], diff --git a/packages/discord.js/src/errors/Messages.js b/packages/discord.js/src/errors/Messages.js index bc348ed9c..15f1de40b 100644 --- a/packages/discord.js/src/errors/Messages.js +++ b/packages/discord.js/src/errors/Messages.js @@ -10,6 +10,8 @@ const Messages = { TOKEN_INVALID: 'An invalid token was provided.', TOKEN_MISSING: 'Request to use token, but token was unavailable to the client.', + APPLICATION_COMMAND_PERMISSIONS_TOKEN_MISSING: + 'Editing application command permissions requires an OAuth2 bearer token, but none was provided.', WS_CLOSE_REQUESTED: 'WebSocket closed due to user request.', WS_CONNECTION_EXISTS: 'There is already an existing WebSocket connection.', @@ -127,7 +129,7 @@ const Messages = { GLOBAL_COMMAND_PERMISSIONS: 'Permissions for global commands may only be fetched or modified by providing a GuildResolvable ' + "or from a guild's application command manager.", - GUILD_UNCACHED_ROLE_RESOLVE: 'Cannot resolve roles from an arbitrary guild, provide an id instead', + GUILD_UNCACHED_ENTITY_RESOLVE: type => `Cannot resolve ${type} from an arbitrary guild, provide an id instead`, INTERACTION_ALREADY_REPLIED: 'The reply to this interaction has already been sent or deferred.', INTERACTION_NOT_REPLIED: 'The reply to this interaction has not been sent or deferred.', diff --git a/packages/discord.js/src/managers/ApplicationCommandManager.js b/packages/discord.js/src/managers/ApplicationCommandManager.js index 5fc288fee..27655cedb 100644 --- a/packages/discord.js/src/managers/ApplicationCommandManager.js +++ b/packages/discord.js/src/managers/ApplicationCommandManager.js @@ -6,6 +6,7 @@ const ApplicationCommandPermissionsManager = require('./ApplicationCommandPermis const CachedManager = require('./CachedManager'); const { TypeError } = require('../errors'); const ApplicationCommand = require('../structures/ApplicationCommand'); +const PermissionsBitField = require('../util/PermissionsBitField'); /** * Manages API methods for application commands and stores their cache. @@ -224,6 +225,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 PermissionsBitField(BigInt(command.default_member_permissions)).bitfield + : command.default_member_permissions; + } + + if ('defaultMemberPermissions' in command) { + default_member_permissions = command.defaultMemberPermissions + ? new PermissionsBitField(command.defaultMemberPermissions).bitfield + : command.defaultMemberPermissions; + } + return { name: command.name, name_localizations: command.nameLocalizations ?? command.name_localizations, @@ -231,7 +246,8 @@ class ApplicationCommandManager extends CachedManager { description_localizations: command.descriptionLocalizations ?? command.description_localizations, type: 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/packages/discord.js/src/managers/ApplicationCommandPermissionsManager.js b/packages/discord.js/src/managers/ApplicationCommandPermissionsManager.js index 7f6ea833f..22fae07e5 100644 --- a/packages/discord.js/src/managers/ApplicationCommandPermissionsManager.js +++ b/packages/discord.js/src/managers/ApplicationCommandPermissionsManager.js @@ -1,7 +1,7 @@ 'use strict'; const { Collection } = require('@discordjs/collection'); -const { RESTJSONErrorCodes, Routes } = require('discord-api-types/v10'); +const { ApplicationCommandPermissionType, RESTJSONErrorCodes, Routes } = require('discord-api-types/v10'); const BaseManager = require('./BaseManager'); const { Error, TypeError } = require('../errors'); @@ -54,21 +54,16 @@ class ApplicationCommandPermissionsManager extends BaseManager { return Routes.guildApplicationCommandsPermissions(this.client.application.id, guildId); } - /** - * Data for setting the permissions of an application command. - * @typedef {Object} ApplicationCommandPermissionData - * @property {Snowflake} id The role or user's id - * @property {ApplicationCommandPermissionType|number} type Whether this permission is for a role or a user - * @property {boolean} permission Whether the role or user has the permission to use this command - */ - + /* eslint-disable max-len */ /** * The object returned when fetching permissions for an application command. * @typedef {Object} ApplicationCommandPermissions - * @property {Snowflake} id The role or user's id + * @property {Snowflake} id The role, user, or channel's id. Can also be a + * {@link https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-constants permission constant}. * @property {ApplicationCommandPermissionType} type Whether this permission is for a role or a user * @property {boolean} permission Whether the role or user has the permission to use this command */ + /* eslint-enable max-len */ /** * Options for managing permissions for one or more Application Commands @@ -82,19 +77,25 @@ class ApplicationCommandPermissionsManager extends BaseManager { */ /** - * Fetches the permissions for one or multiple commands. + * Fetches the permissions for one or multiple commands. Providing the client's id as the "command id" will fetch + * *only* the guild level permissions * @param {BaseApplicationCommandPermissionsOptions} [options] Options used to fetch permissions * @returns {Promise>} * @example * // Fetch permissions for one command * guild.commands.permissions.fetch({ command: '123456789012345678' }) - * .then(perms => console.log(`Fetched permissions for ${perms.length} users`)) + * .then(perms => console.log(`Fetched ${perms.length} overwrites`)) * .catch(console.error); * @example * // Fetch permissions for all commands in a guild * client.application.commands.permissions.fetch({ guild: '123456789012345678' }) * .then(perms => console.log(`Fetched permissions for ${perms.size} commands`)) * .catch(console.error); + * @example + * // Fetch guild level permissions + * guild.commands.permissions.fetch({ command: client.user.id }) + * .then(perms => console.log(`Fetched ${perms.length} guild level permissions`)) + * .catch(console.error); */ async fetch({ guild, command } = {}) { const { guildId, commandId } = this._validateOptions(guild, command); @@ -107,47 +108,42 @@ class ApplicationCommandPermissionsManager extends BaseManager { return data.reduce((coll, perm) => coll.set(perm.id, perm.permissions), new Collection()); } - /** - * Data used for overwriting the permissions for all application commands in a guild. - * @typedef {Object} GuildApplicationCommandPermissionData - * @property {Snowflake} id The command's id - * @property {ApplicationCommandPermissionData[]} permissions The permissions for this command - */ - /** * Options used to set permissions for one or more Application Commands in a guild - * One of `command` AND `permissions`, OR `fullPermissions` is required. - * `fullPermissions` is not a valid option when passing to a manager where `commandId` is non-null - * @typedef {BaseApplicationCommandPermissionsOptions} SetApplicationCommandPermissionsOptions - * @property {ApplicationCommandPermissionData[]} [permissions] The new permissions for the command - * @property {GuildApplicationCommandPermissionData[]} [fullPermissions] The new permissions for all commands - * in a guild When this parameter is set, `permissions` and `command` are ignored + * Omitting the `command` parameter edits the guild wide permissions + * when the manager's `commandId` is `null` + * @typedef {BaseApplicationCommandPermissionsOptions} EditApplicationCommandPermissionsOptions + * @property {ApplicationCommandPermissions[]} permissions The new permissions for the guild or overwrite + * @property {string} token The bearer token to use that authorizes the permission edit */ /** - * Sets the permissions for one or more commands. - * @param {SetApplicationCommandPermissionsOptions} options Options used to set permissions + * Sets the permissions for the guild or a command overwrite. + * @param {EditApplicationCommandPermissionsOptions} options Options used to set permissions * @returns {Promise>} * @example - * // Set the permissions for one command - * client.application.commands.permissions.set({ guild: '892455839386304532', command: '123456789012345678', + * // Set a permission overwrite for a command + * client.application.commands.permissions.set({ + * guild: '892455839386304532', + * command: '123456789012345678', + * token: 'TotallyRealToken', * permissions: [ * { * id: '876543210987654321', - * type: ApplicationCommandOptionType.User, + * type: ApplicationCommandPermissionType.User, * permission: false, * }, * ]}) * .then(console.log) * .catch(console.error); * @example - * // Set the permissions for all commands - * guild.commands.permissions.set({ fullPermissions: [ + * // Set the permissions used for the guild (commands without overwrites) + * guild.commands.permissions.set({ token: 'TotallyRealToken', permissions: [ * { * id: '123456789012345678', * permissions: [{ * id: '876543210987654321', - * type: ApplicationCommandOptionType.User, + * type: ApplicationCommandPermissionType.User, * permission: false, * }], * }, @@ -155,39 +151,34 @@ class ApplicationCommandPermissionsManager extends BaseManager { * .then(console.log) * .catch(console.error); */ - async set({ guild, command, permissions, fullPermissions } = {}) { - const { guildId, commandId } = this._validateOptions(guild, command); + async set({ guild, command, permissions, token } = {}) { + if (!token) { + throw new Error('APPLICATION_COMMAND_PERMISSIONS_TOKEN_MISSING'); + } + let { guildId, commandId } = this._validateOptions(guild, command); - if (commandId) { - if (!Array.isArray(permissions)) { - throw new TypeError('INVALID_TYPE', 'permissions', 'Array of ApplicationCommandPermissionData', true); - } - const data = await this.client.rest.put(this.permissionsPath(guildId, commandId), { body: { permissions } }); - return data.permissions; + if (!Array.isArray(permissions)) { + throw new TypeError('INVALID_TYPE', 'permissions', 'Array of ApplicationCommandPermissions', true); } - if (!Array.isArray(fullPermissions)) { - throw new TypeError('INVALID_TYPE', 'fullPermissions', 'Array of GuildApplicationCommandPermissionData', true); + if (!commandId) { + commandId = this.client.user.id; } - - const data = await this.client.rest.put(this.permissionsPath(guildId), { body: fullPermissions }); - return data.reduce((coll, perm) => coll.set(perm.id, perm.permissions), new Collection()); + const data = await this.client.rest.put(this.permissionsPath(guildId, commandId), { + body: { permissions }, + auth: false, + headers: { Authorization: `Bearer ${token}` }, + }); + return data.permissions; } - /** - * Options used to add permissions to a command - * The `command` parameter is not optional when the managers `commandId` is `null` - * @typedef {BaseApplicationCommandPermissionsOptions} AddApplicationCommandPermissionsOptions - * @property {ApplicationCommandPermissionData[]} permissions The permissions to add to the command - */ - /** * Add permissions to a command. - * @param {AddApplicationCommandPermissionsOptions} options Options used to add permissions + * @param {EditApplicationCommandPermissionsOptions} options Options used to add permissions * @returns {Promise} * @example - * // Block a role from the command permissions - * guild.commands.permissions.add({ command: '123456789012345678', permissions: [ + * // Add a rule to block a role from using a command + * guild.commands.permissions.add({ command: '123456789012345678', token: 'TotallyRealToken', permissions: [ * { * id: '876543211234567890', * type: ApplicationCommandPermissionType.Role, @@ -197,11 +188,16 @@ class ApplicationCommandPermissionsManager extends BaseManager { * .then(console.log) * .catch(console.error); */ - async add({ guild, command, permissions }) { - const { guildId, commandId } = this._validateOptions(guild, command); - if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); + async add({ guild, command, permissions, token } = {}) { + if (!token) { + throw new Error('APPLICATION_COMMAND_PERMISSIONS_TOKEN_MISSING'); + } + let { guildId, commandId } = this._validateOptions(guild, command); + if (!commandId) { + commandId = this.client.user.id; + } if (!Array.isArray(permissions)) { - throw new TypeError('INVALID_TYPE', 'permissions', 'Array of ApplicationCommandPermissionData', true); + throw new TypeError('INVALID_TYPE', 'permissions', 'Array of ApplicationCommandPermissions', true); } let existing = []; @@ -218,17 +214,31 @@ class ApplicationCommandPermissionsManager extends BaseManager { } } - return this.set({ guild: guildId, command: commandId, permissions: newPermissions }); + return this.set({ guild: guildId, command: commandId, permissions: newPermissions, token }); } + /** + * A static snowflake that identifies the everyone role for application command permissions. + * It is the same as the guild id + * @typedef {Snowflake} RolePermissionConstant + */ + + /** + * A static snowflake that identifies the "all channels" entity for application command permissions. + * It will be the result of the calculation `guildId - 1` + * @typedef {Snowflake} ChannelPermissionConstant + */ + /** * Options used to remove permissions from a command - * The `command` parameter is not optional when the managers `commandId` is `null` + * Omitting the `command` parameter removes from the guild wide permissions + * when the managers `commandId` is `null` + * At least one of `users`, `roles`, and `channels` is required * @typedef {BaseApplicationCommandPermissionsOptions} RemoveApplicationCommandPermissionsOptions - * @property {UserResolvable|UserResolvable[]} [users] The user(s) to remove from the command permissions - * One of `users` or `roles` is required - * @property {RoleResolvable|RoleResolvable[]} [roles] The role(s) to remove from the command permissions - * One of `users` or `roles` is required + * @property {string} token The bearer token to use that authorizes the permission removal + * @property {UserResolvable[]} [users] The user(s) to remove + * @property {Array} [roles] The role(s) to remove + * @property {Array} [channels] The channel(s) to remove */ /** @@ -237,59 +247,66 @@ class ApplicationCommandPermissionsManager extends BaseManager { * @returns {Promise} * @example * // Remove a user permission from this command - * guild.commands.permissions.remove({ command: '123456789012345678', users: '876543210123456789' }) + * guild.commands.permissions.remove({ + * command: '123456789012345678', users: '876543210123456789', token: 'TotallyRealToken', + * }) * .then(console.log) * .catch(console.error); * @example * // Remove multiple roles from this command * guild.commands.permissions.remove({ - * command: '123456789012345678', roles: ['876543210123456789', '765432101234567890'] + * command: '123456789012345678', roles: ['876543210123456789', '765432101234567890'], token: 'TotallyRealToken', * }) * .then(console.log) * .catch(console.error); */ - async remove({ guild, command, users, roles }) { - const { guildId, commandId } = this._validateOptions(guild, command); - if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); - - if (!users && !roles) throw new TypeError('INVALID_TYPE', 'users OR roles', 'Array or Resolvable', true); - - let resolvedIds = []; - if (Array.isArray(users)) { - users.forEach(user => { - const userId = this.client.users.resolveId(user); - if (!userId) throw new TypeError('INVALID_ELEMENT', 'Array', 'users', user); - resolvedIds.push(userId); - }); - } else if (users) { - const userId = this.client.users.resolveId(users); - if (!userId) { - throw new TypeError('INVALID_TYPE', 'users', 'Array or UserResolvable'); - } - resolvedIds.push(userId); + async remove({ guild, command, users, roles, channels, token } = {}) { + if (!token) { + throw new Error('APPLICATION_COMMAND_PERMISSIONS_TOKEN_MISSING'); + } + let { guildId, commandId } = this._validateOptions(guild, command); + if (!commandId) { + commandId = this.client.user.id; } + if (!users && !roles && !channels) { + throw new TypeError('INVALID_TYPE', 'users OR roles OR channels', 'Array or Resolvable', true); + } + + let resolvedUserIds = []; + if (Array.isArray(users)) { + for (const user of users) { + const userId = this.client.users.resolveId(user); + if (!userId) throw new TypeError('INVALID_ELEMENT', 'Array', 'users', user); + resolvedUserIds.push(userId); + } + } + + let resolvedRoleIds = []; if (Array.isArray(roles)) { - roles.forEach(role => { + for (const role of roles) { if (typeof role === 'string') { - resolvedIds.push(role); - return; + resolvedRoleIds.push(role); + continue; } - if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE'); + if (!this.guild) throw new Error('GUILD_UNCACHED_ENTITY_RESOLVE', 'roles'); const roleId = this.guild.roles.resolveId(role); if (!roleId) throw new TypeError('INVALID_ELEMENT', 'Array', 'users', role); - resolvedIds.push(roleId); - }); - } else if (roles) { - if (typeof roles === 'string') { - resolvedIds.push(roles); - } else { - if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE'); - const roleId = this.guild.roles.resolveId(roles); - if (!roleId) { - throw new TypeError('INVALID_TYPE', 'users', 'Array or RoleResolvable'); + resolvedRoleIds.push(roleId); + } + } + + let resolvedChannelIds = []; + if (Array.isArray(channels)) { + for (const channel of channels) { + if (typeof channel === 'string') { + resolvedChannelIds.push(channel); + continue; } - resolvedIds.push(roleId); + if (!this.guild) throw new Error('GUILD_UNCACHED_ENTITY_RESOLVE', 'channels'); + const channelId = this.guild.channels.resolveId(channel); + if (!channelId) throw new TypeError('INVALID_ELEMENT', 'Array', 'channels', channel); + resolvedChannelIds.push(channelId); } } @@ -300,22 +317,33 @@ class ApplicationCommandPermissionsManager extends BaseManager { if (error.code !== RESTJSONErrorCodes.UnknownApplicationCommandPermissions) throw error; } - const permissions = existing.filter(perm => !resolvedIds.includes(perm.id)); + const permissions = existing.filter(perm => { + switch (perm.type) { + case ApplicationCommandPermissionType.Role: + return !resolvedRoleIds.includes(perm.id); + case ApplicationCommandPermissionType.User: + return !resolvedUserIds.includes(perm.id); + case ApplicationCommandPermissionType.Channel: + return !resolvedChannelIds.includes(perm.id); + } + return true; + }); - return this.set({ guild: guildId, command: commandId, permissions }); + return this.set({ guild: guildId, command: commandId, permissions, token }); } /** * Options used to check the existence of permissions on a command * The `command` parameter is not optional when the managers `commandId` is `null` * @typedef {BaseApplicationCommandPermissionsOptions} HasApplicationCommandPermissionsOptions - * @property {UserResolvable|RoleResolvable} permissionId The user or role to check if a permission exists for + * @property {ApplicationCommandPermissionIdResolvable} permissionId The entity to check if a permission exists for * on this command. + * @property {ApplicationCommandPermissionType} [permissionType] Check for a specific type of permission */ /** - * Check whether a permission exists for a user or role - * @param {AddApplicationCommandPermissionsOptions} options Options used to check permissions + * Check whether a permission exists for a user, role, or channel + * @param {HasApplicationCommandPermissionsOptions} options Options used to check permissions * @returns {Promise} * @example * // Check whether a user has permission to use a command @@ -323,20 +351,33 @@ class ApplicationCommandPermissionsManager extends BaseManager { * .then(console.log) * .catch(console.error); */ - async has({ guild, command, permissionId }) { + async has({ guild, command, permissionId, permissionType }) { const { guildId, commandId } = this._validateOptions(guild, command); if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); - if (!permissionId) throw new TypeError('INVALID_TYPE', 'permissionId', 'UserResolvable or RoleResolvable'); + if (!permissionId) { + throw new TypeError( + 'INVALID_TYPE', + 'permissionId', + 'UserResolvable, RoleResolvable, ChannelResolvable, or Permission Constant', + ); + } let resolvedId = permissionId; if (typeof permissionId !== 'string') { resolvedId = this.client.users.resolveId(permissionId); if (!resolvedId) { - if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE'); + if (!this.guild) throw new Error('GUILD_UNCACHED_ENTITY_RESOLVE', 'roles'); resolvedId = this.guild.roles.resolveId(permissionId); } if (!resolvedId) { - throw new TypeError('INVALID_TYPE', 'permissionId', 'UserResolvable or RoleResolvable'); + resolvedId = this.guild.channels.resolveId(permissionId); + } + if (!resolvedId) { + throw new TypeError( + 'INVALID_TYPE', + 'permissionId', + 'UserResolvable, RoleResolvable, ChannelResolvable, or Permission Constant', + ); } } @@ -347,7 +388,8 @@ class ApplicationCommandPermissionsManager extends BaseManager { if (error.code !== RESTJSONErrorCodes.UnknownApplicationCommandPermissions) throw error; } - return existing.some(perm => perm.id === resolvedId); + // Check permission type if provided for the single edge case where a channel id is the same as the everyone role id + return existing.some(perm => perm.id === resolvedId && (permissionType ?? perm.type) === perm.type); } _validateOptions(guild, command) { @@ -375,3 +417,8 @@ module.exports = ApplicationCommandPermissionsManager; * @external APIApplicationCommandPermissions * @see {@link https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure} */ + +/** + * Data that resolves to an id used for an application command permission + * @typedef {UserResolvable|RoleResolvable|GuildChannelResolvable|RolePermissionConstant|ChannelPermissionConstant} ApplicationCommandPermissionIdResolvable + */ diff --git a/packages/discord.js/src/structures/ApplicationCommand.js b/packages/discord.js/src/structures/ApplicationCommand.js index eb83fe6dc..4f330d625 100644 --- a/packages/discord.js/src/structures/ApplicationCommand.js +++ b/packages/discord.js/src/structures/ApplicationCommand.js @@ -5,6 +5,7 @@ const { ApplicationCommandOptionType } = require('discord-api-types/v10'); const isEqual = require('fast-deep-equal'); const Base = require('./Base'); const ApplicationCommandPermissionsManager = require('../managers/ApplicationCommandPermissionsManager'); +const PermissionsBitField = require('../util/PermissionsBitField'); /** * Represents an application command. @@ -121,12 +122,27 @@ class ApplicationCommand extends Base { this.options ??= []; } - if ('default_permission' in data) { + if ('default_member_permissions' in data) { /** - * Whether the command is enabled by default when the app is added to a guild - * @type {boolean} + * The default bitfield used to determine whether this command be used in a guild + * @type {?Readonly} */ - this.defaultPermission = data.default_permission; + this.defaultMemberPermissions = data.default_member_permissions + ? new PermissionsBitField(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|null} + */ + this.dmPermission = data.dm_permission; + } else { + this.dmPermission ??= null; } if ('version' in data) { @@ -176,8 +192,9 @@ class ApplicationCommand extends Base { * if type is {@link ApplicationCommandType.ChatInput} * @property {ApplicationCommandType} [type=ApplicationCommandType.ChatInput] The type of the command * @property {ApplicationCommandOptionData[]} [options] Options for the command - * @property {boolean} [defaultPermission=true] 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 */ /** @@ -282,12 +299,21 @@ class ApplicationCommand extends Base { } /** - * Edits the default permission of this ApplicationCommand - * @param {boolean} [defaultPermission=true] The default permission for this command + * Edits the default member permissions of this ApplicationCommand + * @param {PermissionResolvable} defaultMemberPermissions The default member permissions required to run this command * @returns {Promise} */ - setDefaultPermission(defaultPermission = true) { - return this.edit({ defaultPermission }); + 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 }); } /** @@ -325,6 +351,20 @@ 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; + + if ('default_member_permissions' in command) { + defaultMemberPermissions = command.default_member_permissions + ? new PermissionsBitField(BigInt(command.default_member_permissions)).bitfield + : null; + } + + if ('defaultMemberPermissions' in command) { + defaultMemberPermissions = command.defaultMemberPermissions + ? new PermissionsBitField(command.defaultMemberPermissions).bitfield + : null; + } + // Check top level parameters if ( command.name !== this.name || @@ -335,7 +375,8 @@ class ApplicationCommand extends Base { // Future proof for options being nullable // TODO: remove ?? 0 on each when nullable (command.options?.length ?? 0) !== (this.options?.length ?? 0) || - (command.defaultPermission ?? command.default_permission ?? true) !== this.defaultPermission || + defaultMemberPermissions !== (this.defaultMemberPermissions?.bitfield ?? null) || + command.dmPermission !== this.dmPermission || !isEqual(command.nameLocalizations ?? command.name_localizations ?? {}, this.nameLocalizations ?? {}) || !isEqual( command.descriptionLocalizations ?? command.description_localizations ?? {}, diff --git a/packages/discord.js/src/structures/GuildAuditLogs.js b/packages/discord.js/src/structures/GuildAuditLogs.js index edd26ac8d..c637e7bc3 100644 --- a/packages/discord.js/src/structures/GuildAuditLogs.js +++ b/packages/discord.js/src/structures/GuildAuditLogs.js @@ -1,6 +1,7 @@ 'use strict'; const { Collection } = require('@discordjs/collection'); +const ApplicationCommand = require('./ApplicationCommand'); const GuildAuditLogsEntry = require('./GuildAuditLogsEntry'); const Integration = require('./Integration'); const Webhook = require('./Webhook'); @@ -21,6 +22,7 @@ const Util = require('../util/Util'); * * Sticker * * Thread * * GuildScheduledEvent + * * ApplicationCommandPermission * @typedef {string} AuditLogTargetType */ @@ -66,6 +68,18 @@ class GuildAuditLogs { new Collection(), ); + /** + * Cached application commands, includes application commands from other applications + * @type {Collection} + * @private + */ + this.applicationCommands = new Collection(); + if (data.application_commands) { + for (const command of data.application_commands) { + this.applicationCommands.set(command.id, new ApplicationCommand(guild.client, command, guild)); + } + } + /** * The entries for this guild's audit logs * @type {Collection} diff --git a/packages/discord.js/src/structures/GuildAuditLogsEntry.js b/packages/discord.js/src/structures/GuildAuditLogsEntry.js index ecc345519..92ab758f0 100644 --- a/packages/discord.js/src/structures/GuildAuditLogsEntry.js +++ b/packages/discord.js/src/structures/GuildAuditLogsEntry.js @@ -26,6 +26,7 @@ const Targets = { StageInstance: 'StageInstance', Sticker: 'Sticker', Thread: 'Thread', + ApplicationCommand: 'ApplicationCommand', Unknown: 'Unknown', }; @@ -44,10 +45,11 @@ const Targets = { * * A sticker * * A guild scheduled event * * A thread - * * An object with an id key if target was deleted + * * An application command + * * An object with an id key if target was deleted or fake entity * * An object where the keys represent either the new value or the old value * @typedef {?(Object|Guild|Channel|User|Role|Invite|Webhook|GuildEmoji|Message|Integration|StageInstance|Sticker| - * GuildScheduledEvent)} AuditLogEntryTarget + * GuildScheduledEvent|ApplicationCommand)} AuditLogEntryTarget */ /** @@ -110,6 +112,8 @@ class GuildAuditLogsEntry { * An entry in the audit log representing a specific change. * @typedef {Object} AuditLogChange * @property {string} key The property that was changed, e.g. `nick` for nickname changes + * For application command permissions updates the key is the id of the user, channel, + * role, or a permission constant that was updated instead of an actual property name * @property {*} [old] The old value of the change, e.g. for nicknames, the old nickname * @property {*} [new] The new value of the change, e.g. for nicknames, the new nickname */ @@ -194,6 +198,13 @@ class GuildAuditLogsEntry { }; break; + case AuditLogEvent.ApplicationCommandPermissionUpdate: + this.extra = { + applicationId: data.options.application_id, + guild: guild.client.guilds.cache.get(data.options.guild_id) ?? { id: data.options.guild_id }, + }; + break; + default: break; } @@ -321,6 +332,8 @@ class GuildAuditLogsEntry { { id: data.target_id, guild_id: guild.id }, ), ); + } else if (targetType === Targets.ApplicationCommand) { + this.target = logs.applicationCommands.get(data.target_id) ?? { id: data.target_id }; } else if (data.target_id) { this.target = guild[`${targetType.toLowerCase()}s`]?.cache.get(data.target_id) ?? { id: data.target_id }; } @@ -345,6 +358,7 @@ class GuildAuditLogsEntry { if (target < 100) return Targets.Sticker; if (target < 110) return Targets.GuildScheduledEvent; if (target < 120) return Targets.Thread; + if (target < 130) return Targets.ApplicationCommand; return Targets.Unknown; } @@ -417,6 +431,7 @@ class GuildAuditLogsEntry { AuditLogEvent.StickerUpdate, AuditLogEvent.GuildScheduledEventUpdate, AuditLogEvent.ThreadUpdate, + AuditLogEvent.ApplicationCommandPermissionUpdate, ].includes(action) ) { return 'Update'; diff --git a/packages/discord.js/src/util/Events.js b/packages/discord.js/src/util/Events.js index 11d980d61..63db81d8c 100644 --- a/packages/discord.js/src/util/Events.js +++ b/packages/discord.js/src/util/Events.js @@ -1,72 +1,73 @@ 'use strict'; module.exports = { + ApplicationCommandPermissionsUpdate: 'applicationCommandPermissionsUpdate', + CacheSweep: 'cacheSweep', + ChannelCreate: 'channelCreate', + ChannelDelete: 'channelDelete', + ChannelPinsUpdate: 'channelPinsUpdate', + ChannelUpdate: 'channelUpdate', ClientReady: 'ready', + Debug: 'debug', + Error: 'error', + GuildBanAdd: 'guildBanAdd', + GuildBanRemove: 'guildBanRemove', GuildCreate: 'guildCreate', GuildDelete: 'guildDelete', - GuildUpdate: 'guildUpdate', - GuildUnavailable: 'guildUnavailable', - GuildMemberAdd: 'guildMemberAdd', - GuildMemberRemove: 'guildMemberRemove', - GuildMemberUpdate: 'guildMemberUpdate', - GuildMemberAvailable: 'guildMemberAvailable', - GuildMembersChunk: 'guildMembersChunk', - GuildIntegrationsUpdate: 'guildIntegrationsUpdate', - GuildRoleCreate: 'roleCreate', - GuildRoleDelete: 'roleDelete', - InviteCreate: 'inviteCreate', - InviteDelete: 'inviteDelete', - GuildRoleUpdate: 'roleUpdate', GuildEmojiCreate: 'emojiCreate', GuildEmojiDelete: 'emojiDelete', GuildEmojiUpdate: 'emojiUpdate', - GuildBanAdd: 'guildBanAdd', - GuildBanRemove: 'guildBanRemove', - ChannelCreate: 'channelCreate', - ChannelDelete: 'channelDelete', - ChannelUpdate: 'channelUpdate', - ChannelPinsUpdate: 'channelPinsUpdate', + GuildIntegrationsUpdate: 'guildIntegrationsUpdate', + GuildMemberAdd: 'guildMemberAdd', + GuildMemberAvailable: 'guildMemberAvailable', + GuildMemberRemove: 'guildMemberRemove', + GuildMembersChunk: 'guildMembersChunk', + GuildMemberUpdate: 'guildMemberUpdate', + GuildRoleCreate: 'roleCreate', + GuildRoleDelete: 'roleDelete', + GuildRoleUpdate: 'roleUpdate', + GuildScheduledEventCreate: 'guildScheduledEventCreate', + GuildScheduledEventDelete: 'guildScheduledEventDelete', + GuildScheduledEventUpdate: 'guildScheduledEventUpdate', + GuildScheduledEventUserAdd: 'guildScheduledEventUserAdd', + GuildScheduledEventUserRemove: 'guildScheduledEventUserRemove', + GuildStickerCreate: 'stickerCreate', + GuildStickerDelete: 'stickerDelete', + GuildStickerUpdate: 'stickerUpdate', + GuildUnavailable: 'guildUnavailable', + GuildUpdate: 'guildUpdate', + InteractionCreate: 'interactionCreate', + Invalidated: 'invalidated', + InviteCreate: 'inviteCreate', + InviteDelete: 'inviteDelete', + MessageBulkDelete: 'messageDeleteBulk', MessageCreate: 'messageCreate', MessageDelete: 'messageDelete', - MessageUpdate: 'messageUpdate', - MessageBulkDelete: 'messageDeleteBulk', MessageReactionAdd: 'messageReactionAdd', MessageReactionRemove: 'messageReactionRemove', MessageReactionRemoveAll: 'messageReactionRemoveAll', MessageReactionRemoveEmoji: 'messageReactionRemoveEmoji', - ThreadCreate: 'threadCreate', - ThreadDelete: 'threadDelete', - ThreadUpdate: 'threadUpdate', - ThreadListSync: 'threadListSync', - ThreadMemberUpdate: 'threadMemberUpdate', - ThreadMembersUpdate: 'threadMembersUpdate', - UserUpdate: 'userUpdate', + MessageUpdate: 'messageUpdate', PresenceUpdate: 'presenceUpdate', - VoiceServerUpdate: 'voiceServerUpdate', - VoiceStateUpdate: 'voiceStateUpdate', - TypingStart: 'typingStart', - WebhooksUpdate: 'webhookUpdate', - InteractionCreate: 'interactionCreate', - Error: 'error', - Warn: 'warn', - Debug: 'debug', - CacheSweep: 'cacheSweep', + Raw: 'raw', ShardDisconnect: 'shardDisconnect', ShardError: 'shardError', - ShardReconnecting: 'shardReconnecting', ShardReady: 'shardReady', + ShardReconnecting: 'shardReconnecting', ShardResume: 'shardResume', - Invalidated: 'invalidated', - Raw: 'raw', StageInstanceCreate: 'stageInstanceCreate', - StageInstanceUpdate: 'stageInstanceUpdate', StageInstanceDelete: 'stageInstanceDelete', - GuildStickerCreate: 'stickerCreate', - GuildStickerDelete: 'stickerDelete', - GuildStickerUpdate: 'stickerUpdate', - GuildScheduledEventCreate: 'guildScheduledEventCreate', - GuildScheduledEventUpdate: 'guildScheduledEventUpdate', - GuildScheduledEventDelete: 'guildScheduledEventDelete', - GuildScheduledEventUserAdd: 'guildScheduledEventUserAdd', - GuildScheduledEventUserRemove: 'guildScheduledEventUserRemove', + StageInstanceUpdate: 'stageInstanceUpdate', + ThreadCreate: 'threadCreate', + ThreadDelete: 'threadDelete', + ThreadListSync: 'threadListSync', + ThreadMembersUpdate: 'threadMembersUpdate', + ThreadMemberUpdate: 'threadMemberUpdate', + ThreadUpdate: 'threadUpdate', + TypingStart: 'typingStart', + UserUpdate: 'userUpdate', + VoiceServerUpdate: 'voiceServerUpdate', + VoiceStateUpdate: 'voiceStateUpdate', + Warn: 'warn', + WebhooksUpdate: 'webhookUpdate', }; diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index 756038a70..801147459 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -304,10 +304,11 @@ export class ApplicationCommand extends Base { public applicationId: Snowflake; public get createdAt(): Date; public get createdTimestamp(): number; - 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 get manager(): ApplicationCommandManager; @@ -317,7 +318,6 @@ export class ApplicationCommand extends Base { public nameLocalized: string | null; public options: (ApplicationCommandOption & { nameLocalized?: string; descriptionLocalized?: string })[]; public permissions: ApplicationCommandPermissionsManager< - PermissionsFetchType, PermissionsFetchType, PermissionsFetchType, Guild | null, @@ -333,7 +333,10 @@ export class ApplicationCommand extends Base { public setDescriptionLocalizations( descriptionLocalizations: LocalizationMap, ): Promise>; - public setDefaultPermission(defaultPermission?: boolean): Promise>; + public setDefaultMemberPermissions( + defaultMemberPermissions: PermissionResolvable, + ): Promise>; + public setDMPermissoin(dmPermission?: boolean): Promise>; public setOptions(options: ApplicationCommandOptionData[]): Promise>; public equals( command: ApplicationCommand | ApplicationCommandData | RawApplicationCommandData, @@ -1173,6 +1176,7 @@ export class Guild extends AnonymousGuild { export class GuildAuditLogs { private constructor(guild: Guild, data: RawGuildAuditLogData); + private applicationCommands: Collection; private webhooks: Collection; private integrations: Collection; private guildScheduledEvents: Collection; @@ -3016,7 +3020,6 @@ export class ApplicationCommandManager< public permissions: ApplicationCommandPermissionsManager< { command?: ApplicationCommandResolvable } & PermissionsOptionsExtras, { command: ApplicationCommandResolvable } & PermissionsOptionsExtras, - PermissionsOptionsExtras, PermissionsGuildType, null >; @@ -3055,7 +3058,6 @@ export class ApplicationCommandManager< export class ApplicationCommandPermissionsManager< BaseOptions, FetchSingleOptions, - FullPermissionsOptions, GuildType, CommandIdType, > extends BaseManager { @@ -3066,30 +3068,40 @@ export class ApplicationCommandPermissionsManager< public guild: GuildType; public guildId: Snowflake | null; public add( - options: FetchSingleOptions & { permissions: ApplicationCommandPermissionData[] }, + options: FetchSingleOptions & EditApplicationCommandPermissionsMixin, ): Promise; - public has(options: FetchSingleOptions & { permissionId: UserResolvable | RoleResolvable }): Promise; + public has( + options: FetchSingleOptions & { + permissionId: ApplicationCommandPermissionIdResolvable; + permissionType?: ApplicationCommandPermissionType; + }, + ): Promise; public fetch(options: FetchSingleOptions): Promise; public fetch(options: BaseOptions): Promise>; public remove( options: | (FetchSingleOptions & { - users: UserResolvable | UserResolvable[]; - roles?: RoleResolvable | RoleResolvable[]; + token: string; + channels?: (GuildChannelResolvable | ChannelPermissionConstant)[]; + roles?: (RoleResolvable | RolePermissionConstant)[]; + users: UserResolvable[]; }) | (FetchSingleOptions & { - users?: UserResolvable | UserResolvable[]; - roles: RoleResolvable | RoleResolvable[]; + token: string; + channels?: (GuildChannelResolvable | ChannelPermissionConstant)[]; + roles: (RoleResolvable | RolePermissionConstant)[]; + users?: UserResolvable[]; + }) + | (FetchSingleOptions & { + token: string; + channels: (GuildChannelResolvable | ChannelPermissionConstant)[]; + roles?: (RoleResolvable | RolePermissionConstant)[]; + users?: UserResolvable[]; }), ): Promise; public set( - options: FetchSingleOptions & { permissions: ApplicationCommandPermissionData[] }, + options: FetchSingleOptions & EditApplicationCommandPermissionsMixin, ): Promise; - public set( - options: FullPermissionsOptions & { - fullPermissions: GuildApplicationCommandPermissionData[]; - }, - ): Promise>; private permissionsPath(guildId: Snowflake, commandId?: Snowflake): string; } @@ -3517,7 +3529,8 @@ export type AllowedThreadTypeForTextChannel = ChannelType.GuildPublicThread | Ch export interface BaseApplicationCommandData { name: string; nameLocalizations?: LocalizationMap; - defaultPermission?: boolean; + dmPermission?: boolean; + defaultMemberPermissions?: PermissionResolvable; } export interface AttachmentData { @@ -3682,16 +3695,35 @@ export interface ApplicationCommandOptionChoiceData { value: string | number; } -export interface ApplicationCommandPermissionData { +export interface ApplicationCommandPermissions { id: Snowflake; type: ApplicationCommandPermissionType; permission: boolean; } -export interface ApplicationCommandPermissions extends ApplicationCommandPermissionData { - type: ApplicationCommandPermissionType; +export interface ApplicationCommandPermissionsUpdateData { + id: Snowflake; + guildId: Snowflake; + applicationId: Snowflake; + permissions: ApplicationCommandPermissions; } +export interface EditApplicationCommandPermissionsMixin { + permissions: ApplicationCommandPermissions[]; + token: string; +} + +export type ChannelPermissionConstant = Snowflake; + +export type RolePermissionConstant = Snowflake; + +export type ApplicationCommandPermissionIdResolvable = + | GuildChannelResolvable + | RoleResolvable + | UserResolvable + | ChannelPermissionConstant + | RolePermissionConstant; + export type ApplicationCommandResolvable = ApplicationCommand | Snowflake; export type ApplicationFlagsString = keyof typeof ApplicationFlags; @@ -3859,6 +3891,7 @@ export interface WebhookCreateOptions extends ChannelWebhookCreateOptions { } export interface ClientEvents { + applicationCommandPermissionsUpdate: [data: ApplicationCommandPermissionsUpdateData]; cacheSweep: [message: string]; channelCreate: [channel: NonThreadGuildBasedChannel]; channelDelete: [channel: DMChannel | NonThreadGuildBasedChannel]; @@ -4341,11 +4374,6 @@ export interface AttachmentPayload { export type GlobalSweepFilter = () => ((value: V, key: K, collection: Collection) => boolean) | null; -export interface GuildApplicationCommandPermissionData { - id: Snowflake; - permissions: ApplicationCommandPermissionData[]; -} - interface GuildAuditLogsTypes { [AuditLogEvent.GuildUpdate]: ['Guild', 'Update']; [AuditLogEvent.ChannelCreate]: ['Channel', 'Create']; @@ -4394,6 +4422,7 @@ interface GuildAuditLogsTypes { [AuditLogEvent.ThreadCreate]: ['Thread', 'Create']; [AuditLogEvent.ThreadUpdate]: ['Thread', 'Update']; [AuditLogEvent.ThreadDelete]: ['Thread', 'Delete']; + [AuditLogEvent.ApplicationCommandPermissionUpdate]: ['ApplicationCommand', 'Update']; } export type GuildAuditLogsActionType = GuildAuditLogsTypes[keyof GuildAuditLogsTypes][1] | 'All'; @@ -4424,6 +4453,7 @@ export interface GuildAuditLogsEntryExtraField { [AuditLogEvent.StageInstanceCreate]: StageChannel | { id: Snowflake }; [AuditLogEvent.StageInstanceDelete]: StageChannel | { id: Snowflake }; [AuditLogEvent.StageInstanceUpdate]: StageChannel | { id: Snowflake }; + [AuditLogEvent.ApplicationCommandPermissionUpdate]: { applicationId: Snowflake; guild: Guild | { id: Snowflake } }; } export interface GuildAuditLogsEntryTargetField { @@ -4438,6 +4468,7 @@ export interface GuildAuditLogsEntryTargetField { diff --git a/packages/discord.js/typings/index.test-d.ts b/packages/discord.js/typings/index.test-d.ts index 743f0b35f..f86cddf6f 100644 --- a/packages/discord.js/typings/index.test-d.ts +++ b/packages/discord.js/typings/index.test-d.ts @@ -175,44 +175,55 @@ client.on('ready', async () => { // Test command permissions const globalPermissionsManager = client.application?.commands.permissions; const guildPermissionsManager = client.guilds.cache.get(testGuildId)?.commands.permissions; - const originalPermissions = await client.application?.commands.permissions.fetch({ guild: testGuildId }); // Permissions from global manager await globalPermissionsManager?.add({ command: globalCommandId, guild: testGuildId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); await globalPermissionsManager?.has({ command: globalCommandId, guild: testGuildId, permissionId: testGuildId }); await globalPermissionsManager?.fetch({ guild: testGuildId }); await globalPermissionsManager?.fetch({ command: globalCommandId, guild: testGuildId }); - await globalPermissionsManager?.remove({ command: globalCommandId, guild: testGuildId, roles: [testGuildId] }); - await globalPermissionsManager?.remove({ command: globalCommandId, guild: testGuildId, users: [testUserId] }); + await globalPermissionsManager?.remove({ + command: globalCommandId, + guild: testGuildId, + roles: [testGuildId], + token: 'VeryRealToken', + }); + await globalPermissionsManager?.remove({ + command: globalCommandId, + guild: testGuildId, + users: [testUserId], + token: 'VeryRealToken', + }); + await globalPermissionsManager?.remove({ + command: globalCommandId, + guild: testGuildId, + channels: [testGuildId], + token: 'VeryRealToken', + }); await globalPermissionsManager?.remove({ command: globalCommandId, guild: testGuildId, roles: [testGuildId], users: [testUserId], + channels: [testGuildId], + token: 'VeryRealToken', }); await globalPermissionsManager?.set({ command: globalCommandId, guild: testGuildId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], - }); - await globalPermissionsManager?.set({ - guild: testGuildId, - fullPermissions: [ - { - id: globalCommandId, - permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], - }, - ], + token: 'VeryRealToken', }); // @ts-expect-error await globalPermissionsManager?.add({ command: globalCommandId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); // @ts-expect-error await globalPermissionsManager?.has({ command: globalCommandId, permissionId: testGuildId }); @@ -221,68 +232,72 @@ client.on('ready', async () => { // @ts-expect-error await globalPermissionsManager?.fetch({ command: globalCommandId }); // @ts-expect-error - await globalPermissionsManager?.remove({ command: globalCommandId, roles: [testGuildId] }); + await globalPermissionsManager?.remove({ command: globalCommandId, roles: [testGuildId], token: 'VeryRealToken' }); // @ts-expect-error - await globalPermissionsManager?.remove({ command: globalCommandId, users: [testUserId] }); + await globalPermissionsManager?.remove({ command: globalCommandId, users: [testUserId], token: 'VeryRealToken' }); // @ts-expect-error - await globalPermissionsManager?.remove({ command: globalCommandId, roles: [testGuildId], users: [testUserId] }); - // @ts-expect-error - await globalPermissionsManager?.set({ + await globalPermissionsManager?.remove({ command: globalCommandId, - permissions: [{ type: 'Role', id: testGuildId, permission: true }], - }); - // @ts-expect-error - await globalPermissionsManager?.set({ - fullPermissions: [{ id: globalCommandId, permissions: [{ type: 'Role', id: testGuildId, permission: true }] }], + roles: [testGuildId], + users: [testUserId], + token: 'VeryRealToken', }); // @ts-expect-error await globalPermissionsManager?.set({ command: globalCommandId, - guild: testGuildId, - fullPermissions: [{ id: globalCommandId, permissions: [{ type: 'Role', id: testGuildId, permission: true }] }], + permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); // @ts-expect-error await globalPermissionsManager?.add({ guild: testGuildId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); // @ts-expect-error await globalPermissionsManager?.has({ guild: testGuildId, permissionId: testGuildId }); // @ts-expect-error - await globalPermissionsManager?.remove({ guild: testGuildId, roles: [testGuildId] }); + await globalPermissionsManager?.remove({ guild: testGuildId, roles: [testGuildId], token: 'VeryRealToken' }); // @ts-expect-error - await globalPermissionsManager?.remove({ guild: testGuildId, users: [testUserId] }); + await globalPermissionsManager?.remove({ guild: testGuildId, users: [testUserId], token: 'VeryRealToken' }); // @ts-expect-error - await globalPermissionsManager?.remove({ guild: testGuildId, roles: [testGuildId], users: [testUserId] }); + await globalPermissionsManager?.remove({ + guild: testGuildId, + roles: [testGuildId], + users: [testUserId], + token: 'VeryRealToken', + }); // @ts-expect-error await globalPermissionsManager?.set({ guild: testGuildId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); // Permissions from guild manager await guildPermissionsManager?.add({ command: globalCommandId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); await guildPermissionsManager?.has({ command: globalCommandId, permissionId: testGuildId }); await guildPermissionsManager?.fetch({}); await guildPermissionsManager?.fetch({ command: globalCommandId }); - await guildPermissionsManager?.remove({ command: globalCommandId, roles: [testGuildId] }); - await guildPermissionsManager?.remove({ command: globalCommandId, users: [testUserId] }); - await guildPermissionsManager?.remove({ command: globalCommandId, roles: [testGuildId], users: [testUserId] }); + await guildPermissionsManager?.remove({ command: globalCommandId, roles: [testGuildId], token: 'VeryRealToken' }); + await guildPermissionsManager?.remove({ command: globalCommandId, users: [testUserId], token: 'VeryRealToken' }); + await guildPermissionsManager?.remove({ command: globalCommandId, channels: [testGuildId], token: 'VeryRealToken' }); + await guildPermissionsManager?.remove({ + command: globalCommandId, + roles: [testGuildId], + users: [testUserId], + channels: [testGuildId], + token: 'VeryRealToken', + }); await guildPermissionsManager?.set({ command: globalCommandId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], - }); - await guildPermissionsManager?.set({ - fullPermissions: [ - { - id: globalCommandId, - permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], - }, - ], + token: 'VeryRealToken', }); await guildPermissionsManager?.add({ @@ -290,6 +305,7 @@ client.on('ready', async () => { // @ts-expect-error guild: testGuildId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); // @ts-expect-error await guildPermissionsManager?.has({ command: globalCommandId, guild: testGuildId, permissionId: testGuildId }); @@ -297,74 +313,75 @@ client.on('ready', async () => { await guildPermissionsManager?.fetch({ guild: testGuildId }); // @ts-expect-error await guildPermissionsManager?.fetch({ command: globalCommandId, guild: testGuildId }); - // @ts-expect-error - await guildPermissionsManager?.remove({ command: globalCommandId, guild: testGuildId, roles: [testGuildId] }); - // @ts-expect-error - await guildPermissionsManager?.remove({ command: globalCommandId, guild: testGuildId, users: [testUserId] }); + await guildPermissionsManager?.remove({ + command: globalCommandId, + // @ts-expect-error + guild: testGuildId, + roles: [testGuildId], + token: 'VeryRealToken', + }); + await guildPermissionsManager?.remove({ + command: globalCommandId, + // @ts-expect-error + guild: testGuildId, + users: [testUserId], + token: 'VeryRealToken', + }); await guildPermissionsManager?.remove({ command: globalCommandId, // @ts-expect-error guild: testGuildId, roles: [testGuildId], users: [testUserId], + token: 'VeryRealToken', }); - // @ts-expect-error await guildPermissionsManager?.set({ command: globalCommandId, - guild: testGuildId, - permissions: [{ type: 'Role', id: testGuildId, permission: true }], - }); - await guildPermissionsManager?.set({ // @ts-expect-error guild: testGuildId, - fullPermissions: [ - { - id: globalCommandId, - permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], - }, - ], + permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); // @ts-expect-error await guildPermissionsManager?.add({ permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); // @ts-expect-error await guildPermissionsManager?.has({ permissionId: testGuildId }); // @ts-expect-error - await guildPermissionsManager?.remove({ roles: [testGuildId] }); + await guildPermissionsManager?.remove({ roles: [testGuildId], token: 'VeryRealToken' }); // @ts-expect-error - await guildPermissionsManager?.remove({ users: [testUserId] }); + await guildPermissionsManager?.remove({ users: [testUserId], token: 'VeryRealToken' }); // @ts-expect-error - await guildPermissionsManager?.remove({ roles: [testGuildId], users: [testUserId] }); + await guildPermissionsManager?.remove({ roles: [testGuildId], users: [testUserId], token: 'VeryRealToken' }); // @ts-expect-error await guildPermissionsManager?.set({ permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], - }); - // @ts-expect-error - await guildPermissionsManager?.set({ - command: globalCommandId, - fullPermissions: [ - { - id: globalCommandId, - permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], - }, - ], + token: 'VeryRealToken', }); // Permissions from cached global ApplicationCommand await globalCommand?.permissions.add({ guild: testGuildId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); await globalCommand?.permissions.has({ guild: testGuildId, permissionId: testGuildId }); await globalCommand?.permissions.fetch({ guild: testGuildId }); - await globalCommand?.permissions.remove({ guild: testGuildId, roles: [testGuildId] }); - await globalCommand?.permissions.remove({ guild: testGuildId, users: [testUserId] }); - await globalCommand?.permissions.remove({ guild: testGuildId, roles: [testGuildId], users: [testUserId] }); + await globalCommand?.permissions.remove({ guild: testGuildId, roles: [testGuildId], token: 'VeryRealToken' }); + await globalCommand?.permissions.remove({ guild: testGuildId, users: [testUserId], token: 'VeryRealToken' }); + await globalCommand?.permissions.remove({ + guild: testGuildId, + roles: [testGuildId], + users: [testUserId], + token: 'VeryRealToken', + }); await globalCommand?.permissions.set({ guild: testGuildId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); await globalCommand?.permissions.add({ @@ -372,43 +389,62 @@ client.on('ready', async () => { command: globalCommandId, guild: testGuildId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', + }); + await globalCommand?.permissions.has({ + // @ts-expect-error + command: globalCommandId, + guild: testGuildId, + permissionId: testGuildId, + token: 'VeryRealToken', }); // @ts-expect-error - await globalCommand?.permissions.has({ command: globalCommandId, guild: testGuildId, permissionId: testGuildId }); - // @ts-expect-error - await globalCommand?.permissions.fetch({ command: globalCommandId, guild: testGuildId }); - // @ts-expect-error - await globalCommand?.permissions.remove({ command: globalCommandId, guild: testGuildId, roles: [testGuildId] }); - // @ts-expect-error - await globalCommand?.permissions.remove({ command: globalCommandId, guild: testGuildId, users: [testUserId] }); + await globalCommand?.permissions.fetch({ command: globalCommandId, guild: testGuildId, token: 'VeryRealToken' }); + await globalCommand?.permissions.remove({ + // @ts-expect-error + command: globalCommandId, + guild: testGuildId, + roles: [testGuildId], + token: 'VeryRealToken', + }); + await globalCommand?.permissions.remove({ + // @ts-expect-error + command: globalCommandId, + guild: testGuildId, + users: [testUserId], + token: 'VeryRealToken', + }); await globalCommand?.permissions.remove({ // @ts-expect-error command: globalCommandId, guild: testGuildId, roles: [testGuildId], users: [testUserId], + token: 'VeryRealToken', }); await globalCommand?.permissions.set({ // @ts-expect-error command: globalCommandId, guild: testGuildId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); // @ts-expect-error await globalCommand?.permissions.add({ permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); // @ts-expect-error await globalCommand?.permissions.has({ permissionId: testGuildId }); // @ts-expect-error await globalCommand?.permissions.fetch({}); // @ts-expect-error - await globalCommand?.permissions.remove({ roles: [testGuildId] }); + await globalCommand?.permissions.remove({ roles: [testGuildId], token: 'VeryRealToken' }); // @ts-expect-error - await globalCommand?.permissions.remove({ users: [testUserId] }); + await globalCommand?.permissions.remove({ users: [testUserId], token: 'VeryRealToken' }); // @ts-expect-error - await globalCommand?.permissions.remove({ roles: [testGuildId], users: [testUserId] }); + await globalCommand?.permissions.remove({ roles: [testGuildId], users: [testUserId], token: 'VeryRealToken' }); // @ts-expect-error await globalCommand?.permissions.set({ permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], @@ -417,115 +453,162 @@ client.on('ready', async () => { // Permissions from cached guild ApplicationCommand await guildCommandFromGlobal?.permissions.add({ permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); await guildCommandFromGlobal?.permissions.has({ permissionId: testGuildId }); await guildCommandFromGlobal?.permissions.fetch({}); - await guildCommandFromGlobal?.permissions.remove({ roles: [testGuildId] }); - await guildCommandFromGlobal?.permissions.remove({ users: [testUserId] }); - await guildCommandFromGlobal?.permissions.remove({ roles: [testGuildId], users: [testUserId] }); + await guildCommandFromGlobal?.permissions.remove({ roles: [testGuildId], token: 'VeryRealToken' }); + await guildCommandFromGlobal?.permissions.remove({ users: [testUserId], token: 'VeryRealToken' }); + await guildCommandFromGlobal?.permissions.remove({ + roles: [testGuildId], + users: [testUserId], + token: 'VeryRealToken', + }); await guildCommandFromGlobal?.permissions.set({ permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); await guildCommandFromGlobal?.permissions.add({ // @ts-expect-error command: globalCommandId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); // @ts-expect-error await guildCommandFromGlobal?.permissions.has({ command: guildCommandId, permissionId: testGuildId }); - // @ts-expect-error - await guildCommandFromGlobal?.permissions.remove({ command: guildCommandId, roles: [testGuildId] }); - // @ts-expect-error - await guildCommandFromGlobal?.permissions.remove({ command: guildCommandId, users: [testUserId] }); + await guildCommandFromGlobal?.permissions.remove({ + // @ts-expect-error + command: guildCommandId, + roles: [testGuildId], + token: 'VeryRealToken', + }); + await guildCommandFromGlobal?.permissions.remove({ + // @ts-expect-error + command: guildCommandId, + users: [testUserId], + token: 'VeryRealToken', + }); await guildCommandFromGlobal?.permissions.remove({ // @ts-expect-error command: guildCommandId, roles: [testGuildId], users: [testUserId], + token: 'VeryRealToken', }); await guildCommandFromGlobal?.permissions.set({ // @ts-expect-error command: guildCommandId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); await guildCommandFromGlobal?.permissions.add({ // @ts-expect-error guild: testGuildId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); // @ts-expect-error await guildCommandFromGlobal?.permissions.has({ guild: testGuildId, permissionId: testGuildId }); + await guildCommandFromGlobal?.permissions.remove({ + // @ts-expect-error + guild: testGuildId, + roles: [testGuildId], + token: 'VeryRealToken', + }); // @ts-expect-error - await guildCommandFromGlobal?.permissions.remove({ guild: testGuildId, roles: [testGuildId] }); - // @ts-expect-error - await guildCommandFromGlobal?.permissions.remove({ guild: testGuildId, users: [testUserId] }); - // @ts-expect-error - await guildCommandFromGlobal?.permissions.remove({ guild: testGuildId, roles: [testGuildId], users: [testUserId] }); + await guildCommandFromGlobal?.permissions.remove({ guild: testGuildId, users: [testUserId], token: 'VeryRealToken' }); + await guildCommandFromGlobal?.permissions.remove({ + // @ts-expect-error + guild: testGuildId, + roles: [testGuildId], + users: [testUserId], + token: 'VeryRealToken', + }); await guildCommandFromGlobal?.permissions.set({ // @ts-expect-error guild: testGuildId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); await guildCommandFromGuild?.permissions.add({ permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); await guildCommandFromGuild?.permissions.has({ permissionId: testGuildId }); await guildCommandFromGuild?.permissions.fetch({}); - await guildCommandFromGuild?.permissions.remove({ roles: [testGuildId] }); - await guildCommandFromGuild?.permissions.remove({ users: [testUserId] }); - await guildCommandFromGuild?.permissions.remove({ roles: [testGuildId], users: [testUserId] }); + await guildCommandFromGuild?.permissions.remove({ roles: [testGuildId], token: 'VeryRealToken' }); + await guildCommandFromGuild?.permissions.remove({ users: [testUserId], token: 'VeryRealToken' }); + await guildCommandFromGuild?.permissions.remove({ + roles: [testGuildId], + users: [testUserId], + token: 'VeryRealToken', + }); await guildCommandFromGuild?.permissions.set({ permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); await guildCommandFromGuild?.permissions.add({ // @ts-expect-error command: globalCommandId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); // @ts-expect-error await guildCommandFromGuild?.permissions.has({ command: guildCommandId, permissionId: testGuildId }); - // @ts-expect-error - await guildCommandFromGuild?.permissions.remove({ command: guildCommandId, roles: [testGuildId] }); - // @ts-expect-error - await guildCommandFromGuild?.permissions.remove({ command: guildCommandId, users: [testUserId] }); + await guildCommandFromGuild?.permissions.remove({ + // @ts-expect-error + command: guildCommandId, + roles: [testGuildId], + token: 'VeryRealToken', + }); + await guildCommandFromGuild?.permissions.remove({ + // @ts-expect-error + command: guildCommandId, + users: [testUserId], + token: 'VeryRealToken', + }); await guildCommandFromGuild?.permissions.remove({ // @ts-expect-error command: guildCommandId, roles: [testGuildId], users: [testUserId], + token: 'VeryRealToken', }); await guildCommandFromGuild?.permissions.set({ // @ts-expect-error command: guildCommandId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); await guildCommandFromGuild?.permissions.add({ // @ts-expect-error guild: testGuildId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], + token: 'VeryRealToken', }); // @ts-expect-error await guildCommandFromGuild?.permissions.has({ guild: testGuildId, permissionId: testGuildId }); // @ts-expect-error - await guildCommandFromGuild?.permissions.remove({ guild: testGuildId, roles: [testGuildId] }); + await guildCommandFromGuild?.permissions.remove({ guild: testGuildId, roles: [testGuildId], token: 'VeryRealToken' }); // @ts-expect-error - await guildCommandFromGuild?.permissions.remove({ guild: testGuildId, users: [testUserId] }); - // @ts-expect-error - await guildCommandFromGuild?.permissions.remove({ guild: testGuildId, roles: [testGuildId], users: [testUserId] }); + await guildCommandFromGuild?.permissions.remove({ guild: testGuildId, users: [testUserId], token: 'VeryRealToken' }); + await guildCommandFromGuild?.permissions.remove({ + // @ts-expect-error + guild: testGuildId, + roles: [testGuildId], + users: [testUserId], + token: 'VeryRealToken', + }); await guildCommandFromGuild?.permissions.set({ // @ts-expect-error guild: testGuildId, permissions: [{ type: ApplicationCommandPermissionType.Role, id: testGuildId, permission: true }], - }); - - client.application?.commands.permissions.set({ - guild: testGuildId, - fullPermissions: originalPermissions?.map((permissions, id) => ({ permissions, id })) ?? [], + token: 'VeryRealToken', }); });