From dba90bbee84e6f2aad87638acfdecaf565718ac8 Mon Sep 17 00:00:00 2001 From: ckohen Date: Tue, 1 Jun 2021 20:00:26 -0700 Subject: [PATCH 1/3] fix(ApplicationCommands): allow managing commands for uncached guilds --- src/errors/Messages.js | 3 +- src/managers/ApplicationCommandManager.js | 193 ++++++++++++++++-- .../GuildApplicationCommandManager.js | 121 ----------- src/structures/ApplicationCommand.js | 17 +- typings/index.d.ts | 67 +++++- 5 files changed, 240 insertions(+), 161 deletions(-) diff --git a/src/errors/Messages.js b/src/errors/Messages.js index bb08c34ff83f..ba6eb3b5c709 100644 --- a/src/errors/Messages.js +++ b/src/errors/Messages.js @@ -109,7 +109,8 @@ const Messages = { MEMBER_FETCH_NONCE_LENGTH: 'Nonce length must not exceed 32 characters.', GLOBAL_COMMAND_PERMISSIONS: - "Permissions for global commands may only be fetched or modified from a guild's application command manager.", + 'Permissions for global commands may only be fetched or modified by providing a guildID' + + "or from a guild's application command manager.", INTERACTION_ALREADY_REPLIED: 'This interaction has already been deferred or replied to.', }; diff --git a/src/managers/ApplicationCommandManager.js b/src/managers/ApplicationCommandManager.js index 2bfefc76446a..47b639af567b 100644 --- a/src/managers/ApplicationCommandManager.js +++ b/src/managers/ApplicationCommandManager.js @@ -1,9 +1,10 @@ 'use strict'; const BaseManager = require('./BaseManager'); -const { TypeError } = require('../errors'); +const { Error, TypeError } = require('../errors'); const ApplicationCommand = require('../structures/ApplicationCommand'); const Collection = require('../util/Collection'); +const { ApplicationCommandPermissionTypes } = require('../util/Constants'); /** * Manages API methods for application commands and stores their cache. @@ -26,14 +27,15 @@ class ApplicationCommandManager extends BaseManager { /** * The APIRouter path to the commands - * @type {Object} - * @readonly + * @param {Snowflake} [options.id] ID of the application command + * @param {Snowflake} [options.guildID] ID of the guild to use in the path, for uncached guilds + * @returns {Object} * @private */ - get commandPath() { + commandPath({ id, guildID } = {}) { let path = this.client.api.applications(this.client.application.id); - if (this.guild) path = path.guilds(this.guild.id); - return path.commands; + if (this.guild || guildID) path = path.guilds(this.guild?.id ?? guildID); + return id ? path.commands(id) : path.commands; } /** @@ -43,11 +45,23 @@ class ApplicationCommandManager extends BaseManager { * @typedef {ApplicationCommand|Snowflake} ApplicationCommandResolvable */ + /** + * Options used to fetch data from discord + * @typedef {Object} BaseFetchOptions + * @property {boolean} [cache=true] Whether to cache the fetched data if it wasn't already + * @property {boolean} [force=false] Whether to skip the cache check and request the API + */ + + /** + * Options used to fetch Application Commands from discord + * @typedef {BaseFetchOptions} FetchApplicationCommandOptions + * @property {Snowflake} [guildID] ID of the guild to fetch commands for, for when the guild is not cached + */ + /** * Obtains one or multiple application commands from Discord, or the cache if it's already available. * @param {Snowflake} [id] ID of the application command - * @param {boolean} [cache=true] Whether to cache the new application commands if they weren't already - * @param {boolean} [force=false] Whether to skip the cache check and request the API + * @param {FetchApplicationCommandOptions} [options] Additional options for this fetch * @returns {Promise>} * @example * // Fetch a single command @@ -60,23 +74,28 @@ class ApplicationCommandManager extends BaseManager { * .then(commands => console.log(`Fetched ${commands.size} commands`)) * .catch(console.error); */ - async fetch(id, cache = true, force = false) { + async fetch(id, { guildID, cache = true, force = false } = {}) { + if (typeof id === 'object') { + ({ guildID, cache = true, force = false } = id); + id = undefined; + } if (id) { if (!force) { const existing = this.cache.get(id); if (existing) return existing; } - const command = await this.commandPath(id).get(); + const command = await this.commandPath({ id, guildID }).get(); return this.add(command, cache); } - const data = await this.commandPath.get(); + const data = await this.commandPath({ guildID }).get(); return data.reduce((coll, command) => coll.set(command.id, this.add(command, cache)), new Collection()); } /** * Creates an application command. * @param {ApplicationCommandData} command The command + * @param {Snowflake} [guildID] ID of a guild to create this command in, for when the guild is not cached * @returns {Promise} * @example * // Create a new command @@ -87,8 +106,8 @@ class ApplicationCommandManager extends BaseManager { * .then(console.log) * .catch(console.error); */ - async create(command) { - const data = await this.commandPath.post({ + async create(command, guildID) { + const data = await this.commandPath({ guildID }).post({ data: this.constructor.transformCommand(command), }); return this.add(data); @@ -97,6 +116,7 @@ class ApplicationCommandManager extends BaseManager { /** * Sets all the commands for this application or guild. * @param {ApplicationCommandData[]} commands The commands + * @param {Snowflake} [guildID] ID of a guild to create the commands in, for when the guild is not cached * @returns {Promise>} * @example * // Set all commands to just this one @@ -114,8 +134,8 @@ class ApplicationCommandManager extends BaseManager { * .then(console.log) * .catch(console.error); */ - async set(commands) { - const data = await this.commandPath.put({ + async set(commands, guildID) { + const data = await this.commandPath({ guildID }).put({ data: commands.map(c => this.constructor.transformCommand(c)), }); return data.reduce((coll, command) => coll.set(command.id, this.add(command)), new Collection()); @@ -125,6 +145,7 @@ class ApplicationCommandManager extends BaseManager { * Edits an application command. * @param {ApplicationCommandResolvable} command The command to edit * @param {ApplicationCommandData} data The data to update the command with + * @param {Snowflake} [guildID] ID of the guild where the command registered, for when the guild is not cached * @returns {Promise} * @example * // Edit an existing command @@ -134,17 +155,18 @@ class ApplicationCommandManager extends BaseManager { * .then(console.log) * .catch(console.error); */ - async edit(command, data) { + async edit(command, data, guildID) { const id = this.resolveID(command); if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); - const patched = await this.commandPath(id).patch({ data: this.constructor.transformCommand(data) }); + const patched = await this.commandPath({ id, guildID }).patch({ data: this.constructor.transformCommand(data) }); return this.add(patched); } /** * Deletes an application command. * @param {ApplicationCommandResolvable} command The command to delete + * @param {Snowflake} [guildID] ID of the guild where the command is registered, for when the guild is not cached * @returns {Promise} * @example * // Delete a command @@ -152,11 +174,11 @@ class ApplicationCommandManager extends BaseManager { * .then(console.log) * .catch(console.error); */ - async delete(command) { + async delete(command, guildID) { const id = this.resolveID(command); if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); - await this.commandPath(id).delete(); + await this.commandPath({ id, guildID }).delete(); const cached = this.cache.get(id); this.cache.delete(id); @@ -177,6 +199,139 @@ class ApplicationCommandManager extends BaseManager { default_permission: command.defaultPermission, }; } + + /** + * Fetches the permissions for one or multiple commands. + * When calling this on ApplicationCommandManager, guildID is required. + * To fetch all permissions for an uncached guild use `fetchPermissions(undefined, '123456789012345678')` + * @param {ApplicationCommandResolvable} [command] The command to get the permissions from + * @param {Snowflake} [guildID] ID of the guild to get the permissions for, + * for global commands and when the guild is not cached + * @returns {Promise>} + * @example + * // Fetch permissions for one command + * guild.commands.fetchPermissions('123456789012345678') + * .then(perms => console.log(`Fetched permissions for ${perms.length} users`)) + * .catch(console.error); + * @example + * // Fetch permissions for all commands + * client.application.commands.fetchPermissions() + * .then(perms => console.log(`Fetched permissions for ${perms.size} commands`)) + * .catch(console.error); + */ + async fetchPermissions(command, guildID) { + if (!this.guild && !guildID) throw new Error('GLOBAL_COMMAND_PERMISSIONS'); + if (command) { + const id = this.resolveID(command); + if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); + + const data = await this.commandPath({ id, guildID }).permissions.get(); + return data.permissions.map(perm => this.constructor.transformPermissions(perm, true)); + } + + const data = await this.commandPath({ guildID }).permissions.get(); + return data.reduce( + (coll, perm) => + coll.set( + perm.id, + perm.permissions.map(p => this.constructor.transformPermissions(p, true)), + ), + new Collection(), + ); + } + + /** + * Data used for overwriting the permissions for all application commands in a guild. + * @typedef {Object} GuildApplicationCommandPermissionData + * @prop {Snowflake} command The ID of the command + * @prop {ApplicationCommandPermissionData[]} permissions The permissions for this command + */ + + /** + * Sets the permissions for a command. + * When calling this on ApplicationCommandManager, guildID is required. + * To set multiple permissions for an uncached guild use `setPermissions(permissions, '123456789012345678')` + * @param {ApplicationCommandResolvable|GuildApplicationCommandPermissionData[]} command The command to edit the + * permissions for, or an array of guild application command permissions to set the permissions of all commands + * @param {ApplicationCommandPermissionData[]} [permissions] The new permissions for the command + * @param {Snowflake} [guildID] ID of the guild to get the permissions for, + * for global commands and when the guild is not cached + * @returns {Promise>} + * @example + * // Set the permissions for one command + * client.application.commands.setPermissions('123456789012345678', [ + * { + * id: '876543210987654321', + * type: 'USER', + * permission: false, + * }, + * ]) + * .then(console.log) + * .catch(console.error); + * @example + * // Set the permissions for all commands + * guild.commands.setPermissions([ + * { + * id: '123456789012345678', + * permissions: [{ + * id: '876543210987654321', + * type: 'USER', + * permission: false, + * }], + * }, + * ]) + * .then(console.log) + * .catch(console.error); + */ + async setPermissions(command, permissions, guildID) { + if (!this.guild && !guildID) throw new Error('GLOBAL_COMMAND_PERMISSIONS'); + const id = this.resolveID(command); + + if (id) { + const data = await this.commandPath({ id, guildID }).permissions.put({ + data: { permissions: permissions.map(perm => this.constructor.transformPermissions(perm)) }, + }); + return data.permissions.map(perm => this.constructor.transformPermissions(perm, true)); + } + + if (typeof permissions === 'string') { + guildID = permissions; + permissions = undefined; + } + + const data = await this.commandPath({ guildID }).permissions.put({ + data: command.map(perm => ({ + id: perm.id, + permissions: perm.permissions.map(p => this.constructor.transformPermissions(p)), + })), + }); + return data.reduce( + (coll, perm) => + coll.set( + perm.id, + perm.permissions.map(p => this.constructor.transformPermissions(p, true)), + ), + new Collection(), + ); + } + + /** + * Transforms an {@link ApplicationCommandPermissionData} object into something that can be used with the API. + * @param {ApplicationCommandPermissionData} permissions The permissions to transform + * @param {boolean} [received] Whether these permissions have been received from Discord + * @returns {Object} + * @private + */ + static transformPermissions(permissions, received) { + return { + id: permissions.id, + permission: permissions.permission, + type: + typeof permissions.type === 'number' && !received + ? permissions.type + : ApplicationCommandPermissionTypes[permissions.type], + }; + } } module.exports = ApplicationCommandManager; diff --git a/src/managers/GuildApplicationCommandManager.js b/src/managers/GuildApplicationCommandManager.js index 910cbfd7680d..3a78fbf7f864 100644 --- a/src/managers/GuildApplicationCommandManager.js +++ b/src/managers/GuildApplicationCommandManager.js @@ -1,9 +1,6 @@ 'use strict'; const ApplicationCommandManager = require('./ApplicationCommandManager'); -const { TypeError } = require('../errors'); -const Collection = require('../util/Collection'); -const { ApplicationCommandPermissionTypes } = require('../util/Constants'); /** * An extension for guild-specific application commands. @@ -19,124 +16,6 @@ class GuildApplicationCommandManager extends ApplicationCommandManager { */ this.guild = guild; } - - /** - * Fetches the permissions for one or multiple commands. - * @param {ApplicationCommandResolvable} [command] The command to get the permissions from - * @returns {Promise>} - * @example - * // Fetch permissions for one command - * guild.commands.fetchPermissions('123456789012345678') - * .then(perms => console.log(`Fetched permissions for ${perms.length} users`)) - * .catch(console.error); - * @example - * // Fetch permissions for all commands - * client.application.commands.fetchPermissions() - * .then(perms => console.log(`Fetched permissions for ${perms.size} commands`)) - * .catch(console.error); - */ - async fetchPermissions(command) { - if (command) { - const id = this.resolveID(command); - if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); - - const data = await this.commandPath(id).permissions.get(); - return data.permissions.map(perm => this.constructor.transformPermissions(perm, true)); - } - - const data = await this.commandPath.permissions.get(); - return data.reduce( - (coll, perm) => - coll.set( - perm.id, - perm.permissions.map(p => this.constructor.transformPermissions(p, true)), - ), - new Collection(), - ); - } - - /** - * Data used for overwriting the permissions for all application commands in a guild. - * @typedef {Object} GuildApplicationCommandPermissionData - * @prop {Snowflake} command The ID of the command - * @prop {ApplicationCommandPermissionData[]} permissions The permissions for this command - */ - - /** - * Sets the permissions for a command. - * @param {ApplicationCommandResolvable|GuildApplicationCommandPermissionData[]} command The command to edit the - * permissions for, or an array of guild application command permissions to set the permissions of all commands - * @param {ApplicationCommandPermissionData[]} permissions The new permissions for the command - * @returns {Promise>} - * @example - * // Set the permissions for one command - * client.application.commands.setPermissions('123456789012345678', [ - * { - * id: '876543210987654321', - * type: 'USER', - * permission: false, - * }, - * ]) - * .then(console.log) - * .catch(console.error); - * @example - * // Set the permissions for all commands - * guild.commands.setPermissions([ - * { - * id: '123456789012345678', - * permissions: [{ - * id: '876543210987654321', - * type: 'USER', - * permission: false, - * }], - * }, - * ]) - * .then(console.log) - * .catch(console.error); - */ - async setPermissions(command, permissions) { - const id = this.resolveID(command); - - if (id) { - const data = await this.commandPath(id).permissions.put({ - data: { permissions: permissions.map(perm => this.constructor.transformPermissions(perm)) }, - }); - return data.permissions.map(perm => this.constructor.transformPermissions(perm, true)); - } - - const data = await this.commandPath.permissions.put({ - data: command.map(perm => ({ - id: perm.id, - permissions: perm.permissions.map(p => this.constructor.transformPermissions(p)), - })), - }); - return data.reduce( - (coll, perm) => - coll.set( - perm.id, - perm.permissions.map(p => this.constructor.transformPermissions(p, true)), - ), - new Collection(), - ); - } - - /** - * Transforms an {@link ApplicationCommandPermissionData} object into something that can be used with the API. - * @param {ApplicationCommandPermissionData} permissions The permissions to transform - * @param {boolean} [received] Whether these permissions have been received from Discord - * @returns {Object} - * @private - */ - static transformPermissions(permissions, received) { - return { - id: permissions.id, - permission: permissions.permission, - type: - typeof permissions.type === 'number' && !received - ? permissions.type - : ApplicationCommandPermissionTypes[permissions.type], - }; - } } module.exports = GuildApplicationCommandManager; diff --git a/src/structures/ApplicationCommand.js b/src/structures/ApplicationCommand.js index 652de0043937..a4aebced4615 100644 --- a/src/structures/ApplicationCommand.js +++ b/src/structures/ApplicationCommand.js @@ -1,7 +1,6 @@ 'use strict'; const Base = require('./Base'); -const { Error } = require('../errors'); const { ApplicationCommandOptionTypes } = require('../util/Constants'); const SnowflakeUtil = require('../util/SnowflakeUtil'); @@ -148,7 +147,8 @@ class ApplicationCommand extends Base { /** * Fetches the permissions for this command. - * This is only available for guild application commands. + * You must specify guildID on global commands and commands on uncached guilds, otherwise it is ignored. + * @param {Snowflake} [guildID] ID for the guild to fetch permissions for if this is a global command * @returns {Promise} * @example * // Fetch permissions for this command @@ -156,15 +156,15 @@ class ApplicationCommand extends Base { * .then(perms => console.log(`Fetched permissions for ${perms.length} users`)) * .catch(console.error); */ - fetchPermissions() { - if (!this.guild) throw new Error('GLOBAL_COMMAND_PERMISSIONS'); - return this.manager.fetchPermissions(this); + fetchPermissions(guildID) { + return this.manager.fetchPermissions(this, guildID); } /** * Sets the permissions for this command. - * This is only available for guild application commands. + * You must specify guildID on global commands and commands on uncached guilds, otherwise it is ignored. * @param {ApplicationCommandPermissionData[]} permissions The new permissions for the command + * @param {Snowflake} [guildID] ID for the guild to fetch permissions for if this is a global command * @returns {Promise} * @example * // Set the permissions for this command @@ -178,9 +178,8 @@ class ApplicationCommand extends Base { * .then(console.log) * .catch(console.error); */ - setPermissions(permissions) { - if (!this.guild) throw new Error('GLOBAL_COMMAND_PERMISSIONS'); - return this.manager.setPermissions(this, permissions); + setPermissions(permissions, guildID) { + return this.manager.setPermissions(this, permissions, guildID); } /** diff --git a/typings/index.d.ts b/typings/index.d.ts index 4df55c233011..5d9bc55dc1ca 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -184,8 +184,11 @@ declare module 'discord.js' { public options: ApplicationCommandOption[]; public delete(): Promise; public edit(data: ApplicationCommandData): Promise; - public fetchPermissions(): Promise; - public setPermissions(permissions: ApplicationCommandPermissionData[]): Promise; + public fetchPermissions(guildID?: Snowflake): Promise; + public setPermissions( + permissions: ApplicationCommandPermissionData[], + guildID?: Snowflake, + ): Promise; private static transformOption(option: ApplicationCommandOptionData, received?: boolean): unknown; } @@ -2079,14 +2082,42 @@ declare module 'discord.js' { ApplicationCommandResolvable > { constructor(client: Client, iterable?: Iterable); - private readonly commandPath: unknown; - public create(command: ApplicationCommandData): Promise; - public delete(command: ApplicationCommandResolvable): Promise; - public edit(command: ApplicationCommandResolvable, data: ApplicationCommandData): Promise; - public fetch(id: Snowflake, cache?: boolean, force?: boolean): Promise; - public fetch(id?: Snowflake, cache?: boolean, force?: boolean): Promise>; - public set(commands: ApplicationCommandData[]): Promise>; + private commandPath({ id, guildID }: { id?: Snowflake; guildID?: Snowflake }): unknown; + public create(command: ApplicationCommandData, guildID?: Snowflake): Promise; + public delete(command: ApplicationCommandResolvable, guildID?: Snowflake): Promise; + public edit( + command: ApplicationCommandResolvable, + data: ApplicationCommandData, + guildID?: Snowflake, + ): Promise; + public fetch(id: Snowflake, options?: FetchApplicationCommandOptions): Promise; + public fetch( + id?: Snowflake, + options?: FetchApplicationCommandOptions, + ): Promise>; + public fetchPermissions( + command: undefined, + guildID: Snowflake, + ): Promise>; + public fetchPermissions( + command: ApplicationCommandResolvable, + guildID: Snowflake, + ): Promise; + public set( + commands: ApplicationCommandData[], + guildID?: Snowflake, + ): Promise>; + public setPermissions( + command: ApplicationCommandResolvable, + permissions: ApplicationCommandPermissionData[], + guildID: Snowflake, + ): Promise; + public setPermissions( + permissions: GuildApplicationCommandPermissionData[], + guildID: Snowflake, + ): Promise>; private static transformCommand(command: ApplicationCommandData): unknown; + private static transformPermissions(permissions: ApplicationCommandPermissionData, received?: boolean): unknown; } export class BaseGuildEmojiManager extends BaseManager { @@ -2102,8 +2133,14 @@ declare module 'discord.js' { export class GuildApplicationCommandManager extends ApplicationCommandManager { constructor(guild: Guild, iterable?: Iterable); public guild: Guild; - public fetchPermissions(): Promise>; + public create(command: ApplicationCommandData): Promise; + public delete(command: ApplicationCommandResolvable): Promise; + public edit(command: ApplicationCommandResolvable, data: ApplicationCommandData): Promise; + public fetch(id: Snowflake, options?: BaseFetchOptions): Promise; + public fetch(id?: Snowflake, options?: BaseFetchOptions): Promise>; + public fetchPermissions(command: undefined): Promise>; public fetchPermissions(command: ApplicationCommandResolvable): Promise; + public set(commands: ApplicationCommandData[]): Promise>; public setPermissions( command: ApplicationCommandResolvable, permissions: ApplicationCommandPermissionData[], @@ -2111,7 +2148,6 @@ declare module 'discord.js' { public setPermissions( permissions: GuildApplicationCommandPermissionData[], ): Promise>; - private static transformPermissions(permissions: ApplicationCommandPermissionData, received?: boolean): unknown; } export class GuildChannelManager extends BaseManager { @@ -2524,6 +2560,11 @@ declare module 'discord.js' { type Base64String = string; + interface BaseFetchOptions { + cache?: boolean; + force?: boolean; + } + interface BaseMessageComponentOptions { type?: MessageComponentType | MessageComponentTypes; } @@ -2810,6 +2851,10 @@ declare module 'discord.js' { CommandInteraction: typeof CommandInteraction; } + interface FetchApplicationCommandOptions extends BaseFetchOptions { + guildID?: Snowflake; + } + interface FetchBanOptions { user: UserResolvable; cache?: boolean; From 97d1070cbe3d01274a56fb62a0b33d64584d6f77 Mon Sep 17 00:00:00 2001 From: ckohen Date: Wed, 2 Jun 2021 12:14:57 -0700 Subject: [PATCH 2/3] docs: better docstrings Co-authored-by: SpaceEEC --- src/managers/ApplicationCommandManager.js | 19 ++++++++++++------- src/structures/ApplicationCommand.js | 6 ++++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/managers/ApplicationCommandManager.js b/src/managers/ApplicationCommandManager.js index 47b639af567b..93695d2e4d0d 100644 --- a/src/managers/ApplicationCommandManager.js +++ b/src/managers/ApplicationCommandManager.js @@ -28,7 +28,8 @@ class ApplicationCommandManager extends BaseManager { /** * The APIRouter path to the commands * @param {Snowflake} [options.id] ID of the application command - * @param {Snowflake} [options.guildID] ID of the guild to use in the path, for uncached guilds + * @param {Snowflake} [options.guildID] ID of the guild to use in the path, + * ignored when using a {@link GuildApplicationCommandManager} * @returns {Object} * @private */ @@ -95,7 +96,8 @@ class ApplicationCommandManager extends BaseManager { /** * Creates an application command. * @param {ApplicationCommandData} command The command - * @param {Snowflake} [guildID] ID of a guild to create this command in, for when the guild is not cached + * @param {Snowflake} [guildID] ID of the guild to create this command in, + * ignored when using a {@link GuildApplicationCommandManager} * @returns {Promise} * @example * // Create a new command @@ -116,7 +118,8 @@ class ApplicationCommandManager extends BaseManager { /** * Sets all the commands for this application or guild. * @param {ApplicationCommandData[]} commands The commands - * @param {Snowflake} [guildID] ID of a guild to create the commands in, for when the guild is not cached + * @param {Snowflake} [guildID] ID of the guild to create the commands in, + * ignored when using a {@link GuildApplicationCommandManager} * @returns {Promise>} * @example * // Set all commands to just this one @@ -145,7 +148,8 @@ class ApplicationCommandManager extends BaseManager { * Edits an application command. * @param {ApplicationCommandResolvable} command The command to edit * @param {ApplicationCommandData} data The data to update the command with - * @param {Snowflake} [guildID] ID of the guild where the command registered, for when the guild is not cached + * @param {Snowflake} [guildID] ID of the guild where the command registered, + * ignored when using a {@link GuildApplicationCommandManager} * @returns {Promise} * @example * // Edit an existing command @@ -166,7 +170,8 @@ class ApplicationCommandManager extends BaseManager { /** * Deletes an application command. * @param {ApplicationCommandResolvable} command The command to delete - * @param {Snowflake} [guildID] ID of the guild where the command is registered, for when the guild is not cached + * @param {Snowflake} [guildID] ID of the guild where the command is registered, + * ignored when using a {@link GuildApplicationCommandManager} * @returns {Promise} * @example * // Delete a command @@ -206,7 +211,7 @@ class ApplicationCommandManager extends BaseManager { * To fetch all permissions for an uncached guild use `fetchPermissions(undefined, '123456789012345678')` * @param {ApplicationCommandResolvable} [command] The command to get the permissions from * @param {Snowflake} [guildID] ID of the guild to get the permissions for, - * for global commands and when the guild is not cached + * ignored when using a {@link GuildApplicationCommandManager} * @returns {Promise>} * @example * // Fetch permissions for one command @@ -255,7 +260,7 @@ class ApplicationCommandManager extends BaseManager { * permissions for, or an array of guild application command permissions to set the permissions of all commands * @param {ApplicationCommandPermissionData[]} [permissions] The new permissions for the command * @param {Snowflake} [guildID] ID of the guild to get the permissions for, - * for global commands and when the guild is not cached + * ignored when using a {@link GuildApplicationCommandManager} * @returns {Promise>} * @example * // Set the permissions for one command diff --git a/src/structures/ApplicationCommand.js b/src/structures/ApplicationCommand.js index a4aebced4615..88159d127c88 100644 --- a/src/structures/ApplicationCommand.js +++ b/src/structures/ApplicationCommand.js @@ -147,7 +147,8 @@ class ApplicationCommand extends Base { /** * Fetches the permissions for this command. - * You must specify guildID on global commands and commands on uncached guilds, otherwise it is ignored. + * You must specify guildID if this command is handled by a {@link ApplicationCommandManager}, + * including commands fetched for arbitrary guilds from it, otherwise it is ignored. * @param {Snowflake} [guildID] ID for the guild to fetch permissions for if this is a global command * @returns {Promise} * @example @@ -162,7 +163,8 @@ class ApplicationCommand extends Base { /** * Sets the permissions for this command. - * You must specify guildID on global commands and commands on uncached guilds, otherwise it is ignored. + * You must specify guildID if this command is handled by a {@link ApplicationCommandManager}, + * including commands fetched for arbitrary guilds from it, otherwise it is ignored. * @param {ApplicationCommandPermissionData[]} permissions The new permissions for the command * @param {Snowflake} [guildID] ID for the guild to fetch permissions for if this is a global command * @returns {Promise} From c1cdc9e9bb7db58d0484b14570ca361775df7220 Mon Sep 17 00:00:00 2001 From: ckohen Date: Thu, 10 Jun 2021 21:09:03 -0700 Subject: [PATCH 3/3] fix: don't throw too early docs: fixes typedef for GuildApplicationCommandPermissionData --- src/managers/ApplicationCommandManager.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/managers/ApplicationCommandManager.js b/src/managers/ApplicationCommandManager.js index 93695d2e4d0d..6a214d817dab 100644 --- a/src/managers/ApplicationCommandManager.js +++ b/src/managers/ApplicationCommandManager.js @@ -248,7 +248,7 @@ class ApplicationCommandManager extends BaseManager { /** * Data used for overwriting the permissions for all application commands in a guild. * @typedef {Object} GuildApplicationCommandPermissionData - * @prop {Snowflake} command The ID of the command + * @prop {Snowflake} id The ID of the command * @prop {ApplicationCommandPermissionData[]} permissions The permissions for this command */ @@ -289,10 +289,10 @@ class ApplicationCommandManager extends BaseManager { * .catch(console.error); */ async setPermissions(command, permissions, guildID) { - if (!this.guild && !guildID) throw new Error('GLOBAL_COMMAND_PERMISSIONS'); const id = this.resolveID(command); if (id) { + if (!this.guild && !guildID) throw new Error('GLOBAL_COMMAND_PERMISSIONS'); const data = await this.commandPath({ id, guildID }).permissions.put({ data: { permissions: permissions.map(perm => this.constructor.transformPermissions(perm)) }, }); @@ -304,6 +304,8 @@ class ApplicationCommandManager extends BaseManager { permissions = undefined; } + if (!this.guild && !guildID) throw new Error('GLOBAL_COMMAND_PERMISSIONS'); + const data = await this.commandPath({ guildID }).permissions.put({ data: command.map(perm => ({ id: perm.id,