diff --git a/packages/discord.js/.eslintrc.json b/packages/discord.js/.eslintrc.json index fc997967debf..b7c7dd186c5f 100644 --- a/packages/discord.js/.eslintrc.json +++ b/packages/discord.js/.eslintrc.json @@ -110,7 +110,7 @@ "key-spacing": "error", "keyword-spacing": "error", "max-depth": "error", - "max-len": ["error", 120, 2], + "max-len": ["error", 120, 2, { "ignorePattern": "^exports\\.\\w+\\s=\\srequire" }], "max-nested-callbacks": ["error", { "max": 4 }], "max-statements-per-line": ["error", { "max": 2 }], "new-cap": "off", diff --git a/packages/discord.js/src/index.js b/packages/discord.js/src/index.js index 9ec254f017f3..e00fecc47159 100644 --- a/packages/discord.js/src/index.js +++ b/packages/discord.js/src/index.js @@ -86,6 +86,7 @@ exports.AnonymousGuild = require('./structures/AnonymousGuild'); exports.Application = require('./structures/interfaces/Application'); exports.ApplicationCommand = require('./structures/ApplicationCommand'); exports.AutocompleteInteraction = require('./structures/AutocompleteInteraction'); +exports.AutocompleteInteractionOptionResolver = require('./structures/AutocompleteInteractionOptionResolver'); exports.Base = require('./structures/Base'); exports.BaseGuild = require('./structures/BaseGuild'); exports.BaseGuildEmoji = require('./structures/BaseGuildEmoji'); @@ -97,14 +98,15 @@ exports.ButtonInteraction = require('./structures/ButtonInteraction'); exports.CategoryChannel = require('./structures/CategoryChannel'); exports.BaseChannel = require('./structures/BaseChannel').BaseChannel; exports.ChatInputCommandInteraction = require('./structures/ChatInputCommandInteraction'); +exports.ChatInputCommandInteractionOptionResolver = require('./structures/ChatInputCommandInteractionOptionResolver'); exports.ClientApplication = require('./structures/ClientApplication'); exports.ClientPresence = require('./structures/ClientPresence'); exports.ClientUser = require('./structures/ClientUser'); exports.CommandInteraction = require('./structures/CommandInteraction'); exports.Collector = require('./structures/interfaces/Collector'); -exports.CommandInteractionOptionResolver = require('./structures/CommandInteractionOptionResolver'); exports.Component = require('./structures/Component'); exports.ContextMenuCommandInteraction = require('./structures/ContextMenuCommandInteraction'); +exports.ContextMenuCommandInteractionOptionResolver = require('./structures/ContextMenuCommandInteractionOptionResolver'); exports.DMChannel = require('./structures/DMChannel'); exports.Embed = require('./structures/Embed'); exports.EmbedBuilder = require('./structures/EmbedBuilder'); @@ -124,6 +126,7 @@ exports.Integration = require('./structures/Integration'); exports.IntegrationApplication = require('./structures/IntegrationApplication'); exports.BaseInteraction = require('./structures/BaseInteraction'); exports.InteractionCollector = require('./structures/InteractionCollector'); +exports.InteractionOptionResolver = require('./structures/InteractionOptionResolver'); exports.InteractionResponse = require('./structures/InteractionResponse'); exports.InteractionWebhook = require('./structures/InteractionWebhook'); exports.Invite = require('./structures/Invite'); diff --git a/packages/discord.js/src/structures/AutocompleteInteraction.js b/packages/discord.js/src/structures/AutocompleteInteraction.js index e5674367d5d7..4a9fa4324bda 100644 --- a/packages/discord.js/src/structures/AutocompleteInteraction.js +++ b/packages/discord.js/src/structures/AutocompleteInteraction.js @@ -1,8 +1,8 @@ 'use strict'; const { InteractionResponseType, Routes } = require('discord-api-types/v10'); +const AutocompleteInteractionOptionResolver = require('./AutocompleteInteractionOptionResolver'); const BaseInteraction = require('./BaseInteraction'); -const CommandInteractionOptionResolver = require('./CommandInteractionOptionResolver'); const { ErrorCodes } = require('../errors'); /** @@ -53,7 +53,7 @@ class AutocompleteInteraction extends BaseInteraction { * The options passed to the command * @type {CommandInteractionOptionResolver} */ - this.options = new CommandInteractionOptionResolver(this.client, data.data.options ?? []); + this.options = new AutocompleteInteractionOptionResolver(this.client, data.data.options ?? []); } /** diff --git a/packages/discord.js/src/structures/AutocompleteInteractionOptionResolver.js b/packages/discord.js/src/structures/AutocompleteInteractionOptionResolver.js new file mode 100644 index 000000000000..53ee07dddce2 --- /dev/null +++ b/packages/discord.js/src/structures/AutocompleteInteractionOptionResolver.js @@ -0,0 +1,102 @@ +'use strict'; + +const { ApplicationCommandOptionType } = require('discord-api-types/v10'); +const InteractionOptionResolver = require('./InteractionOptionResolver'); +const { ErrorCodes } = require('../errors'); + +/** + * A resolver for autocomplete interaction options. + * @extends {InteractionOptionResolver} + */ +class AutocompleteInteractionOptionResolver extends InteractionOptionResolver { + /** + * Gets the selected subcommand. + * @param {boolean} [required=true] Whether to throw an error if there is no subcommand. + * @returns {?string} The name of the selected subcommand, or null if not set and not required. + */ + getSubcommand(required = true) { + if (required && !this._subcommand) { + throw new TypeError(ErrorCodes.CommandInteractionOptionNoSubcommand); + } + return this._subcommand; + } + + /** + * Gets the selected subcommand group. + * @param {boolean} [required=false] Whether to throw an error if there is no subcommand group. + * @returns {?string} The name of the selected subcommand group, or null if not set and not required. + */ + getSubcommandGroup(required = false) { + if (required && !this._group) { + throw new TypeError(ErrorCodes.CommandInteractionOptionNoSubcommandGroup); + } + return this._group; + } + + /** + * Gets a boolean 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 {?boolean} The value of the option, or null if not set and not required. + */ + getBoolean(name, required = false) { + const option = this._getTypedOption(name, ApplicationCommandOptionType.Boolean, ['value'], required); + return option?.value ?? null; + } + + /** + * Gets a string 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 {?string} The value of the option, or null if not set and not required. + */ + getString(name, required = false) { + const option = this._getTypedOption(name, ApplicationCommandOptionType.String, ['value'], required); + return option?.value ?? null; + } + + /** + * Gets an integer 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 {?number} The value of the option, or null if not set and not required. + */ + getInteger(name, required = false) { + const option = this._getTypedOption(name, ApplicationCommandOptionType.Integer, ['value'], required); + return option?.value ?? null; + } + + /** + * Gets a number 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 {?number} The value of the option, or null if not set and not required. + */ + getNumber(name, required = false) { + const option = this._getTypedOption(name, ApplicationCommandOptionType.Number, ['value'], required); + return option?.value ?? null; + } + + /** + * The full autocomplete option object. + * @typedef {Object} AutocompleteFocusedOption + * @property {string} name The name of the option + * @property {ApplicationCommandOptionType} type The type of the application command option + * @property {string} value The value of the option + * @property {boolean} focused Whether this option is currently in focus for autocomplete + */ + + /** + * Gets the focused option. + * @param {boolean} [getFull=false] Whether to get the full option object + * @returns {string|AutocompleteFocusedOption} + * The value of the option, or the whole option if getFull is true + */ + getFocused(getFull = false) { + const focusedOption = this._hoistedOptions.find(option => option.focused); + if (!focusedOption) throw new TypeError(ErrorCodes.AutocompleteInteractionOptionNoFocusedOption); + return getFull ? focusedOption : focusedOption.value; + } +} + +module.exports = AutocompleteInteractionOptionResolver; diff --git a/packages/discord.js/src/structures/ChatInputCommandInteraction.js b/packages/discord.js/src/structures/ChatInputCommandInteraction.js index 35175e49c470..725da856f21a 100644 --- a/packages/discord.js/src/structures/ChatInputCommandInteraction.js +++ b/packages/discord.js/src/structures/ChatInputCommandInteraction.js @@ -1,7 +1,7 @@ 'use strict'; +const ChatInputCommandInteractionOptionResolver = require('./ChatInputCommandInteractionOptionResolver'); const CommandInteraction = require('./CommandInteraction'); -const CommandInteractionOptionResolver = require('./CommandInteractionOptionResolver'); /** * Represents a command interaction. @@ -15,7 +15,7 @@ class ChatInputCommandInteraction extends CommandInteraction { * The options passed to the command. * @type {CommandInteractionOptionResolver} */ - this.options = new CommandInteractionOptionResolver( + this.options = new ChatInputCommandInteractionOptionResolver( this.client, data.data.options?.map(option => this.transformOption(option, data.data.resolved)) ?? [], this.transformResolved(data.data.resolved ?? {}), diff --git a/packages/discord.js/src/structures/CommandInteractionOptionResolver.js b/packages/discord.js/src/structures/ChatInputCommandInteractionOptionResolver.js similarity index 62% rename from packages/discord.js/src/structures/CommandInteractionOptionResolver.js rename to packages/discord.js/src/structures/ChatInputCommandInteractionOptionResolver.js index 3fe08bbf14d3..f47f72c9b152 100644 --- a/packages/discord.js/src/structures/CommandInteractionOptionResolver.js +++ b/packages/discord.js/src/structures/ChatInputCommandInteractionOptionResolver.js @@ -1,21 +1,16 @@ 'use strict'; const { ApplicationCommandOptionType } = require('discord-api-types/v10'); +const InteractionOptionResolver = require('./InteractionOptionResolver'); const { TypeError, ErrorCodes } = require('../errors'); /** - * A resolver for command interaction options. + * A resolver for chat input interaction options. + * @extends {InteractionOptionResolver} */ -class CommandInteractionOptionResolver { +class ChatInputCommandInteractionOptionResolver extends InteractionOptionResolver { constructor(client, options, resolved) { - /** - * The client that instantiated this. - * @name CommandInteractionOptionResolver#client - * @type {Client} - * @readonly - */ - Object.defineProperty(this, 'client', { value: client }); - + super(client, options, resolved); /** * The name of the subcommand group. * @type {?string} @@ -30,14 +25,6 @@ class CommandInteractionOptionResolver { */ this._subcommand = null; - /** - * The bottom-level options for the interaction. - * If there is a subcommand (or subcommand and group), this is the options for the subcommand. - * @type {CommandInteractionOption[]} - * @private - */ - this._hoistedOptions = options; - // Hoist subcommand group if present if (this._hoistedOptions[0]?.type === ApplicationCommandOptionType.SubcommandGroup) { this._group = this._hoistedOptions[0].name; @@ -48,59 +35,6 @@ class CommandInteractionOptionResolver { this._subcommand = this._hoistedOptions[0].name; this._hoistedOptions = this._hoistedOptions[0].options ?? []; } - - /** - * The interaction options array. - * @name CommandInteractionOptionResolver#data - * @type {ReadonlyArray} - * @readonly - */ - Object.defineProperty(this, 'data', { value: Object.freeze([...options]) }); - - /** - * The interaction resolved data - * @name CommandInteractionOptionResolver#resolved - * @type {?Readonly} - */ - Object.defineProperty(this, 'resolved', { value: resolved ? Object.freeze(resolved) : null }); - } - - /** - * Gets an option by its name. - * @param {string} name The name of the option. - * @param {boolean} [required=false] Whether to throw an error if the option is not found. - * @returns {?CommandInteractionOption} The option, if found. - */ - get(name, required = false) { - const option = this._hoistedOptions.find(opt => opt.name === name); - if (!option) { - if (required) { - throw new TypeError(ErrorCodes.CommandInteractionOptionNotFound, name); - } - return null; - } - return option; - } - - /** - * Gets an option by name and property and checks its type. - * @param {string} name The name of the option. - * @param {ApplicationCommandOptionType} type The type of the option. - * @param {string[]} properties The properties to check for for `required`. - * @param {boolean} required Whether to throw an error if the option is not found. - * @returns {?CommandInteractionOption} The option, if found. - * @private - */ - _getTypedOption(name, type, properties, required) { - const option = this.get(name, required); - if (!option) { - return null; - } else if (option.type !== type) { - throw new TypeError(ErrorCodes.CommandInteractionOptionType, name, option.type, type); - } else if (required && properties.every(prop => option[prop] === null || typeof option[prop] === 'undefined')) { - throw new TypeError(ErrorCodes.CommandInteractionOptionEmpty, name, option.type); - } - return option; } /** @@ -243,39 +177,6 @@ class CommandInteractionOptionResolver { ); 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} - * 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; - } - - /** - * The full autocomplete option object. - * @typedef {Object} AutocompleteFocusedOption - * @property {string} name The name of the option - * @property {ApplicationCommandOptionType} type The type of the application command option - * @property {string} value The value of the option - * @property {boolean} focused Whether this option is currently in focus for autocomplete - */ - - /** - * Gets the focused option. - * @param {boolean} [getFull=false] Whether to get the full option object - * @returns {string|AutocompleteFocusedOption} - * The value of the option, or the whole option if getFull is true - */ - getFocused(getFull = false) { - const focusedOption = this._hoistedOptions.find(option => option.focused); - if (!focusedOption) throw new TypeError(ErrorCodes.AutocompleteInteractionOptionNoFocusedOption); - return getFull ? focusedOption : focusedOption.value; - } } -module.exports = CommandInteractionOptionResolver; +module.exports = ChatInputCommandInteractionOptionResolver; diff --git a/packages/discord.js/src/structures/ContextMenuCommandInteraction.js b/packages/discord.js/src/structures/ContextMenuCommandInteraction.js index b5e1d56c64c7..ee45d76a133e 100644 --- a/packages/discord.js/src/structures/ContextMenuCommandInteraction.js +++ b/packages/discord.js/src/structures/ContextMenuCommandInteraction.js @@ -2,7 +2,7 @@ const { ApplicationCommandOptionType } = require('discord-api-types/v10'); const CommandInteraction = require('./CommandInteraction'); -const CommandInteractionOptionResolver = require('./CommandInteractionOptionResolver'); +const ContextMenuCommandInteractionOptionResolver = require('./ContextMenuCommandInteractionOptionResolver'); const { lazy } = require('../util/Util'); const getMessage = lazy(() => require('./Message').Message); @@ -18,7 +18,7 @@ class ContextMenuCommandInteraction extends CommandInteraction { * The target of the interaction, parsed into options * @type {CommandInteractionOptionResolver} */ - this.options = new CommandInteractionOptionResolver( + this.options = new ContextMenuCommandInteractionOptionResolver( this.client, this.resolveContextMenuOptions(data.data), this.transformResolved(data.data.resolved), diff --git a/packages/discord.js/src/structures/ContextMenuCommandInteractionOptionResolver.js b/packages/discord.js/src/structures/ContextMenuCommandInteractionOptionResolver.js new file mode 100644 index 000000000000..71a5bc643439 --- /dev/null +++ b/packages/discord.js/src/structures/ContextMenuCommandInteractionOptionResolver.js @@ -0,0 +1,43 @@ +'use strict'; + +const { ApplicationCommandOptionType } = require('discord-api-types/v10'); +const InteractionOptionResolver = require('./InteractionOptionResolver'); + +/** + * A resolver for context menu interaction options. + * @extends {InteractionOptionResolver} + */ +class ContextMenuCommandInteractionOptionResolver extends InteractionOptionResolver { + /** + * Gets a user option. + * @param {boolean} [required=false] Whether to throw an error if the option is not found. + * @returns {?User} The value of the option, or null if not set and not required. + */ + getUser(required = false) { + const option = this._getTypedOption('user', ApplicationCommandOptionType.User, ['user'], required); + return option?.user ?? null; + } + + /** + * Gets a member option. + * @returns {?(GuildMember|APIGuildMember)} + * The value of the option, or null if the user is not present in the guild or the option is not set. + */ + getMember() { + const option = this._getTypedOption('user', ApplicationCommandOptionType.User, ['member'], false); + return option?.member ?? null; + } + + /** + * Gets a message option. + * @param {boolean} [required=false] Whether to throw an error if the option is not found. + * @returns {?Message} + * The value of the option, or null if not set and not required. + */ + getMessage(required = false) { + const option = this._getTypedOption('message', '_MESSAGE', ['message'], required); + return option?.message ?? null; + } +} + +module.exports = ContextMenuCommandInteractionOptionResolver; diff --git a/packages/discord.js/src/structures/InteractionOptionResolver.js b/packages/discord.js/src/structures/InteractionOptionResolver.js new file mode 100644 index 000000000000..be517daae1a0 --- /dev/null +++ b/packages/discord.js/src/structures/InteractionOptionResolver.js @@ -0,0 +1,81 @@ +'use strict'; + +const { TypeError, ErrorCodes } = require('../errors'); + +/** + * A resolver for interaction options. + */ +class InteractionOptionResolver { + constructor(client, options, resolved) { + /** + * The client that instantiated this. + * @name CommandInteractionOptionResolver#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: client }); + + /** + * The bottom-level options for the interaction. + * If there is a subcommand (or subcommand and group), this is the options for the subcommand. + * @type {CommandInteractionOption[]} + * @private + */ + this._hoistedOptions = options; + + /** + * The interaction options array. + * @name CommandInteractionOptionResolver#data + * @type {ReadonlyArray} + * @readonly + */ + Object.defineProperty(this, 'data', { value: Object.freeze([...options]) }); + + /** + * The interaction resolved data + * @name CommandInteractionOptionResolver#resolved + * @type {?Readonly} + */ + Object.defineProperty(this, 'resolved', { value: resolved ? Object.freeze(resolved) : null }); + } + + /** + * Gets an option by its name. + * @param {string} name The name of the option. + * @param {boolean} [required=false] Whether to throw an error if the option is not found. + * @returns {?CommandInteractionOption} The option, if found. + */ + get(name, required = false) { + const option = this._hoistedOptions.find(opt => opt.name === name); + if (!option) { + if (required) { + throw new TypeError(ErrorCodes.CommandInteractionOptionNotFound, name); + } + return null; + } + return option; + } + + /** + * Gets an option by name and property and checks its type. + * @param {string} name The name of the option. + * @param {ApplicationCommandOptionType} type The type of the option. + * @param {string[]} properties The properties to check for for `required`. + * @param {boolean} required Whether to throw an error if the option is not found. + * @returns {?CommandInteractionOption} The option, if found. + * @private + */ + _getTypedOption(name, type, properties, required) { + const option = this.get(name, required); + if (!option) { + return null; + } else if (option.type !== type) { + throw new TypeError(ErrorCodes.CommandInteractionOptionType, name, option.type, type); + } else if (required && properties.every(prop => option[prop] === null || typeof option[prop] === 'undefined')) { + throw new TypeError(ErrorCodes.CommandInteractionOptionEmpty, name, option.type); + } + return option; + } +} + +module.exports = InteractionOptionResolver; diff --git a/packages/discord.js/src/structures/MessageContextMenuCommandInteraction.js b/packages/discord.js/src/structures/MessageContextMenuCommandInteraction.js index 11005914571f..f90a58a9fea1 100644 --- a/packages/discord.js/src/structures/MessageContextMenuCommandInteraction.js +++ b/packages/discord.js/src/structures/MessageContextMenuCommandInteraction.js @@ -13,7 +13,7 @@ class MessageContextMenuCommandInteraction extends ContextMenuCommandInteraction * @readonly */ get targetMessage() { - return this.options.getMessage('message'); + return this.options.getMessage(true); } } diff --git a/packages/discord.js/src/structures/UserContextMenuCommandInteraction.js b/packages/discord.js/src/structures/UserContextMenuCommandInteraction.js index a074de1ad0a8..bd997b1afb4e 100644 --- a/packages/discord.js/src/structures/UserContextMenuCommandInteraction.js +++ b/packages/discord.js/src/structures/UserContextMenuCommandInteraction.js @@ -13,7 +13,7 @@ class UserContextMenuCommandInteraction extends ContextMenuCommandInteraction { * @readonly */ get targetUser() { - return this.options.getUser('user'); + return this.options.getUser(true); } /** @@ -22,7 +22,7 @@ class UserContextMenuCommandInteraction extends ContextMenuCommandInteraction { * @readonly */ get targetMember() { - return this.options.getMember('user'); + return this.options.getMember(); } } diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index 8c98c25e4e68..5b55e415fac9 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -421,21 +421,6 @@ export type BooleanCache = T extends 'cached' ? true : fals export abstract class CommandInteraction extends BaseInteraction { public type: InteractionType.ApplicationCommand; public get command(): ApplicationCommand | ApplicationCommand<{ guild: GuildResolvable }> | null; - public options: Omit< - CommandInteractionOptionResolver, - | 'getMessage' - | 'getFocused' - | 'getMentionable' - | 'getRole' - | 'getAttachment' - | 'getNumber' - | 'getInteger' - | 'getString' - | 'getChannel' - | 'getBoolean' - | 'getSubcommandGroup' - | 'getSubcommand' - >; public channelId: Snowflake; public commandId: Snowflake; public commandName: string; @@ -919,42 +904,11 @@ export abstract class Collector extends EventEmi ): this; } -export class ChatInputCommandInteraction extends CommandInteraction { - public commandType: ApplicationCommandType.ChatInput; - public options: Omit, 'getMessage' | 'getFocused'>; - public inGuild(): this is ChatInputCommandInteraction<'raw' | 'cached'>; - public inCachedGuild(): this is ChatInputCommandInteraction<'cached'>; - public inRawGuild(): this is ChatInputCommandInteraction<'raw'>; - public toString(): string; -} - -export class AutocompleteInteraction extends BaseInteraction { - public type: InteractionType.ApplicationCommandAutocomplete; - public get command(): ApplicationCommand | ApplicationCommand<{ guild: GuildResolvable }> | null; - public channelId: Snowflake; - public commandId: Snowflake; - public commandName: string; - public commandType: ApplicationCommandType.ChatInput; - public commandGuildId: Snowflake | null; - public responded: boolean; - public options: Omit< - CommandInteractionOptionResolver, - 'getMessage' | 'getUser' | 'getAttachment' | 'getChannel' | 'getMember' | 'getMentionable' | 'getRole' - >; - public inGuild(): this is AutocompleteInteraction<'raw' | 'cached'>; - public inCachedGuild(): this is AutocompleteInteraction<'cached'>; - public inRawGuild(): this is AutocompleteInteraction<'raw'>; - public respond(options: ApplicationCommandOptionChoiceData[]): Promise; -} - -export class CommandInteractionOptionResolver { - private constructor(client: Client, options: CommandInteractionOption[], resolved: CommandInteractionResolvedData); +export abstract class InteractionOptionResolver { + public constructor(client: Client, options: CommandInteractionOption[], resolved: CommandInteractionResolvedData); public readonly client: Client; public readonly data: readonly CommandInteractionOption[]; public readonly resolved: Readonly> | null; - private _group: string | null; - private _hoistedOptions: CommandInteractionOption[]; - private _subcommand: string | null; private _getTypedOption( name: string, type: ApplicationCommandOptionType, @@ -970,7 +924,24 @@ export class CommandInteractionOptionResolver; public get(name: string, required?: boolean): CommandInteractionOption | null; +} +export class ChatInputCommandInteraction extends CommandInteraction { + public commandType: ApplicationCommandType.ChatInput; + public options: ChatInputCommandInteractionOptionResolver; + public inGuild(): this is ChatInputCommandInteraction<'raw' | 'cached'>; + public inCachedGuild(): this is ChatInputCommandInteraction<'cached'>; + public inRawGuild(): this is ChatInputCommandInteraction<'raw'>; + public toString(): string; +} + +export class ChatInputCommandInteractionOptionResolver< + Cached extends CacheType = CacheType, +> extends InteractionOptionResolver { + public readonly resolved: Readonly, 'messages'>>; + private _group: string | null; + private _hoistedOptions: CommandInteractionOption[]; + private _subcommand: string | null; public getSubcommand(required?: true): string; public getSubcommand(required: boolean): string | null; public getSubcommandGroup(required: true): string; @@ -1003,27 +974,48 @@ export class CommandInteractionOptionResolver['member' | 'role' | 'user']> | null; - public getMessage(name: string, required: true): NonNullable['message']>; - public getMessage(name: string, required?: boolean): NonNullable['message']> | null; +} + +export class AutocompleteInteraction extends BaseInteraction { + public type: InteractionType.ApplicationCommandAutocomplete; + public get command(): ApplicationCommand | ApplicationCommand<{ guild: GuildResolvable }> | null; + public channelId: Snowflake; + public commandId: Snowflake; + public commandName: string; + public commandType: ApplicationCommandType.ChatInput; + public commandGuildId: Snowflake | null; + public responded: boolean; + public options: AutocompleteInteractionOptionResolver; + public inGuild(): this is AutocompleteInteraction<'raw' | 'cached'>; + public inCachedGuild(): this is AutocompleteInteraction<'cached'>; + public inRawGuild(): this is AutocompleteInteraction<'raw'>; + public respond(options: ApplicationCommandOptionChoiceData[]): Promise; +} + +export class AutocompleteInteractionOptionResolver< + Cached extends CacheType = CacheType, +> extends InteractionOptionResolver { + public readonly resolved: null; + private constructor(client: Client, options: CommandInteractionOption[]); + public getSubcommand(required?: true): string; + public getSubcommand(required: boolean): string | null; + public getSubcommandGroup(required: true): string; + public getSubcommandGroup(required?: boolean): string | null; + public getBoolean(name: string, required: true): boolean; + public getBoolean(name: string, required?: boolean): boolean | null; + public getString(name: string, required: true): string; + public getString(name: string, required?: boolean): string | null; + public getInteger(name: string, required: true): number; + public getInteger(name: string, required?: boolean): number | null; + public getNumber(name: string, required: true): number; + public getNumber(name: string, required?: boolean): number | null; public getFocused(getFull: true): AutocompleteFocusedOption; public getFocused(getFull?: boolean): string; } export class ContextMenuCommandInteraction extends CommandInteraction { public commandType: ApplicationCommandType.Message | ApplicationCommandType.User; - public options: Omit< - CommandInteractionOptionResolver, - | 'getFocused' - | 'getMentionable' - | 'getRole' - | 'getNumber' - | 'getInteger' - | 'getString' - | 'getChannel' - | 'getBoolean' - | 'getSubcommandGroup' - | 'getSubcommand' - >; + public options: ContextMenuCommandInteractionOptionResolver; public targetId: Snowflake; public inGuild(): this is ContextMenuCommandInteraction<'raw' | 'cached'>; public inCachedGuild(): this is ContextMenuCommandInteraction<'cached'>; @@ -1031,6 +1023,19 @@ export class ContextMenuCommandInteraction private resolveContextMenuOptions(data: APIApplicationCommandInteractionData): CommandInteractionOption[]; } +export class ContextMenuCommandInteractionOptionResolver< + Cached extends CacheType = CacheType, +> extends InteractionOptionResolver { + public readonly resolved: Readonly< + Omit, 'roles' | 'channels' | 'attachments'> + >; + public getUser(required: true): NonNullable['user']>; + public getUser(required?: boolean): NonNullable['user']> | null; + public getMember(): NonNullable['member']> | null; + public getMessage(required: true): NonNullable['message']>; + public getMessage(required?: boolean): NonNullable['message']> | null; +} + export interface ResolvedFile { data: Buffer; contentType?: string; diff --git a/packages/discord.js/typings/index.test-d.ts b/packages/discord.js/typings/index.test-d.ts index 7946a360e758..a1496f4fce72 100644 --- a/packages/discord.js/typings/index.test-d.ts +++ b/packages/discord.js/typings/index.test-d.ts @@ -35,7 +35,6 @@ import { ApplicationCommandSubGroupData, CommandInteraction, ButtonInteraction, - CacheType, CategoryChannel, Client, ClientApplication, @@ -44,7 +43,6 @@ import { Collection, ChatInputCommandInteraction, CommandInteractionOption, - CommandInteractionOptionResolver, CommandOptionNonChoiceResolvableType, ContextMenuCommandInteraction, DMChannel, @@ -1445,7 +1443,6 @@ client.on('interactionCreate', async interaction => { } expectType(interaction); - expectType, 'getFocused' | 'getMessage'>>(interaction.options); expectType(interaction.options.data); const optionalOption = interaction.options.get('name');