From 0266f280960729b27bf65ba0ee7b7bd8659f304d Mon Sep 17 00:00:00 2001 From: monbrey Date: Wed, 11 Aug 2021 08:56:12 +1000 Subject: [PATCH] feat: right-clickybois (context menu support for ApplicationCommand and CommandInteraction) (#6176) Co-authored-by: SpaceEEC Co-authored-by: Sugden <28943913+NotSugden@users.noreply.github.com> --- src/client/actions/InteractionCreate.js | 19 ++- src/managers/ApplicationCommandManager.js | 2 + src/structures/ApplicationCommand.js | 9 +- src/structures/BaseCommandInteraction.js | 142 ++++++++++++++++++ src/structures/CommandInteraction.js | 139 +---------------- .../CommandInteractionOptionResolver.js | 18 ++- src/structures/ContextMenuInteraction.js | 61 ++++++++ src/structures/Interaction.js | 10 +- src/util/Constants.js | 9 ++ typings/enums.d.ts | 6 + typings/index.d.ts | 85 +++++++---- typings/tests.ts | 8 +- 12 files changed, 328 insertions(+), 180 deletions(-) create mode 100644 src/structures/BaseCommandInteraction.js create mode 100644 src/structures/ContextMenuInteraction.js diff --git a/src/client/actions/InteractionCreate.js b/src/client/actions/InteractionCreate.js index 85072d059..eeb68c0bc 100644 --- a/src/client/actions/InteractionCreate.js +++ b/src/client/actions/InteractionCreate.js @@ -3,8 +3,9 @@ const Action = require('./Action'); const ButtonInteraction = require('../../structures/ButtonInteraction'); const CommandInteraction = require('../../structures/CommandInteraction'); +const ContextMenuInteraction = require('../../structures/ContextMenuInteraction'); const SelectMenuInteraction = require('../../structures/SelectMenuInteraction'); -const { Events, InteractionTypes, MessageComponentTypes } = require('../../util/Constants'); +const { Events, InteractionTypes, MessageComponentTypes, ApplicationCommandTypes } = require('../../util/Constants'); let deprecationEmitted = false; @@ -18,7 +19,21 @@ class InteractionCreateAction extends Action { let InteractionType; switch (data.type) { case InteractionTypes.APPLICATION_COMMAND: - InteractionType = CommandInteraction; + switch (data.data.type) { + case ApplicationCommandTypes.CHAT_INPUT: + InteractionType = CommandInteraction; + break; + case ApplicationCommandTypes.USER: + case ApplicationCommandTypes.MESSAGE: + InteractionType = ContextMenuInteraction; + break; + default: + client.emit( + Events.DEBUG, + `[INTERACTION] Received application command interaction with unknown type: ${data.data.type}`, + ); + return; + } break; case InteractionTypes.MESSAGE_COMPONENT: switch (data.data.component_type) { diff --git a/src/managers/ApplicationCommandManager.js b/src/managers/ApplicationCommandManager.js index 77dd16b6f..dbc83dcb2 100644 --- a/src/managers/ApplicationCommandManager.js +++ b/src/managers/ApplicationCommandManager.js @@ -5,6 +5,7 @@ const ApplicationCommandPermissionsManager = require('./ApplicationCommandPermis const CachedManager = require('./CachedManager'); const { TypeError } = require('../errors'); const ApplicationCommand = require('../structures/ApplicationCommand'); +const { ApplicationCommandTypes } = require('../util/Constants'); /** * Manages API methods for application commands and stores their cache. @@ -207,6 +208,7 @@ class ApplicationCommandManager extends CachedManager { return { name: command.name, description: command.description, + type: typeof command.type === 'number' ? command.type : ApplicationCommandTypes[command.type], options: command.options?.map(o => ApplicationCommand.transformOption(o)), default_permission: command.defaultPermission, }; diff --git a/src/structures/ApplicationCommand.js b/src/structures/ApplicationCommand.js index 104048c66..139693de1 100644 --- a/src/structures/ApplicationCommand.js +++ b/src/structures/ApplicationCommand.js @@ -2,7 +2,7 @@ const Base = require('./Base'); const ApplicationCommandPermissionsManager = require('../managers/ApplicationCommandPermissionsManager'); -const { ApplicationCommandOptionTypes } = require('../util/Constants'); +const { ApplicationCommandOptionTypes, ApplicationCommandTypes } = require('../util/Constants'); const SnowflakeUtil = require('../util/SnowflakeUtil'); /** @@ -44,6 +44,12 @@ class ApplicationCommand extends Base { */ this.permissions = new ApplicationCommandPermissionsManager(this); + /** + * The type of this application command + * @type {ApplicationCommandType} + */ + this.type = ApplicationCommandTypes[data.type]; + this._patch(data); } @@ -105,6 +111,7 @@ class ApplicationCommand extends Base { * @typedef {Object} ApplicationCommandData * @property {string} name The name of the command * @property {string} description The description of the command + * @property {ApplicationCommandTypes} [type] The type of the command * @property {ApplicationCommandOptionData[]} [options] Options for the command * @property {boolean} [defaultPermission] Whether the command is enabled by default when the app is added to a guild */ diff --git a/src/structures/BaseCommandInteraction.js b/src/structures/BaseCommandInteraction.js new file mode 100644 index 000000000..0eb331fb9 --- /dev/null +++ b/src/structures/BaseCommandInteraction.js @@ -0,0 +1,142 @@ +'use strict'; + +const Interaction = require('./Interaction'); +const InteractionWebhook = require('./InteractionWebhook'); +const InteractionResponses = require('./interfaces/InteractionResponses'); +const { ApplicationCommandOptionTypes } = require('../util/Constants'); + +/** + * Represents a command interaction. + * @extends {Interaction} + * @implements {InteractionResponses} + * @abstract + */ +class BaseCommandInteraction extends Interaction { + constructor(client, data) { + super(client, data); + + /** + * The channel this interaction was sent in + * @type {?TextBasedChannels} + * @name BaseCommandInteraction#channel + * @readonly + */ + + /** + * The id of the channel this interaction was sent in + * @type {Snowflake} + * @name BaseCommandInteraction#channelId + */ + + /** + * The invoked application command's id + * @type {Snowflake} + */ + this.commandId = data.data.id; + + /** + * The invoked application command's name + * @type {string} + */ + this.commandName = data.data.name; + + /** + * Whether the reply to this interaction has been deferred + * @type {boolean} + */ + this.deferred = false; + + /** + * Whether this interaction has already been replied to + * @type {boolean} + */ + this.replied = false; + + /** + * Whether the reply to this interaction is ephemeral + * @type {?boolean} + */ + this.ephemeral = null; + + /** + * An associated interaction webhook, can be used to further interact with this interaction + * @type {InteractionWebhook} + */ + this.webhook = new InteractionWebhook(this.client, this.applicationId, this.token); + } + + /** + * The invoked application command, if it was fetched before + * @type {?ApplicationCommand} + */ + get command() { + const id = this.commandId; + return this.guild?.commands.cache.get(id) ?? this.client.application.commands.cache.get(id) ?? null; + } + + /** + * Represents an option of a received command interaction. + * @typedef {Object} CommandInteractionOption + * @property {string} name The name of the option + * @property {ApplicationCommandOptionType} type The type of the option + * @property {string|number|boolean} [value] The value of the option + * @property {CommandInteractionOption[]} [options] Additional options if this option is a + * subcommand (group) + * @property {User} [user] The resolved user + * @property {GuildMember|APIGuildMember} [member] The resolved member + * @property {GuildChannel|APIChannel} [channel] The resolved channel + * @property {Role|APIRole} [role] The resolved role + */ + + /** + * Transforms an option received from the API. + * @param {APIApplicationCommandOption} option The received option + * @param {APIInteractionDataResolved} resolved The resolved interaction data + * @returns {CommandInteractionOption} + * @private + */ + transformOption(option, resolved) { + const result = { + name: option.name, + type: ApplicationCommandOptionTypes[option.type], + }; + + if ('value' in option) result.value = option.value; + if ('options' in option) result.options = option.options.map(opt => this.transformOption(opt, resolved)); + + if (resolved) { + const user = resolved.users?.[option.value]; + if (user) result.user = this.client.users._add(user); + + const member = resolved.members?.[option.value]; + if (member) result.member = this.guild?.members._add({ user, ...member }) ?? member; + + const channel = resolved.channels?.[option.value]; + if (channel) result.channel = this.client.channels._add(channel, this.guild) ?? channel; + + const role = resolved.roles?.[option.value]; + if (role) result.role = this.guild?.roles._add(role) ?? role; + } + + return result; + } + + // These are here only for documentation purposes - they are implemented by InteractionResponses + /* eslint-disable no-empty-function */ + defer() {} + reply() {} + fetchReply() {} + editReply() {} + deleteReply() {} + followUp() {} +} + +InteractionResponses.applyToClass(BaseCommandInteraction, ['deferUpdate', 'update']); + +module.exports = BaseCommandInteraction; + +/* eslint-disable max-len */ +/** + * @external APIInteractionDataResolved + * @see {@link https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-resolved-data-structure} + */ diff --git a/src/structures/CommandInteraction.js b/src/structures/CommandInteraction.js index 57894029c..10693e65f 100644 --- a/src/structures/CommandInteraction.js +++ b/src/structures/CommandInteraction.js @@ -1,51 +1,16 @@ 'use strict'; +const BaseCommandInteraction = require('./BaseCommandInteraction'); const CommandInteractionOptionResolver = require('./CommandInteractionOptionResolver'); -const Interaction = require('./Interaction'); -const InteractionWebhook = require('./InteractionWebhook'); -const InteractionResponses = require('./interfaces/InteractionResponses'); -const { ApplicationCommandOptionTypes } = require('../util/Constants'); /** * Represents a command interaction. - * @extends {Interaction} - * @implements {InteractionResponses} + * @extends {BaseCommandInteraction} */ -class CommandInteraction extends Interaction { +class CommandInteraction extends BaseCommandInteraction { constructor(client, data) { super(client, data); - /** - * The channel this interaction was sent in - * @type {?TextBasedChannels} - * @name CommandInteraction#channel - * @readonly - */ - - /** - * The id of the channel this interaction was sent in - * @type {Snowflake} - * @name CommandInteraction#channelId - */ - - /** - * The invoked application command's id - * @type {Snowflake} - */ - this.commandId = data.data.id; - - /** - * The invoked application command's name - * @type {string} - */ - this.commandName = data.data.name; - - /** - * Whether the reply to this interaction has been deferred - * @type {boolean} - */ - this.deferred = false; - /** * The options passed to the command. * @type {CommandInteractionOptionResolver} @@ -54,105 +19,7 @@ class CommandInteraction extends Interaction { this.client, data.data.options?.map(option => this.transformOption(option, data.data.resolved)) ?? [], ); - - /** - * Whether this interaction has already been replied to - * @type {boolean} - */ - this.replied = false; - - /** - * Whether the reply to this interaction is ephemeral - * @type {?boolean} - */ - this.ephemeral = null; - - /** - * An associated interaction webhook, can be used to further interact with this interaction - * @type {InteractionWebhook} - */ - this.webhook = new InteractionWebhook(this.client, this.applicationId, this.token); } - - /** - * The invoked application command, if it was fetched before - * @type {?ApplicationCommand} - */ - get command() { - const id = this.commandId; - return this.guild?.commands.cache.get(id) ?? this.client.application.commands.cache.get(id) ?? null; - } - - /** - * Represents an option of a received command interaction. - * @typedef {Object} CommandInteractionOption - * @property {string} name The name of the option - * @property {ApplicationCommandOptionType} type The type of the option - * @property {string|number|boolean} [value] The value of the option - * @property {CommandInteractionOption[]} [options] Additional options if this option is a - * subcommand (group) - * @property {User} [user] The resolved user - * @property {GuildMember|APIInteractionDataResolvedOption} [member] The resolved member - * @property {GuildChannel|APIInteractionDataResolvedOption} [channel] The resolved channel - * @property {Role|APIRole} [role] The resolved role - */ - - /** - * Transforms an option received from the API. - * @param {APIApplicationCommandOption} option The received option - * @param {APIApplicationCommandOptionResolved} resolved The resolved interaction data - * @returns {CommandInteractionOption} - * @private - */ - transformOption(option, resolved) { - const result = { - name: option.name, - type: ApplicationCommandOptionTypes[option.type], - }; - - if ('value' in option) result.value = option.value; - if ('options' in option) result.options = option.options.map(opt => this.transformOption(opt, resolved)); - - if (resolved) { - const user = resolved.users?.[option.value]; - if (user) result.user = this.client.users._add(user); - - const member = resolved.members?.[option.value]; - if (member) result.member = this.guild?.members._add({ user, ...member }) ?? member; - - const channel = resolved.channels?.[option.value]; - if (channel) { - result.channel = this.client.channels._add(channel, this.guild, { fromInteraction: true }) ?? channel; - } - - const role = resolved.roles?.[option.value]; - if (role) result.role = this.guild?.roles._add(role) ?? role; - } - - return result; - } - - // These are here only for documentation purposes - they are implemented by InteractionResponses - /* eslint-disable no-empty-function */ - deferReply() {} - reply() {} - fetchReply() {} - editReply() {} - deleteReply() {} - followUp() {} } -InteractionResponses.applyToClass(CommandInteraction, ['deferUpdate', 'update']); - module.exports = CommandInteraction; - -/* eslint-disable max-len */ -/** - * @external APIApplicationCommandOptionResolved - * @see {@link https://discord.com/developers/docs/interactions/slash-commands#interaction-applicationcommandinteractiondataresolved} - */ - -/** - * @external APIInteractionDataResolvedOption - * @see {@link https://discord.com/developers/docs/interactions/slash-commands#sample-application-command-interaction-application-command-interaction-data-resolved-structure} - */ diff --git a/src/structures/CommandInteractionOptionResolver.js b/src/structures/CommandInteractionOptionResolver.js index b97a1af19..011f1cbd2 100644 --- a/src/structures/CommandInteractionOptionResolver.js +++ b/src/structures/CommandInteractionOptionResolver.js @@ -134,7 +134,7 @@ class CommandInteractionOptionResolver { * Gets a channel option. * @param {string} name The name of the option. * @param {boolean} [required=false] Whether to throw an error if the option is not found. - * @returns {?(GuildChannel|APIInteractionDataResolvedOption)} + * @returns {?(GuildChannel|APIGuildChannel)} * The value of the option, or null if not set and not required. */ getChannel(name, required = false) { @@ -190,7 +190,7 @@ class CommandInteractionOptionResolver { * Gets a member option. * @param {string} name The name of the option. * @param {boolean} [required=false] Whether to throw an error if the option is not found. - * @returns {?(GuildMember|APIInteractionDataResolvedOption)} + * @returns {?(GuildMember|APIGuildMember)} * The value of the option, or null if not set and not required. */ getMember(name, required = false) { @@ -213,13 +213,25 @@ class CommandInteractionOptionResolver { * Gets a mentionable option. * @param {string} name The name of the option. * @param {boolean} [required=false] Whether to throw an error if the option is not found. - * @returns {?(User|GuildMember|APIInteractionDataResolvedOption|Role|APIRole)} + * @returns {?(User|GuildMember|APIGuildMember|Role|APIRole)} * The value of the option, or null if not set and not required. */ getMentionable(name, required = false) { const option = this._getTypedOption(name, 'MENTIONABLE', ['user', 'member', 'role'], required); return option?.member ?? option?.user ?? option?.role ?? null; } + + /** + * Gets a message option. + * @param {string} name The name of the option. + * @param {boolean} [required=false] Whether to throw an error if the option is not found. + * @returns {?(Message|APIMessage)} + * The value of the option, or null if not set and not required. + */ + getMessage(name, required = false) { + const option = this._getTypedOption(name, '_MESSAGE', ['message'], required); + return option?.message ?? null; + } } module.exports = CommandInteractionOptionResolver; diff --git a/src/structures/ContextMenuInteraction.js b/src/structures/ContextMenuInteraction.js new file mode 100644 index 000000000..c69d775f3 --- /dev/null +++ b/src/structures/ContextMenuInteraction.js @@ -0,0 +1,61 @@ +'use strict'; + +const BaseCommandInteraction = require('./BaseCommandInteraction'); +const CommandInteractionOptionResolver = require('./CommandInteractionOptionResolver'); +const { ApplicationCommandOptionTypes, ApplicationCommandTypes } = require('../util/Constants'); + +/** + * Represents a context menu interaction. + * @extends {BaseCommandInteraction} + */ +class ContextMenuInteraction extends BaseCommandInteraction { + constructor(client, data) { + super(client, data); + /** + * The target of the interaction, parsed into options + * @type {CommandInteractionOptionResolver} + */ + this.options = new CommandInteractionOptionResolver(this.client, this.resolveContextMenuOptions(data.data)); + + /** + * The id of the target of the interaction + * @type {Snowflake} + */ + this.targetId = data.data.target_id; + + /** + * The type of the target of the interaction; either USER or MESSAGE + * @type {ApplicationCommandType} + */ + this.targetType = ApplicationCommandTypes[data.data.type]; + } + + /** + * Resolves and transforms options received from the API for a context menu interaction. + * @param {APIApplicationCommandInteractionData} data The interaction data + * @returns {CommandInteractionOption[]} + * @private + */ + resolveContextMenuOptions({ target_id, resolved }) { + const result = []; + + if (resolved.users?.[target_id]) { + result.push( + this.transformOption({ name: 'user', type: ApplicationCommandOptionTypes.USER, value: target_id }, resolved), + ); + } + + if (resolved.messages?.[target_id]) { + result.push({ + name: 'message', + type: '_MESSAGE', + value: target_id, + message: this.channel?.messages._add(resolved.messages[target_id]), + }); + } + + return result; + } +} + +module.exports = ContextMenuInteraction; diff --git a/src/structures/Interaction.js b/src/structures/Interaction.js index 35e97ffa2..369c352e3 100644 --- a/src/structures/Interaction.js +++ b/src/structures/Interaction.js @@ -118,7 +118,15 @@ class Interaction extends Base { * @returns {boolean} */ isCommand() { - return InteractionTypes[this.type] === InteractionTypes.APPLICATION_COMMAND; + return InteractionTypes[this.type] === InteractionTypes.APPLICATION_COMMAND && typeof this.targetId === 'undefined'; + } + + /** + * Indicates whether this interaction is a {@link ContextMenuInteraction} + * @returns {boolean} + */ + isContextMenu() { + return InteractionTypes[this.type] === InteractionTypes.APPLICATION_COMMAND && typeof this.targetId !== 'undefined'; } /** diff --git a/src/util/Constants.js b/src/util/Constants.js index f115acb3d..ec3b569c2 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -887,6 +887,15 @@ exports.StickerFormatTypes = createEnum([null, 'PNG', 'APNG', 'LOTTIE']); exports.OverwriteTypes = createEnum(['role', 'member']); /* eslint-disable max-len */ +/** + * The type of an {@link ApplicationCommand} object: + * * CHAT_INPUT + * * USER + * * MESSAGE + * @typedef {string} ApplicationCommandType + */ +exports.ApplicationCommandTypes = createEnum([null, 'CHAT_INPUT', 'USER', 'MESSAGE']); + /** * The type of an {@link ApplicationCommandOption} object: * * SUB_COMMAND diff --git a/typings/enums.d.ts b/typings/enums.d.ts index 58938da25..32a4c43fa 100644 --- a/typings/enums.d.ts +++ b/typings/enums.d.ts @@ -10,6 +10,12 @@ export enum ActivityTypes { COMPETING = 5, } +export enum ApplicationCommandTypes { + CHAT_INPUT = 1, + USER = 2, + MESSAGE = 3, +} + export enum ApplicationCommandOptionTypes { SUB_COMMAND = 1, SUB_COMMAND_GROUP = 2, diff --git a/typings/index.d.ts b/typings/index.d.ts index 433c7cf66..0c051bac9 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -21,6 +21,7 @@ import { APIApplicationCommand, APIApplicationCommandInteractionData, APIApplicationCommandInteractionDataOption, + APIApplicationCommandOption, APIApplicationCommandPermission, APIAuditLogChange, APIEmoji, @@ -48,6 +49,7 @@ import { ActivityTypes, ApplicationCommandOptionTypes, ApplicationCommandPermissionTypes, + ApplicationCommandTypes, ChannelTypes, DefaultMessageNotificationLevels, ExplicitContentFilterLevels, @@ -209,6 +211,7 @@ export class ApplicationCommand extends Base { Guild | null, Snowflake >; + public type: ApplicationCommandType; public delete(): Promise>; public edit(data: ApplicationCommandData): Promise>; private static transformOption(option: ApplicationCommandOptionData, received?: boolean): unknown; @@ -240,6 +243,30 @@ export class BaseClient extends EventEmitter { public toJSON(...props: Record[]): unknown; } +export abstract class BaseCommandInteraction extends Interaction { + public readonly command: ApplicationCommand | ApplicationCommand<{ guild: GuildResolvable }> | null; + public readonly channel: TextBasedChannels | null; + public channelId: Snowflake; + public commandId: Snowflake; + public commandName: string; + public deferred: boolean; + public ephemeral: boolean | null; + public replied: boolean; + public webhook: InteractionWebhook; + public deferReply(options: InteractionDeferReplyOptions & { fetchReply: true }): Promise; + public deferReply(options?: InteractionDeferReplyOptions): Promise; + public deleteReply(): Promise; + public editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise; + public fetchReply(): Promise; + public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise; + public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise; + public reply(options: string | MessagePayload | InteractionReplyOptions): Promise; + private transformOption( + option: APIApplicationCommandOption, + resolved: APIApplicationCommandInteractionData['resolved'], + ): CommandInteractionOption; +} + export abstract class BaseGuild extends Base { public constructor(client: Client, data: RawBaseGuildData); public readonly createdAt: Date; @@ -494,30 +521,8 @@ export abstract class Collector extends EventEmi public once(event: 'end', listener: (collected: Collection, reason: string) => Awaited): this; } -export class CommandInteraction extends Interaction { - public constructor(client: Client, data: RawCommandInteractionData); - public readonly command: ApplicationCommand | ApplicationCommand<{ guild: GuildResolvable }> | null; - public readonly channel: TextBasedChannels | null; - public channelId: Snowflake; - public commandId: Snowflake; - public commandName: string; - public deferred: boolean; - public ephemeral: boolean | null; +export class CommandInteraction extends BaseCommandInteraction { public options: CommandInteractionOptionResolver; - public replied: boolean; - public webhook: InteractionWebhook; - public deferReply(options: InteractionDeferReplyOptions & { fetchReply: true }): Promise; - public deferReply(options?: InteractionDeferReplyOptions): Promise; - public deleteReply(): Promise; - public editReply(options: string | MessagePayload | WebhookEditMessageOptions): Promise; - public fetchReply(): Promise; - public followUp(options: string | MessagePayload | InteractionReplyOptions): Promise; - public reply(options: InteractionReplyOptions & { fetchReply: true }): Promise; - public reply(options: string | MessagePayload | InteractionReplyOptions): Promise; - private transformOption( - option: APIApplicationCommandInteractionDataOption, - resolved: APIApplicationCommandInteractionData['resolved'], - ): CommandInteractionOption; } export class CommandInteractionOptionResolver { @@ -571,6 +576,15 @@ export class CommandInteractionOptionResolver { name: string, required?: boolean, ): NonNullable | null; + public getMessage(name: string, required: true): NonNullable; + public getMessage(name: string, required?: boolean): NonNullable | null; +} + +export class ContextMenuInteraction extends BaseCommandInteraction { + public options: CommandInteractionOptionResolver; + public targetId: Snowflake; + public targetType: Exclude; + private resolveContextMenuOptions(data: APIApplicationCommandInteractionData): CommandInteractionOption[]; } export class DataResolver extends null { @@ -976,6 +990,7 @@ export class Interaction extends Base { public inGuild(): this is this & { guildId: Snowflake; member: GuildMember | APIInteractionGuildMember }; public isButton(): this is ButtonInteraction; public isCommand(): this is CommandInteraction; + public isContextMenu(): this is ContextMenuInteraction; public isMessageComponent(): this is MessageComponentInteraction; public isSelectMenu(): this is SelectMenuInteraction; } @@ -2256,11 +2271,11 @@ export abstract class CachedManager extends DataManager, + ApplicationCommandScope = ApplicationCommand<{ guild: GuildResolvable }>, PermissionsOptionsExtras = { guild: GuildResolvable }, PermissionsGuildType = null, -> extends CachedManager { - public constructor(client: Client, iterable?: Iterable); +> extends CachedManager { + public constructor(client: Client, iterable?: Iterable); public permissions: ApplicationCommandPermissionsManager< { command?: ApplicationCommandResolvable } & PermissionsOptionsExtras, { command: ApplicationCommandResolvable } & PermissionsOptionsExtras, @@ -2269,10 +2284,10 @@ export class ApplicationCommandManager< null >; private commandPath({ id, guildId }: { id?: Snowflake; guildId?: Snowflake }): unknown; - public create(command: ApplicationCommandData): Promise; + public create(command: ApplicationCommandData): Promise; public create(command: ApplicationCommandData, guildId: Snowflake): Promise; - public delete(command: ApplicationCommandResolvable, guildId?: Snowflake): Promise; - public edit(command: ApplicationCommandResolvable, data: ApplicationCommandData): Promise; + public delete(command: ApplicationCommandResolvable, guildId?: Snowflake): Promise; + public edit(command: ApplicationCommandResolvable, data: ApplicationCommandData): Promise; public edit( command: ApplicationCommandResolvable, data: ApplicationCommandData, @@ -2282,12 +2297,12 @@ export class ApplicationCommandManager< id: Snowflake, options: FetchApplicationCommandOptions & { guildId: Snowflake }, ): Promise; - public fetch(id: Snowflake, options?: FetchApplicationCommandOptions): Promise; + public fetch(id: Snowflake, options?: FetchApplicationCommandOptions): Promise; public fetch( id?: Snowflake, options?: FetchApplicationCommandOptions, - ): Promise>; - public set(commands: ApplicationCommandData[]): Promise>; + ): Promise>; + public set(commands: ApplicationCommandData[]): Promise>; public set( commands: ApplicationCommandData[], guildId: Snowflake, @@ -2868,7 +2883,8 @@ export interface ApplicationAsset { export interface ApplicationCommandData { name: string; - description: string; + description?: string; + type?: ApplicationCommandType | ApplicationCommandTypes; options?: ApplicationCommandOptionData[]; defaultPermission?: boolean; } @@ -2892,6 +2908,8 @@ export interface ApplicationCommandOptionChoice { value: string | number; } +export type ApplicationCommandType = keyof typeof ApplicationCommandTypes; + export type ApplicationCommandOptionType = keyof typeof ApplicationCommandOptionTypes; export interface ApplicationCommandPermissionData { @@ -3234,6 +3252,7 @@ export interface CommandInteractionOption { member?: GuildMember | APIInteractionDataResolvedGuildMember; channel?: GuildChannel | APIInteractionDataResolvedChannel; role?: Role | APIRole; + message?: Message | APIMessage; } export interface ConstantsClientApplicationAssetTypes { diff --git a/typings/tests.ts b/typings/tests.ts index 4c9f6c73a..663b6c621 100644 --- a/typings/tests.ts +++ b/typings/tests.ts @@ -610,17 +610,17 @@ declare const applicationCommandData: ApplicationCommandData; declare const applicationCommandResolvable: ApplicationCommandResolvable; declare const applicationCommandManager: ApplicationCommandManager; { - type ApplicationCommandType = ApplicationCommand<{ guild: GuildResolvable }>; + type ApplicationCommandScope = ApplicationCommand<{ guild: GuildResolvable }>; - assertType>(applicationCommandManager.create(applicationCommandData)); + assertType>(applicationCommandManager.create(applicationCommandData)); assertType>(applicationCommandManager.create(applicationCommandData, '0')); - assertType>( + assertType>( applicationCommandManager.edit(applicationCommandResolvable, applicationCommandData), ); assertType>( applicationCommandManager.edit(applicationCommandResolvable, applicationCommandData, '0'), ); - assertType>>( + assertType>>( applicationCommandManager.set([applicationCommandData]), ); assertType>>(