From 0a2b4c1f626fb7e171229da3ee9820c03688f23b Mon Sep 17 00:00:00 2001 From: Vaporox Date: Tue, 11 May 2021 19:57:18 +0200 Subject: [PATCH 1/5] fix(ApplicationCommandManager): limit permission methods to guilds --- src/errors/Messages.js | 2 + src/managers/ApplicationCommandManager.js | 119 ----------------- .../GuildApplicationCommandManager.js | 120 ++++++++++++++++++ src/structures/ApplicationCommand.js | 5 +- typings/index.d.ts | 20 +-- 5 files changed, 136 insertions(+), 130 deletions(-) diff --git a/src/errors/Messages.js b/src/errors/Messages.js index 49df0178c0f5..e5813f87b575 100644 --- a/src/errors/Messages.js +++ b/src/errors/Messages.js @@ -112,6 +112,8 @@ const Messages = { MEMBER_FETCH_NONCE_LENGTH: 'Nonce length must not exceed 32 characters.', + GLOBAL_COMMAND: 'This method cannot be used on global application commands.', + 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 2860771d676c..2bfefc76446a 100644 --- a/src/managers/ApplicationCommandManager.js +++ b/src/managers/ApplicationCommandManager.js @@ -4,7 +4,6 @@ const BaseManager = require('./BaseManager'); const { 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. @@ -164,106 +163,6 @@ class ApplicationCommandManager extends BaseManager { return cached ?? null; } - /** - * 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 ApplicationCommandData} object into something that can be used with the API. * @param {ApplicationCommandData} command The command to transform @@ -278,24 +177,6 @@ class ApplicationCommandManager extends BaseManager { default_permission: command.defaultPermission, }; } - - /** - * 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 3a78fbf7f864..79316a14ed45 100644 --- a/src/managers/GuildApplicationCommandManager.js +++ b/src/managers/GuildApplicationCommandManager.js @@ -1,6 +1,8 @@ 'use strict'; const ApplicationCommandManager = require('./ApplicationCommandManager'); +const Collection = require('../util/Collection'); +const { ApplicationCommandPermissionTypes } = require('../util/Constants'); /** * An extension for guild-specific application commands. @@ -16,6 +18,124 @@ 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 2291dc3dee92..95dc40df6361 100644 --- a/src/structures/ApplicationCommand.js +++ b/src/structures/ApplicationCommand.js @@ -1,6 +1,7 @@ 'use strict'; const Base = require('./Base'); +const { Error } = require('../errors'); const { ApplicationCommandOptionTypes } = require('../util/Constants'); const SnowflakeUtil = require('../util/SnowflakeUtil'); @@ -130,7 +131,7 @@ class ApplicationCommand extends Base { } /** - * The object returned when fetching permissions for an application command. + * Data for setting the permissions of an application command. * @typedef {object} ApplicationCommandPermissionData * @property {Snowflake} id The ID of the role or user * @property {ApplicationCommandPermissionType|number} type Whether this permission if for a role or a user @@ -155,6 +156,7 @@ class ApplicationCommand extends Base { * .catch(console.error); */ fetchPermissions() { + if (!this.guild) throw new Error('GLOBAL_COMMAND'); return this.manager.fetchPermissions(this); } @@ -175,6 +177,7 @@ class ApplicationCommand extends Base { * .catch(console.error); */ setPermissions(permissions) { + if (!this.guild) throw new Error('GLOBAL_COMMAND'); return this.manager.setPermissions(this, permissions); } diff --git a/typings/index.d.ts b/typings/index.d.ts index c0ad9fc31dba..045a72601918 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -2065,18 +2065,8 @@ declare module 'discord.js' { 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 fetchPermissions(): Promise>; - public fetchPermissions(command: ApplicationCommandResolvable): Promise; public set(commands: ApplicationCommandData[]): Promise>; - public setPermissions( - command: ApplicationCommandResolvable, - permissions: ApplicationCommandPermissionData[], - ): Promise; - public setPermissions( - permissions: GuildApplicationCommandPermissionData[], - ): Promise>; private static transformCommand(command: ApplicationCommandData): object; - private static transformPermissions(permissions: ApplicationCommandPermissionData, received?: boolean): object; } export class BaseGuildEmojiManager extends BaseManager { @@ -2092,6 +2082,16 @@ declare module 'discord.js' { export class GuildApplicationCommandManager extends ApplicationCommandManager { constructor(guild: Guild, iterable?: Iterable); public guild: Guild; + public fetchPermissions(): Promise>; + public fetchPermissions(command: ApplicationCommandResolvable): Promise; + public setPermissions( + command: ApplicationCommandResolvable, + permissions: ApplicationCommandPermissionData[], + ): Promise; + public setPermissions( + permissions: GuildApplicationCommandPermissionData[], + ): Promise>; + private static transformPermissions(permissions: ApplicationCommandPermissionData, received?: boolean): object; } export class GuildChannelManager extends BaseManager { From 355ca686d5785a03428589bf52b7843e499c6f33 Mon Sep 17 00:00:00 2001 From: Vaporox Date: Tue, 11 May 2021 20:06:39 +0200 Subject: [PATCH 2/5] docs: add warn tags --- src/structures/ApplicationCommand.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/structures/ApplicationCommand.js b/src/structures/ApplicationCommand.js index 95dc40df6361..2f34744193d1 100644 --- a/src/structures/ApplicationCommand.js +++ b/src/structures/ApplicationCommand.js @@ -148,6 +148,7 @@ class ApplicationCommand extends Base { /** * Fetches the permissions for this command. + * This is only available for guild application commands. * @returns {Promise} * @example * // Fetch permissions for this command @@ -162,6 +163,7 @@ class ApplicationCommand extends Base { /** * Sets the permissions for this command. + * This is only available for guild application commands. * @param {ApplicationCommandPermissionData[]} permissions The new permissions for the command * @returns {Promise} * @example From 5997d0ef089661267e8bc90ffc63d99feffc1541 Mon Sep 17 00:00:00 2001 From: Jan <66554238+vaporox@users.noreply.github.com> Date: Tue, 11 May 2021 23:01:24 +0200 Subject: [PATCH 3/5] feat: improve error message --- src/errors/Messages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/errors/Messages.js b/src/errors/Messages.js index e5813f87b575..06e74b489953 100644 --- a/src/errors/Messages.js +++ b/src/errors/Messages.js @@ -112,7 +112,7 @@ const Messages = { MEMBER_FETCH_NONCE_LENGTH: 'Nonce length must not exceed 32 characters.', - GLOBAL_COMMAND: 'This method cannot be used on global application commands.', + GLOBAL_COMMAND: "Permissions for global commands may only be fetched or modified from a guild's application command manager.", INTERACTION_ALREADY_REPLIED: 'This interaction has already been deferred or replied to.', }; From 60ee1d5bc64acee4c90ff3b6fdbdf5a45f2e5280 Mon Sep 17 00:00:00 2001 From: Jan <66554238+vaporox@users.noreply.github.com> Date: Tue, 11 May 2021 23:44:05 +0200 Subject: [PATCH 4/5] style: eslint --- src/errors/Messages.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/errors/Messages.js b/src/errors/Messages.js index 06e74b489953..5eeee6c3bbbc 100644 --- a/src/errors/Messages.js +++ b/src/errors/Messages.js @@ -112,7 +112,8 @@ const Messages = { MEMBER_FETCH_NONCE_LENGTH: 'Nonce length must not exceed 32 characters.', - GLOBAL_COMMAND: "Permissions for global commands may only be fetched or modified from a guild's application command manager.", + GLOBAL_COMMAND: + "Permissions for global commands may only be fetched or modified from a guild's application command manager.", INTERACTION_ALREADY_REPLIED: 'This interaction has already been deferred or replied to.', }; From f8857f18c0610982f05e641811fef535d0a30d31 Mon Sep 17 00:00:00 2001 From: Vaporox Date: Wed, 12 May 2021 00:04:42 +0200 Subject: [PATCH 5/5] chore: make error name more verbose --- src/errors/Messages.js | 2 +- src/structures/ApplicationCommand.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/errors/Messages.js b/src/errors/Messages.js index 5eeee6c3bbbc..583b9add13be 100644 --- a/src/errors/Messages.js +++ b/src/errors/Messages.js @@ -112,7 +112,7 @@ const Messages = { MEMBER_FETCH_NONCE_LENGTH: 'Nonce length must not exceed 32 characters.', - GLOBAL_COMMAND: + GLOBAL_COMMAND_PERMISSIONS: "Permissions for global commands may only be fetched or modified from a guild's application command manager.", INTERACTION_ALREADY_REPLIED: 'This interaction has already been deferred or replied to.', diff --git a/src/structures/ApplicationCommand.js b/src/structures/ApplicationCommand.js index 2f34744193d1..738f7549511f 100644 --- a/src/structures/ApplicationCommand.js +++ b/src/structures/ApplicationCommand.js @@ -157,7 +157,7 @@ class ApplicationCommand extends Base { * .catch(console.error); */ fetchPermissions() { - if (!this.guild) throw new Error('GLOBAL_COMMAND'); + if (!this.guild) throw new Error('GLOBAL_COMMAND_PERMISSIONS'); return this.manager.fetchPermissions(this); } @@ -179,7 +179,7 @@ class ApplicationCommand extends Base { * .catch(console.error); */ setPermissions(permissions) { - if (!this.guild) throw new Error('GLOBAL_COMMAND'); + if (!this.guild) throw new Error('GLOBAL_COMMAND_PERMISSIONS'); return this.manager.setPermissions(this, permissions); }