diff --git a/esm.mjs b/esm.mjs index b250cead8..e0215da59 100644 --- a/esm.mjs +++ b/esm.mjs @@ -5,6 +5,7 @@ export default function(token, options) { } export const { + ApplicationCommand, AutocompleteInteraction, Base, Bucket, diff --git a/index.d.ts b/index.d.ts index ccdedc3cd..3951dda68 100644 --- a/index.d.ts +++ b/index.d.ts @@ -16,14 +16,6 @@ declare namespace Eris { // TYPES // Application Commands - type AnyApplicationCommand = ChatInputApplicationCommand | MessageApplicationCommand | UserApplicationCommand; - type ApplicationCommandStructure = ChatInputApplicationCommandStructure | MessageApplicationCommandStructure | UserApplicationCommandStructure; - type ChatInputApplicationCommand = ApplicationCommand; - type ChatInputApplicationCommandStructure = Omit; - type MessageApplicationCommand = Omit, "description" | "options">; - type MessageApplicationCommandStructure = Omit; - type UserApplicationCommand = Omit, "description" | "options">; - type UserApplicationCommandStructure = Omit; type ApplicationCommandOptions = ApplicationCommandOptionsSubCommand | ApplicationCommandOptionsSubCommandGroup | ApplicationCommandOptionsWithValue; type ApplicationCommandOptionsBoolean = ApplicationCommandOption; type ApplicationCommandOptionsChannel = ApplicationCommandOption; @@ -181,15 +173,28 @@ declare namespace Eris { } // Application Commands - interface ApplicationCommand { - application_id: string; + /** Generic T is `true` if editing Guild scoped commands, and `false` if not */ + interface ApplicationCommandEditOptions { + defaultMemberPermissions?: bigint | number | string | Permission | null; + /** @deprecated */ defaultPermission?: boolean; - description: T extends Constants["ApplicationCommandTypes"]["CHAT_INPUT"] ? string : never; - guild_id?: string; - id: string; - name: string; + description?: U extends Constants["ApplicationCommandTypes"]["CHAT_INPUT"] ? string : "" | void; + descriptionLocalizations?: U extends Constants["ApplicationCommandTypes"]["CHAT_INPUT"] ? { [s: string]: string } | null : null; + dmPermission?: T extends true ? never : boolean | null; + name?: string; + nameLocalizations?: { [s: string]: string } | null; + nsfw?: boolean; options?: ApplicationCommandOptions[]; - type: T; + } + /** Generic T is `true` if editing Guild scoped commands, and `false` if not */ + interface ApplicationCommandCreateOptions extends ApplicationCommandEditOptions { + description: U extends Constants["ApplicationCommandTypes"]["CHAT_INPUT"] ? string : "" | void; + name: string; + type?: T; + } + /** Generic T is `true` if editing Guild scoped commands, and `false` if not */ + interface ApplicationCommandBulkEditOptions extends ApplicationCommandCreateOptions { + id?: string; } interface ApplicationCommandOptionsSubCommand { description: string; @@ -260,7 +265,7 @@ declare namespace Eris { application_id: string; guild_id: string; id: string; - permissions?: ApplicationCommandPermissions[]; + permissions: ApplicationCommandPermissions[]; } // Channel @@ -722,6 +727,7 @@ declare namespace Eris { selfVideo: boolean; } interface EventListeners { + applicationCommandPermissionsUpdate: [applicationCommandPermissions: GuildApplicationCommandPermissions]; callCreate: [call: Call]; callDelete: [call: Call]; callRing: [call: Call]; @@ -1610,8 +1616,9 @@ declare namespace Eris { NUMBER: 10; }; ApplicationCommandPermissionTypes: { - ROLE: 1; - USER: 2; + ROLE: 1; + USER: 2; + CHANNEL: 3; }; ApplicationCommandTypes: { CHAT_INPUT: 1; @@ -2167,6 +2174,26 @@ declare namespace Eris { theme: string; } + /** Generic T is `true` if a Guild scoped command, and `false` if not */ + export class ApplicationCommand extends Base { + applicationID: string; + defaultMemberPermissions: Permission; + /** @deprecated */ + defaultPermission?: boolean | null; + description: U extends Constants["ApplicationCommandTypes"]["CHAT_INPUT"] ? string : ""; + descriptionLocalizations?: U extends "CHAT_INPUT" ? Record | null : null; + dmPermission?: boolean; + guild: T extends true ? PossiblyUncachedGuild : never; + name: string; + nameLocalizations?: Record | null; + nsfw?: boolean; + options?: ApplicationCommandOptions[]; + type?: U; + version: string; + delete(): Promise; + edit(options: ApplicationCommandEditOptions): Promise>; + } + class Base implements SimpleJSON { createdAt: number; id: string; @@ -2282,9 +2309,8 @@ declare namespace Eris { banGuildMember(guildID: string, userID: string, options?: BanMemberOptions): Promise; /** @deprecated */ banGuildMember(guildID: string, userID: string, deleteMessageDays?: number, reason?: string): Promise; - bulkEditCommandPermissions(guildID: string, permissions: { id: string; permissions: ApplicationCommandPermissions[] }[]): Promise; - bulkEditCommands(commands: ApplicationCommandStructure[]): Promise; - bulkEditGuildCommands(guildID: string, commands: ApplicationCommandStructure[]): Promise; + bulkEditCommands(commands: ApplicationCommandBulkEditOptions[]): Promise[]>; + bulkEditGuildCommands(guildID: string, commands: ApplicationCommandBulkEditOptions[]): Promise[]>; closeVoiceConnection(guildID: string): void; connect(): Promise; createChannel(guildID: string, name: string): Promise; @@ -2396,10 +2422,10 @@ declare namespace Eris { options: { name: string; avatar?: string | null }, reason?: string ): Promise; - createCommand(command: ApplicationCommandStructure): Promise; + createCommand(command: ApplicationCommandCreateOptions): Promise>; createGroupChannel(userIDs: string[]): Promise; createGuild(name: string, options?: CreateGuildOptions): Promise; - createGuildCommand(guildID: string, command: ApplicationCommandStructure): Promise; + createGuildCommand(guildID: string, command: ApplicationCommandCreateOptions): Promise>; createGuildEmoji(guildID: string, options: EmojiOptions, reason?: string): Promise; createGuildFromTemplate(code: string, name: string, icon?: string): Promise; createGuildScheduledEvent(guildID: string, event: GuildScheduledEventOptions, reason?: string): Promise>; @@ -2452,10 +2478,10 @@ declare namespace Eris { ): Promise; editChannelPosition(channelID: string, position: number, options?: EditChannelPositionOptions): Promise; editChannelPositions(guildID: string, channelPositions: ChannelPosition[]): Promise; - editCommand(commandID: string, command: ApplicationCommandStructure): Promise; - editCommandPermissions(guildID: string, commandID: string, permissions: ApplicationCommandPermissions[]): Promise; + editCommand(commandID: string, command: ApplicationCommandEditOptions): Promise>; + editCommandPermissions(guildID: string, commandID: string, permissions: ApplicationCommandPermissions[], reason?: string): Promise; editGuild(guildID: string, options: GuildOptions, reason?: string): Promise; - editGuildCommand(guildID: string, commandID: string, command: ApplicationCommandStructure): Promise; + editGuildCommand(guildID: string, commandID: string, command: ApplicationCommandEditOptions): Promise>; editGuildDiscovery(guildID: string, options?: DiscoveryOptions): Promise; editGuildEmoji( guildID: string, @@ -2518,9 +2544,9 @@ declare namespace Eris { getChannel(channelID: string): AnyChannel; getChannelInvites(channelID: string): Promise; getChannelWebhooks(channelID: string): Promise; - getCommand(commandID: string): Promise; + getCommand(commandID: string): Promise>; getCommandPermissions(guildID: string, commandID: string): Promise; - getCommands(): Promise; + getCommands(): Promise[]>; getDiscoveryCategories(): Promise; getDMChannel(userID: string): Promise; getEmojiGuild(emojiID: string): Promise; @@ -2530,9 +2556,9 @@ declare namespace Eris { getGuildAuditLogs(guildID: string, limit?: number, before?: string, actionType?: number, userID?: string): Promise; getGuildBan(guildID: string, userID: string): Promise; getGuildBans(guildID: string, options?: GetGuildBansOptions): Promise; - getGuildCommand(guildID: string, commandID: string): Promise; + getGuildCommand(guildID: string, commandID: string): Promise>; getGuildCommandPermissions(guildID: string): Promise; - getGuildCommands(guildID: string): Promise; + getGuildCommands(guildID: string): Promise[]>; getGuildDiscovery(guildID: string): Promise; /** @deprecated */ getGuildEmbed(guildID: string): Promise; @@ -2851,7 +2877,7 @@ declare namespace Eris { banMember(userID: string, options?: BanMemberOptions): Promise; /** @deprecated */ banMember(userID: string, deleteMessageDays?: number, reason?: string): Promise; - bulkEditCommands(commands: ApplicationCommandStructure[]): Promise; + bulkEditCommands(commands: ApplicationCommandBulkEditOptions[]): Promise[]>; createChannel(name: string): Promise; createChannel(name: string, type: Constants["ChannelTypes"]["GUILD_TEXT"], options?: CreateChannelOptions): Promise; createChannel(name: string, type: Constants["ChannelTypes"]["GUILD_VOICE"], options?: CreateChannelOptions): Promise; @@ -2874,7 +2900,7 @@ declare namespace Eris { createChannel(name: string, type: Constants["ChannelTypes"]["GUILD_STAGE"], reason?: string, options?: CreateChannelOptions | string): Promise; /** @deprecated */ createChannel(name: string, type?: number, reason?: string, options?: CreateChannelOptions | string): Promise; - createCommand(command: ApplicationCommandStructure): Promise; + createCommand(command: ApplicationCommandCreateOptions): Promise>; createEmoji(options: { image: string; name: string; roles?: string[] }, reason?: string): Promise; createRole(options: RoleOptions, reason?: string): Promise; createRole(options: Role, reason?: string): Promise; @@ -2896,8 +2922,8 @@ declare namespace Eris { dynamicSplashURL(format?: ImageFormat, size?: number): string | null; edit(options: GuildOptions, reason?: string): Promise; editChannelPositions(channelPositions: ChannelPosition[]): Promise; - editCommand(commandID: string, command: ApplicationCommandStructure): Promise; - editCommandPermissions(permissions: ApplicationCommandPermissions[]): Promise; + editCommand(commandID: string, command: ApplicationCommandEditOptions): Promise>; + editCommandPermissions(permissions: ApplicationCommandPermissions[], reason?: string): Promise; editDiscovery(options?: DiscoveryOptions): Promise; editEmoji(emojiID: string, options: { name: string; roles?: string[] }, reason?: string): Promise; editIntegration(integrationID: string, options: IntegrationOptions): Promise; @@ -2920,9 +2946,9 @@ declare namespace Eris { getAuditLogs(limit?: number, before?: string, actionType?: number, userID?: string): Promise; getBan(userID: string): Promise; getBans(options?: GetGuildBansOptions): Promise; - getCommand(commandID: string): Promise; + getCommand(commandID: string): Promise>; getCommandPermissions(): Promise; - getCommands(): Promise; + getCommands(): Promise[]>; getDiscovery(): Promise; /** @deprecated */ getEmbed(): Promise; diff --git a/index.js b/index.js index 56a662d89..31f1ffb6a 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,7 @@ function Eris(token, options) { return new Client(token, options); } +Eris.ApplicationCommand = require("./lib/structures/ApplicationCommand"); Eris.AutocompleteInteraction = require("./lib/structures/AutocompleteInteraction"); Eris.Base = require("./lib/structures/Base"); Eris.Bucket = require("./lib/util/Bucket"); diff --git a/lib/Client.js b/lib/Client.js index 1c180e4ce..160bd67db 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -1,5 +1,6 @@ "use strict"; +const ApplicationCommand = require("./structures/ApplicationCommand"); const Base = require("./structures/Base"); const Channel = require("./structures/Channel"); const Collection = require("./util/Collection"); @@ -390,29 +391,25 @@ class Client extends EventEmitter { }); } - /** - * Edits command permissions for a multiple commands in a guild. - * Note: You can only add up to 10 permission overwrites for a command. - * @arg {String} guildID The guild ID - * @arg {Array} permissions An array of [partial guild command permissions](https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) - * @returns {Promise>} Returns an array of [GuildApplicationCommandPermissions](https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) objects. - */ - bulkEditCommandPermissions(guildID, permissions) { - if(!guildID) { - throw new Error("You must provide an id of the guild whose permissions you want to edit."); - } - - return this.requestHandler.request("PUT", Endpoints.GUILD_COMMAND_PERMISSIONS(this.application.id, guildID), true, permissions); - } - /** * Bulk create/edit global application commands - * @arg {Array} commands An array of [Command objects](https://discord.com/developers/docs/interactions/application-commands#application-command-object) - * @returns {Promise} Resolves with an array of commands objects + * @arg {Array} commands An array of command objects + * @arg {BigInt | Number | String | Permission?} commands[].defaultMemberPermissions The default permissions the user must have to use the command + * @arg {Boolean} [commands[].defaultPermission=true] [DEPRECATED] Whether the command is enabled by default when the application is added to a guild. Replaced by `defaultMemberPermissions` + * @arg {String} [commands[].description] The command description, required for `CHAT_INPUT` commands + * @arg {Object?} [commands[].descriptionLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command description + * @arg {Boolean?} [commands[].dmPermission=true] Whether the command is available in DMs with the app + * @arg {String} [commands[].id] The command ID, if known + * @arg {String} commands[].name The command name + * @arg {Object?} [commands[].nameLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command name + * @arg {Boolean} [commands[].nsfw=false] Whether the command is age-restricted + * @arg {Array} [commands[].options] The application command [options](https://discord.dev/interactions/application-commands#application-command-object-application-command-option-structure) + * @arg {Number} [commands[].type=1] The command type, either `1` for `CHAT_INPUT`, `2` for `USER` or `3` for `MESSAGE` + * @returns {Promise>} Resolves with the overwritten application commands */ bulkEditCommands(commands) { for(const command of commands) { - if(command.name !== undefined){ + if(command.name !== undefined) { if(command.type === 1 || command.type === undefined) { command.name = command.name.toLowerCase(); if(!command.name.match(/^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$/u)) { @@ -420,19 +417,38 @@ class Client extends EventEmitter { } } } + if(command.defaultPermission !== undefined) { + emitDeprecation("DEFAULT_PERMISSION"); + this.emit("warn", "[DEPRECATED] bulkEditCommands() was called with a `defaultPermission` parameter. This has been replaced with `defaultMemberPermissions`"); + command.default_permission = command.defaultPermission; + } + if(command.defaultMemberPermissions !== undefined) { + command.default_member_permissions = command.defaultMemberPermissions === null ? null : String(command.defaultMemberPermissions.allow || command.defaultMemberPermissions); + } + command.dm_permission = command.dmPermission; } - return this.requestHandler.request("PUT", Endpoints.COMMANDS(this.application.id), true, commands); + return this.requestHandler.request("PUT", Endpoints.COMMANDS(this.application.id), true, commands).then((commands) => commands.map((command) => new ApplicationCommand(command, this))); } /** * Bulk create/edit guild application commands - * @arg {String} guildID Guild id to create the commands in - * @arg {Array} commands An array of [Command objects](https://discord.com/developers/docs/interactions/application-commands#application-command-object) - * @returns {Promise} Resolves with an array of commands objects + * @arg {String} guildID The ID of the guild + * @arg {Array} commands An array of command objects + * @arg {BigInt | Number | String | Permission?} commands[].defaultMemberPermissions The default permissions the user must have to use the command + * @arg {Boolean} [commands[].defaultPermission=true] [DEPRECATED] Whether the command is enabled by default when the application is added to a guild. Replaced by `defaultMemberPermissions` + * @arg {String} [commands[].description] The command description, required for `CHAT_INPUT` commands + * @arg {Object?} [commands[].descriptionLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command description + * @arg {String} [commands[].id] The command ID, if known + * @arg {String} commands[].name The command name + * @arg {Object?} [commands[].nameLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command name + * @arg {Boolean} [commands[].nsfw=false] Whether the command is age-restricted + * @arg {Array} [commands[].options] The application command [options](https://discord.dev/interactions/application-commands#application-command-object-application-command-option-structure) + * @arg {Number} [commands[].type=1] The command type, either `1` for `CHAT_INPUT`, `2` for `USER` or `3` for `MESSAGE` + * @returns {Promise>} Resolves with the overwritten application commands */ bulkEditGuildCommands(guildID, commands) { for(const command of commands) { - if(command.name !== undefined){ + if(command.name !== undefined) { if(command.type === 1 || command.type === undefined) { command.name = command.name.toLowerCase(); if(!command.name.match(/^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$/u)) { @@ -440,8 +456,16 @@ class Client extends EventEmitter { } } } + if(command.defaultMemberPermissions !== undefined) { + command.default_member_permissions = command.defaultMemberPermissions === null ? null : String(command.defaultMemberPermissions.allow || command.defaultMemberPermissions); + } + if(command.defaultPermission !== undefined) { + emitDeprecation("DEFAULT_PERMISSION"); + this.emit("warn", "[DEPRECATED] bulkEditGuildCommands() was called with a `defaultPermission` parameter. This has been replaced with `defaultMemberPermissions`"); + command.default_permission = command.defaultPermission; + } } - return this.requestHandler.request("PUT", Endpoints.GUILD_COMMANDS(this.application.id, guildID), true, commands); + return this.requestHandler.request("PUT", Endpoints.GUILD_COMMANDS(this.application.id, guildID), true, commands).then((commands) => commands.map((command) => new ApplicationCommand(command, this))); } /** @@ -605,15 +629,20 @@ class Client extends EventEmitter { /** * Create a global application command * @arg {Object} command A command object + * @arg {BigInt | Number | String | Permission?} command.defaultMemberPermissions The default permissions the user must have to use the command + * @arg {Boolean} [command.defaultPermission=true] [DEPRECATED] Whether the command is enabled by default when the application is added to a guild. Replaced by `defaultMemberPermissions` + * @arg {String} [command.description] The command description, required for `CHAT_INPUT` commands + * @arg {Object?} [command.descriptionLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command description + * @arg {Boolean?} [command.dmPermission=true] Whether the command is available in DMs with the app * @arg {String} command.name The command name - * @arg {String} [command.description] The command description (Slash Commands Only) - * @arg {Array} [command.options] An array of [command options](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure) - * @arg {Number} [type=1] The type of application command, 1 for slash command, 2 for user, and 3 for message - * @arg {Boolean} [command.defaultPermission=true] Whether the command is enabled by default when the app is added to a guild - * @returns {Promise} Resolves with a commands object + * @arg {Object?} [command.nameLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command name + * @arg {Boolean} [command.nsfw=false] Whether the command is age-restricted + * @arg {Array} [command.options] The application command [options](https://discord.dev/interactions/application-commands#application-command-object-application-command-option-structure) + * @arg {Number} [command.type=1] The command type, either `1` for `CHAT_INPUT`, `2` for `USER` or `3` for `MESSAGE` + * @returns {Promise} Resolves with the created application command */ createCommand(command) { - if(command.name !== undefined){ + if(command.name !== undefined) { if(command.type === 1 || command.type === undefined) { command.name = command.name.toLowerCase(); if(!command.name.match(/^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$/u)) { @@ -621,8 +650,16 @@ class Client extends EventEmitter { } } } - command.default_permission = command.defaultPermission; - return this.requestHandler.request("POST", Endpoints.COMMANDS(this.application.id), true, command); + if(command.defaultMemberPermissions !== undefined) { + command.default_member_permissions = command.defaultMemberPermissions === null ? null : String(command.defaultMemberPermissions.allow || command.defaultMemberPermissions); + } + if(command.defaultPermission !== undefined) { + emitDeprecation("DEFAULT_PERMISSION"); + this.emit("warn", "[DEPRECATED] createCommand() was called with a `defaultPermission` parameter. This has been replaced with `defaultMemberPermissions`"); + command.default_permission = command.defaultPermission; + } + command.dm_permission = command.dmPermission; + return this.requestHandler.request("POST", Endpoints.COMMANDS(this.application.id), true, command).then((command) => new ApplicationCommand(command, this)); } /** @@ -673,17 +710,21 @@ class Client extends EventEmitter { /** * Create a guild application command - * @arg {String} guildID The ID of the guild to create the command in + * @arg {String} guildID The ID of the guild * @arg {Object} command A command object + * @arg {BigInt | Number | String | Permission?} command.defaultMemberPermissions The default permissions the user must have to use the command + * @arg {Boolean} [command.defaultPermission=true] [DEPRECATED] Whether the command is enabled by default when the application is added to a guild. Replaced by `defaultMemberPermissions` + * @arg {String} [command.description] The command description, required for `CHAT_INPUT` commands + * @arg {Object?} [command.descriptionLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command description * @arg {String} command.name The command name - * @arg {String} [command.description] The command description (Slash Commands Only) - * @arg {Array} [command.options] An array of [command options](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure) - * @arg {Number} [type=1] The type of application command, 1 for slash command, 2 for user, and 3 for message - * @arg {Boolean} [command.defaultPermission] Whether the command is enabled by default when the app is added to a guild - * @returns {Promise} Resolves with a commands object + * @arg {Object?} [command.nameLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command name + * @arg {Boolean} [command.nsfw=false] Whether the command is age-restricted + * @arg {Array} [command.options] The application command [options](https://discord.dev/interactions/application-commands#application-command-object-application-command-option-structure) + * @arg {Number} [command.type=1] The command type, either `1` for `CHAT_INPUT`, `2` for `USER` or `3` for `MESSAGE` + * @returns {Promise} Resolves with the created application command */ createGuildCommand(guildID, command) { - if(command.name !== undefined){ + if(command.name !== undefined) { if(command.type === 1 || command.type === undefined) { command.name = command.name.toLowerCase(); if(!command.name.match(/^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$/u)) { @@ -691,8 +732,15 @@ class Client extends EventEmitter { } } } - command.default_permission = command.defaultPermission; - return this.requestHandler.request("POST", Endpoints.GUILD_COMMANDS(this.application.id, guildID), true, command); + if(command.defaultMemberPermissions !== undefined) { + command.default_member_permissions = command.defaultMemberPermissions === null ? null : String(command.defaultMemberPermissions.allow || command.defaultMemberPermissions); + } + if(command.defaultPermission !== undefined) { + emitDeprecation("DEFAULT_PERMISSION"); + this.emit("warn", "[DEPRECATED] createGuildCommand() was called with a `defaultPermission` parameter. This has been replaced with `defaultMemberPermissions`"); + command.default_permission = command.defaultPermission; + } + return this.requestHandler.request("POST", Endpoints.GUILD_COMMANDS(this.application.id, guildID), true, command).then((command) => new ApplicationCommand(command, this)); } /** @@ -1471,19 +1519,24 @@ class Client extends EventEmitter { /** * Edit a global application command - * @arg {String} commandID The command id + * @arg {String} commandID The command ID * @arg {Object} command A command object - * @arg {String} command.name The command name - * @arg {String} [command.description] The command description (Slash Commands Only) - * @arg {Array} [command.options] An array of [command options](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure) - * @arg {Boolean} [command.defaultPermission] Whether the command is enabled by default when the app is added to a guild - * @returns {Promise} Resolves with a commands object + * @arg {BigInt | Number | String | Permission?} [command.defaultMemberPermissions] The default permissions the user must have to use the command + * @arg {Boolean} [command.defaultPermission] [DEPRECATED] Whether the command is enabled by default when the application is added to a guild. Replaced by `defaultMemberPermissions` + * @arg {String} [command.description] The command description, required for `CHAT_INPUT` commands + * @arg {Object?} [command.descriptionLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command description + * @arg {Boolean?} [command.dmPermission=true] Whether the command is available in DMs with the app + * @arg {String} [command.name] The command name + * @arg {Object?} [command.nameLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command name + * @arg {Boolean} [command.nsfw] Whether the command is age-restricted + * @arg {Array} [command.options] The application command [options](https://discord.dev/interactions/application-commands#application-command-object-application-command-option-structure) + * @returns {Promise} Resolves with the edited application command */ editCommand(commandID, command) { if(!commandID) { throw new Error("You must provide an id of the command to edit."); } - if(command.name !== undefined){ + if(command.name !== undefined) { if(command.type === 1 || command.type === undefined) { command.name = command.name.toLowerCase(); if(!command.name.match(/^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$/u)) { @@ -1491,8 +1544,16 @@ class Client extends EventEmitter { } } } - command.default_permission = command.defaultPermission; - return this.requestHandler.request("PATCH", Endpoints.COMMAND(this.application.id, commandID), true, command); + if(command.defaultPermission !== undefined) { + emitDeprecation("DEFAULT_PERMISSION"); + this.emit("warn", "[DEPRECATED] editCommand() was called with a `defaultPermission` parameter. This has been replaced with `defaultMemberPermissions`"); + command.default_permission = command.defaultPermission; + } + if(command.defaultMemberPermissions !== undefined) { + command.default_member_permissions = command.defaultMemberPermissions === null ? null : String(command.defaultMemberPermissions.allow || command.defaultMemberPermissions); + } + command.dm_permission = command.dmPermission; + return this.requestHandler.request("PATCH", Endpoints.COMMAND(this.application.id, commandID), true, command).then((command) => new ApplicationCommand(command, this)); } /** @@ -1502,15 +1563,16 @@ class Client extends EventEmitter { * @arg {String} commandID The command id * @arg {Array} permissions An array of [permissions objects](https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure) * @returns {Promise} Resolves with a [GuildApplicationCommandPermissions](https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) object. + * @arg {String} [reason] The reason to be displayed in audit logs */ - editCommandPermissions(guildID, commandID, permissions) { + editCommandPermissions(guildID, commandID, permissions, reason) { if(!guildID) { throw new Error("You must provide an id of the guild whose permissions you want to edit."); } if(!commandID) { throw new Error("You must provide an id of the command whose permissions you want to edit."); } - return this.requestHandler.request("PUT", Endpoints.COMMAND_PERMISSIONS(this.application.id, guildID, commandID), true, {permissions}); + return this.requestHandler.request("PUT", Endpoints.COMMAND_PERMISSIONS(this.application.id, guildID, commandID), true, {permissions, reason}); } /** @@ -1564,19 +1626,24 @@ class Client extends EventEmitter { /** * Edit a guild application command - * @arg {String} guildID The guild ID + * @arg {String} guildID The ID of the guild + * @arg {String} commandID The ID of the command * @arg {Object} command A command object - * @arg {String} command.name The command name - * @arg {String} [command.description] The command description (Slash Commands Only) - * @arg {Array} [command.options] An array of [command options](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure) - * @arg {Boolean} [command.defaultPermission] Whether the command is enabled by default when the app is added to a guild - * @returns {Promise} Resolves with a commands object + * @arg {BigInt | Number | String | Permission?} [command.defaultMemberPermissions] The default permissions the user must have to use the command + * @arg {Boolean} [command.defaultPermission] [DEPRECATED] Whether the command is enabled by default when the application is added to a guild. Replaced by `defaultMemberPermissions` + * @arg {String} [command.description] The command description, required for `CHAT_INPUT` commands + * @arg {Object?} [command.descriptionLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command description + * @arg {String} [command.name] The command name + * @arg {Object?} [command.nameLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command name + * @arg {Boolean} [command.nsfw] Whether the command is age-restricted + * @arg {Array} [command.options] The application command [options](https://discord.dev/interactions/application-commands#application-command-object-application-command-option-structure) + * @returns {Promise} Resolves with the edited application command */ editGuildCommand(guildID, commandID, command) { if(!commandID) { throw new Error("You must provide an id of the command to edit."); } - if(command.name !== undefined){ + if(command.name !== undefined) { if(command.type === 1 || command.type === undefined) { command.name = command.name.toLowerCase(); if(!command.name.match(/^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$/u)) { @@ -1584,8 +1651,15 @@ class Client extends EventEmitter { } } } - command.default_permission = command.defaultPermission; - return this.requestHandler.request("PATCH", Endpoints.GUILD_COMMAND(this.application.id, guildID, commandID), true, command); + if(command.defaultMemberPermissions !== undefined) { + command.default_member_permissions = command.defaultMemberPermissions === null ? null : String(command.defaultMemberPermissions.allow || command.defaultMemberPermissions); + } + if(command.defaultPermission !== undefined) { + emitDeprecation("DEFAULT_PERMISSION"); + this.emit("warn", "[DEPRECATED] editGuildCommand() was called with a `defaultPermission` parameter. This has been replaced with `defaultMemberPermissions`"); + command.default_permission = command.defaultPermission; + } + return this.requestHandler.request("PATCH", Endpoints.GUILD_COMMAND(this.application.id, guildID, commandID), true, command).then((command) => new ApplicationCommand(command, this)); } /** diff --git a/lib/Constants.js b/lib/Constants.js index ce4d40bca..8b871903d 100644 --- a/lib/Constants.js +++ b/lib/Constants.js @@ -26,8 +26,9 @@ module.exports.ApplicationCommandOptionTypes = { }; module.exports.ApplicationCommandPermissionTypes = { - ROLE: 1, - USER: 2 + ROLE: 1, + USER: 2, + CHANNEL: 3 }; module.exports.ApplicationCommandTypes = { diff --git a/lib/gateway/Shard.js b/lib/gateway/Shard.js index 4a0d8a977..82eb9f9e0 100644 --- a/lib/gateway/Shard.js +++ b/lib/gateway/Shard.js @@ -2498,11 +2498,20 @@ class Shard extends EventEmitter { /** * Fired when an interaction is created * @event Client#interactionCreate - * @prop {PingInteraction | CommandInteraction | ComponentInteraction | AutocompleteInteraction | UnknownInteraction} Interaction The Interaction that was created + * @prop {PingInteraction | CommandInteraction | ComponentInteraction | AutocompleteInteraction | UnknownInteraction} interaction The Interaction that was created */ this.emit("interactionCreate", Interaction.from(packet.d, this.client)); break; } + case "APPLICATION_COMMAND_PERMISSIONS_UPDATE": { + /** + * Fired when an application command's permissions are updated + * @event Client#applicationCommandPermissionsUpdate + * @prop {Object} guildApplicationCommandPermissions The updated command permissions + */ + this.emit("applicationCommandPermissionsUpdate", packet.d); + break; + } default: { /** * Fired when the shard encounters an unknown packet diff --git a/lib/rest/Endpoints.js b/lib/rest/Endpoints.js index 654a2f62a..7724487c8 100644 --- a/lib/rest/Endpoints.js +++ b/lib/rest/Endpoints.js @@ -41,7 +41,7 @@ module.exports.GUILD_BAN = (guildID, memberID) module.exports.GUILD_BANS = (guildID) => `/guilds/${guildID}/bans`; module.exports.GUILD_CHANNELS = (guildID) => `/guilds/${guildID}/channels`; module.exports.GUILD_COMMAND = (applicationID, guildID, commandID) => `/applications/${applicationID}/guilds/${guildID}/commands/${commandID}`; -module.exports.GUILD_COMMAND_PERMISSIONS = (applicationID, guildID) => `/applications/${applicationID}/guilds/${guildID}/commands/permissions`; +module.exports.GUILD_COMMAND_PERMISSIONS = (applicationID, guildID) => `/applications/${applicationID}/guilds/${guildID}/commands/permissions`; module.exports.GUILD_COMMANDS = (applicationID, guildID) => `/applications/${applicationID}/guilds/${guildID}/commands`; module.exports.GUILD_DISCOVERY = (guildID) => `/guilds/${guildID}/discovery-metadata`; module.exports.GUILD_DISCOVERY_CATEGORY = (guildID, categoryID) => `/guilds/${guildID}/discovery-categories/${categoryID}`; diff --git a/lib/structures/ApplicationCommand.js b/lib/structures/ApplicationCommand.js new file mode 100644 index 000000000..7208b308b --- /dev/null +++ b/lib/structures/ApplicationCommand.js @@ -0,0 +1,108 @@ +"use strict"; + +const Base = require("./Base"); +const Permission = require("./Permission"); + +/** +* Represents an application command +* @prop {String} applicationID The ID of the application that this command belongs to +* @prop {Permission?} defaultMemberPermissions The permissions required by default for this command to be usable +* @prop {Boolean?} defaultPermission [DEPRECATED] Indicates whether the command is enabled by default when the application is added to a guild +* @prop {String} description The description of the command (empty for user & message commands) +* @prop {Object?} descriptionLocalizations A map of [locales](https://discord.com/developers/docs/reference#locales) to descriptions for that locale +* @prop {Boolean?} dmPermission If this command can be used in direct messages (global commands only) +* @prop {Guild?} guild The guild associated with this command (guild commands only) +* @prop {String} id The ID of the application command +* @prop {String} name The name of the command +* @prop {Object?} nameLocalizations A map of [locales](https://discord.com/developers/docs/reference#locales) to names for that locale +* @prop {Boolean?} nsfw Indicates whether the command is age-restricted +* @prop {Array?} options The [options](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure) associated with this command +* @prop {Number} type The [command type](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-types) +* @prop {String} version The id of the version of this command +*/ +class ApplicationCommand extends Base { + constructor(data, client) { + super(data.id); + this._client = client; + this.applicationID = data.application_id; + this.name = data.name; + this.description = data.description; + this.defaultMemberPermissions = data.defaultMemberPermissions == null ? null : new Permission(data.default_member_permissions); + this.version = data.version; + + if(data.type !== undefined) { + this.type = data.type; + } + if(data.guild_id !== undefined) { + this.guild = client.guilds.get(data.guild_id) || { + id: data.guild_id + }; + } + if(data.name_localizations !== undefined) { + this.nameLocalizations = data.name_localizations; + } + if(data.description_localizations !== undefined) { + this.descriptionLocalizations = data.description_localizations; + } + if(data.options !== undefined) { + this.options = data.options; + } + if(data.dm_permission !== undefined) { + this.dmPermission = data.dm_permission; + } + if(data.default_permission !== undefined) { + this.defaultPermission = data.default_permission; + } + if(data.nsfw !== undefined) { + this.nsfw = data.nsfw; + } + } + + + /** + * Delete the command + * @returns {Promise} + */ + delete() { + return this.guildID === undefined ? this._client.deleteCommand.call(this._client, this.id) : this._client.deleteGuildCommand.call(this._client, this.guildID, this.id); + } + + /** + * Edit this application command + * @arg {Object} command A command object + * @arg {BigInt | Number | String | Permission?} [command.defaultMemberPermissions] The default permissions the user must have to use the command + * @arg {Boolean} [command.defaultPermission] [DEPRECATED] Whether the command is enabled by default when the application is added to a guild. Replaced by `defaultMemberPermissions` + * @arg {String} [command.description] The command description, required for `CHAT_INPUT` commands + * @arg {Object?} [command.descriptionLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command description + * @arg {Boolean?} [command.dmPermission=true] Whether the command is available in DMs with the app (Global applications only) + * @arg {String} [command.name] The command name + * @arg {Object?} [command.nameLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command name + * @arg {Boolean} [command.nsfw] Whether the command is age-restricted + * @arg {Array} [command.options] The application command [options](https://discord.dev/interactions/application-commands#application-command-object-application-command-option-structure) + * @returns {Promise} Resolves with the edited application command + */ + edit(command) { + return this.guildID === undefined ? this._client.editCommand.call(this._client, this.id, command) : this._client.editGuildCommand.call(this._client, this.id, this.guildID, command); + } + + toJSON(props = []) { + return super.toJSON([ + "applicationID", + "defaultMemberPermissions", + "defaultPermission", + "description", + "descriptionLocalizations", + "dmPermission", + "guild", + "name", + "nameLocalizations", + "nsfw", + "options", + "type", + "version", + ...props + ]); + } +} + +module.exports = ApplicationCommand; diff --git a/lib/structures/Guild.js b/lib/structures/Guild.js index 7f22a09c0..654f3fe98 100644 --- a/lib/structures/Guild.js +++ b/lib/structures/Guild.js @@ -384,19 +384,19 @@ class Guild extends Base { } /** - * Edits command permissions for a multiple commands in a guild. - * Note: You can only add up to 10 permission overwrites for a command. - * @arg {Array} permissions An array of [partial guild command permissions](https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) - * @returns {Promise>} Returns an array of [GuildApplicationCommandPermissions](https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) objects. - */ - bulkEditCommandPermissions(permissions) { - return this._client.bulkEditCommandPermissions.call(this._client, this.id, permissions); - } - - /** - * Bulk create/edit guild application commands - * @arg {Array} commands An array of [Command objects](https://discord.com/developers/docs/interactions/application-commands#application-command-object) - * @returns {Promise} Resolves with a commands object + * Bulk create/edit the guild application commands + * @arg {Array} commands An array of command objects + * @arg {BigInt | Number | String | Permission?} commands[].defaultMemberPermissions The default permissions the user must have to use the command + * @arg {Boolean} [commands[].defaultPermission=true] [DEPRECATED] Whether the command is enabled by default when the application is added to a guild. Replaced by `defaultMemberPermissions` + * @arg {String} [commands[].description] The command description, required for `CHAT_INPUT` commands + * @arg {Object?} [commands[].descriptionLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command description + * @arg {String} [commands[].id] The command ID, if known + * @arg {String} commands[].name The command name + * @arg {Object?} [commands[].nameLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command name + * @arg {Boolean} [commands[].nsfw=false] Whether the command is age-restricted + * @arg {Array} [commands[].options] The application command [options](https://discord.dev/interactions/application-commands#application-command-object-application-command-option-structure) + * @arg {Number} [commands[].type=1] The command type, either `1` for `CHAT_INPUT`, `2` for `USER` or `3` for `MESSAGE` + * @returns {Promise>} Resolves with the overwritten application commands */ bulkEditCommands(commands) { return this._client.bulkEditGuildCommands.call(this._client, this.id, commands); @@ -424,13 +424,17 @@ class Guild extends Base { /** * Create a guild application command - * @arg {Object} command A command object + * @arg {Object} command An array of command objects + * @arg {BigInt | Number | String | Permission?} command.defaultMemberPermissions The default permissions the user must have to use the command + * @arg {Boolean} [command.defaultPermission=true] [DEPRECATED] Whether the command is enabled by default when the application is added to a guild. Replaced by `defaultMemberPermissions` + * @arg {String} [command.description] The command description, required for `CHAT_INPUT` commands + * @arg {Object?} [command.descriptionLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command description * @arg {String} command.name The command name - * @arg {String} [command.description] The command description (Slash Commands Only) - * @arg {Array} [command.options] An array of [command options](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure) - * @arg {Number} [type=1] The type of application command, 1 for slash command, 2 for user, and 3 for message - * @arg {Boolean} [command.defaultPermission] Whether the command is enabled by default when the app is added to a guild - * @returns {Promise} Resolves with a command object + * @arg {Object?} [command.nameLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command name + * @arg {Boolean} [command.nsfw=false] Whether the command is age-restricted + * @arg {Array} [command.options] The application command [options](https://discord.dev/interactions/application-commands#application-command-object-application-command-option-structure) + * @arg {Number} [command.type=1] The command type, either `1` for `CHAT_INPUT`, `2` for `USER` or `3` for `MESSAGE` + * @returns {Promise} Resolves with the created application command */ createCommand(command) { return this._client.createGuildCommand.call(this._client, this.id, command); @@ -679,16 +683,20 @@ class Guild extends Base { /** * Edit a guild application command - * @arg {String} commandID The command id - * @arg {Object} command A command object - * @arg {String} command.name The command name - * @arg {String} [command.description] The command description (Slash Commands Only) - * @arg {Array} [command.options] An array of [command options](https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure) - * @arg {Boolean} [command.defaultPermission] Whether the command is enabled by default when the app is added to a guild - * @returns {Promise} Resolves with a command object + * @arg {String} commandID The command ID + * @arg {Array} command An array of command objects + * @arg {BigInt | Number | String | Permission?} [command.defaultMemberPermissions] The default permissions the user must have to use the command + * @arg {Boolean} [command.defaultPermission] [DEPRECATED] Whether the command is enabled by default when the application is added to a guild. Replaced by `defaultMemberPermissions` + * @arg {String} [command.description] The command description, required for `CHAT_INPUT` commands + * @arg {Object?} [command.descriptionLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command description + * @arg {String} [command.name] The command name + * @arg {Object?} [command.nameLocalizations] Localization directory with keys in [available locales](https://discord.dev/reference#locales) for the command name + * @arg {Boolean} [command.nsfw] Whether the command is age-restricted + * @arg {Array} [command.options] The application command [options](https://discord.dev/interactions/application-commands#application-command-object-application-command-option-structure) + * @returns {Promise} Resolves with the edited application command */ - editCommand(commandID, commands) { - return this._client.editGuildCommand.call(this._client, this.id, commandID, commands); + editCommand(commandID, command) { + return this._client.editGuildCommand.call(this._client, this.id, commandID, command); } /** @@ -696,10 +704,11 @@ class Guild extends Base { * Note: You can only add up to 10 permission overwrites for a command. * @arg {String} commandID The command id * @arg {Array} permissions An array of [permissions objects](https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure) + * @arg {String} [reason] The reason to be displayed in audit logs * @returns {Promise} Resolves with a [GuildApplicationCommandPermissions](https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-guild-application-command-permissions-structure) object. */ - editCommandPermissions(commandID, permissions) { - return this._client.editCommandPermissions.call(this._client, this.id, commandID, permissions); + editCommandPermissions(commandID, permissions, reason) { + return this._client.editCommandPermissions.call(this._client, this.id, commandID, permissions, reason); } /** diff --git a/lib/util/emitDeprecation.js b/lib/util/emitDeprecation.js index 190e530a0..362765a88 100644 --- a/lib/util/emitDeprecation.js +++ b/lib/util/emitDeprecation.js @@ -1,5 +1,6 @@ const warningMessages = { CREATE_CHANNEL_OPTIONS: "Passing parentID or reason string arguments directly to createChannel() is deprecated. Use an options object instead.", + DEFAULT_PERMISSION: "Passing a defaultPermission for application commands is deprecated. Use defaultMemberPermissions instead.", DELETE_MESSAGE_DAYS: "Passing the deleteMessageDays parameter to banGuildMember is deprecated. Use an options object with deleteMessageSeconds instead.", DM_REACTION_REMOVE: "Passing a userID when using removeMessageReaction() in a PrivateChannel is deprecated. This behavior is no longer supported by Discord.", GET_REACTION_BEFORE: "Passing the before parameter to getMessageReaction() is deprecated. Discord no longer supports this parameter.",