Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Managers): new ApplicationCommandPermissionsManager #5897

Merged
merged 7 commits into from Jun 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/errors/Messages.js
Expand Up @@ -119,8 +119,9 @@ 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 by providing a guildID' +
'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',

INTERACTION_ALREADY_REPLIED: 'This interaction has already been deferred or replied to.',
INTERACTION_NOT_REPLIED: 'This interaction has not been deferred or replied to.',
Expand Down
165 changes: 17 additions & 148 deletions src/managers/ApplicationCommandManager.js
@@ -1,10 +1,10 @@
'use strict';

const ApplicationCommandPermissionsManager = require('./ApplicationCommandPermissionsManager');
const BaseManager = require('./BaseManager');
const { Error, TypeError } = require('../errors');
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.
Expand All @@ -13,6 +13,12 @@ const { ApplicationCommandPermissionTypes } = require('../util/Constants');
class ApplicationCommandManager extends BaseManager {
constructor(client, iterable) {
super(client, iterable, ApplicationCommand);

/**
* The manager for permissions of arbitrary commands on arbitrary guilds
* @type {ApplicationCommandPermissionsManager}
*/
this.permissions = new ApplicationCommandPermissionsManager(this);
}

/**
Expand All @@ -21,8 +27,8 @@ class ApplicationCommandManager extends BaseManager {
* @name ApplicationCommandManager#cache
*/

add(data, cache) {
return super.add(data, cache, { extras: [this.guild] });
add(data, cache, guildID) {
return super.add(data, cache, { extras: [this.guild, guildID] });
}

/**
Expand Down Expand Up @@ -90,7 +96,7 @@ class ApplicationCommandManager extends BaseManager {
}

const data = await this.commandPath({ guildID }).get();
return data.reduce((coll, command) => coll.set(command.id, this.add(command, cache)), new Collection());
return data.reduce((coll, command) => coll.set(command.id, this.add(command, cache, guildID)), new Collection());
}

/**
Expand All @@ -112,7 +118,7 @@ class ApplicationCommandManager extends BaseManager {
const data = await this.commandPath({ guildID }).post({
data: this.constructor.transformCommand(command),
});
return this.add(data);
return this.add(data, undefined, guildID);
}

/**
Expand Down Expand Up @@ -141,7 +147,10 @@ class ApplicationCommandManager extends BaseManager {
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());
return data.reduce(
(coll, command) => coll.set(command.id, this.add(command, undefined, guildID)),
new Collection(),
);
}

/**
Expand All @@ -164,7 +173,7 @@ class ApplicationCommandManager extends BaseManager {
if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');

const patched = await this.commandPath({ id, guildID }).patch({ data: this.constructor.transformCommand(data) });
return this.add(patched);
return this.add(patched, undefined, guildID);
}

/**
Expand Down Expand Up @@ -204,146 +213,6 @@ class ApplicationCommandManager extends BaseManager {
default_permission: command.defaultPermission,
};
}

/**
* Fetches the permissions for one or multiple commands.
* <warn>When calling this on ApplicationCommandManager, guildID is required.
* To fetch all permissions for an uncached guild use `fetchPermissions(undefined, '123456789012345678')`</warn>
* @param {ApplicationCommandResolvable} [command] The command to get the permissions from
* @param {Snowflake} [guildID] ID of the guild to get the permissions for,
* ignored when using a {@link GuildApplicationCommandManager}
* @returns {Promise<ApplicationCommandPermissions[]|Collection<Snowflake, ApplicationCommandPermissions[]>>}
* @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} id The ID of the command
* @prop {ApplicationCommandPermissionData[]} permissions The permissions for this command
*/

/**
* Sets the permissions for a command.
* <warn>When calling this on ApplicationCommandManager, guildID is required.
* To set multiple permissions for an uncached guild use `setPermissions(permissions, '123456789012345678')`</warn>
* @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,
* ignored when using a {@link GuildApplicationCommandManager}
* @returns {Promise<ApplicationCommandPermissions[]|Collection<Snowflake, ApplicationCommandPermissions[]>>}
* @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) {
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)) },
});
return data.permissions.map(perm => this.constructor.transformPermissions(perm, true));
}

if (typeof permissions === 'string') {
guildID = permissions;
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,
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 {APIApplicationCommandPermissions}
* @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;

/**
* @external APIApplicationCommandPermissions
* @see {@link https://discord.com/developers/docs/interactions/slash-commands#applicationcommandpermissions}
*/