'use strict'; const ApplicationCommandPermissionsManager = require('./ApplicationCommandPermissionsManager'); const CachedManager = require('./CachedManager'); const { TypeError } = require('../errors'); const ApplicationCommand = require('../structures/ApplicationCommand'); const Collection = require('../util/Collection'); /** * Manages API methods for application commands and stores their cache. * @extends {CachedManager} */ class ApplicationCommandManager extends CachedManager { constructor(client, iterable) { super(client, ApplicationCommand, iterable); /** * The manager for permissions of arbitrary commands on arbitrary guilds * @type {ApplicationCommandPermissionsManager} */ this.permissions = new ApplicationCommandPermissionsManager(this); } /** * The cache of this manager * @type {Collection} * @name ApplicationCommandManager#cache */ add(data, cache, guildID) { return super.add(data, cache, { extras: [this.guild, guildID] }); } /** * 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, * ignored when using a {@link GuildApplicationCommandManager} * @returns {Object} * @private */ commandPath({ id, guildID } = {}) { let path = this.client.api.applications(this.client.application.id); if (this.guild ?? guildID) path = path.guilds(this.guild?.id ?? guildID); return id ? path.commands(id) : path.commands; } /** * Data that resolves to give an ApplicationCommand object. This can be: * * An ApplicationCommand object * * A Snowflake * @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 {FetchApplicationCommandOptions} [options] Additional options for this fetch * @returns {Promise>} * @example * // Fetch a single command * client.application.commands.fetch('123456789012345678') * .then(command => console.log(`Fetched command ${command.name}`)) * .catch(console.error); * @example * // Fetch all commands * guild.commands.fetch() * .then(commands => console.log(`Fetched ${commands.size} commands`)) * .catch(console.error); */ async fetch(id, { guildID, cache = true, force = false } = {}) { if (typeof id === 'object') { ({ guildID, cache = true, force = false } = id); } else if (id) { if (!force) { const existing = this.cache.get(id); if (existing) return existing; } const command = await this.commandPath({ id, guildID }).get(); return this.add(command, cache); } const data = await this.commandPath({ guildID }).get(); return data.reduce((coll, command) => coll.set(command.id, this.add(command, cache, guildID)), new Collection()); } /** * Creates an application command. * @param {ApplicationCommandData} command The command * @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 * client.application.commands.create({ * name: 'test', * description: 'A test command', * }) * .then(console.log) * .catch(console.error); */ async create(command, guildID) { const data = await this.commandPath({ guildID }).post({ data: this.constructor.transformCommand(command), }); return this.add(data, undefined, guildID); } /** * Sets all the commands for this application or guild. * @param {ApplicationCommandData[]} commands The commands * @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 * client.application.commands.set([ * { * name: 'test', * description: 'A test command', * }, * ]) * .then(console.log) * .catch(console.error); * @example * // Remove all commands * guild.commands.set([]) * .then(console.log) * .catch(console.error); */ 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, undefined, guildID)), new Collection(), ); } /** * 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, * ignored when using a {@link GuildApplicationCommandManager} * @returns {Promise} * @example * // Edit an existing command * client.application.commands.edit('123456789012345678', { * description: 'New description', * }) * .then(console.log) * .catch(console.error); */ 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, guildID }).patch({ data: this.constructor.transformCommand(data) }); return this.add(patched, undefined, guildID); } /** * Deletes an application command. * @param {ApplicationCommandResolvable} command The command to delete * @param {Snowflake} [guildID] ID of the guild where the command is registered, * ignored when using a {@link GuildApplicationCommandManager} * @returns {Promise} * @example * // Delete a command * guild.commands.delete('123456789012345678') * .then(console.log) * .catch(console.error); */ async delete(command, guildID) { const id = this.resolveID(command); if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); await this.commandPath({ id, guildID }).delete(); const cached = this.cache.get(id); this.cache.delete(id); return cached ?? null; } /** * Transforms an {@link ApplicationCommandData} object into something that can be used with the API. * @param {ApplicationCommandData} command The command to transform * @returns {APIApplicationCommand} * @private */ static transformCommand(command) { return { name: command.name, description: command.description, options: command.options?.map(o => ApplicationCommand.transformOption(o)), default_permission: command.defaultPermission, }; } } module.exports = ApplicationCommandManager;