From 3b14883e349c23a15aec225bb133ce49ed440817 Mon Sep 17 00:00:00 2001 From: ckohen Date: Mon, 27 Sep 2021 04:49:39 -0700 Subject: [PATCH] feat(ApplicationCommand): add support for channel_types (#6640) Co-authored-by: Tiemen Co-authored-by: Sugden <28943913+NotSugden@users.noreply.github.com> --- src/structures/ApplicationCommand.js | 28 +++++++++++++++-- typings/index.d.ts | 45 ++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/src/structures/ApplicationCommand.js b/src/structures/ApplicationCommand.js index 2a90b38b764c..58d0f8c48e67 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, ApplicationCommandTypes } = require('../util/Constants'); +const { ApplicationCommandOptionTypes, ApplicationCommandTypes, ChannelTypes } = require('../util/Constants'); const SnowflakeUtil = require('../util/SnowflakeUtil'); /** @@ -131,6 +131,12 @@ class ApplicationCommand extends Base { * @property {boolean} [required] Whether the option is required * @property {ApplicationCommandOptionChoice[]} [choices] The choices of the option for the user to pick from * @property {ApplicationCommandOptionData[]} [options] Additional options if this option is a subcommand (group) + * @property {ChannelType[]|number[]} [channelTypes] When the option type is channel, + * the allowed types of channels that can be selected + * @property {number[]} [channel_types] When the option type is channel, + * the API data for allowed types of channels that can be selected + * This is provided for compatibility with something like `@discordjs/builders` + * and will be discarded when `channelTypes` is present */ /** @@ -239,7 +245,8 @@ class ApplicationCommand extends Base { (option.required ?? (['SUB_COMMAND', 'SUB_COMMAND_GROUP'].includes(optionType) ? undefined : false)) !== existing.required || option.choices?.length !== existing.choices?.length || - option.options?.length !== existing.options?.length + option.options?.length !== existing.options?.length || + (option.channelTypes ?? option.channel_types)?.length !== existing.channelTypes?.length ) { return false; } @@ -262,6 +269,15 @@ class ApplicationCommand extends Base { } } + if (existing.channelTypes) { + const newTypes = (option.channelTypes ?? option.channel_types).map(type => + typeof type === 'number' ? ChannelTypes[type] : type, + ); + for (const type of existing.channelTypes) { + if (!newTypes.includes(type)) return false; + } + } + if (existing.options) { return this.optionsEqual(existing.options, option.options, enforceOptionOrder); } @@ -277,6 +293,8 @@ class ApplicationCommand extends Base { * @property {boolean} [required] Whether the option is required * @property {ApplicationCommandOptionChoice[]} [choices] The choices of the option for the user to pick from * @property {ApplicationCommandOption[]} [options] Additional options if this option is a subcommand (group) + * @property {ChannelType[]} [channelTypes] When the option type is channel, + * the allowed types of channels that can be selected */ /** @@ -295,6 +313,7 @@ class ApplicationCommand extends Base { */ static transformOption(option, received) { const stringType = typeof option.type === 'string' ? option.type : ApplicationCommandOptionTypes[option.type]; + const channelTypesKey = received ? 'channelTypes' : 'channel_types'; return { type: typeof option.type === 'number' && !received ? option.type : ApplicationCommandOptionTypes[option.type], name: option.name, @@ -303,6 +322,11 @@ class ApplicationCommand extends Base { option.required ?? (stringType === 'SUB_COMMAND' || stringType === 'SUB_COMMAND_GROUP' ? undefined : false), choices: option.choices, options: option.options?.map(o => this.transformOption(o, received)), + [channelTypesKey]: received + ? option.channel_types?.map(type => ChannelTypes[type]) + : option.channelTypes?.map(type => (typeof type === 'string' ? ChannelTypes[type] : type)) ?? + // When transforming to API data, accept API data + option.channel_types, }; } } diff --git a/typings/index.d.ts b/typings/index.d.ts index 00d98fcf1f1e..7bdbbdf53dbc 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -3032,6 +3032,8 @@ export interface BaseApplicationCommandData { export type CommandOptionDataTypeResolvable = ApplicationCommandOptionType | ApplicationCommandOptionTypes; +export type CommandOptionChannelResolvableType = ApplicationCommandOptionTypes.CHANNEL | 'CHANNEL'; + export type CommandOptionChoiceResolvableType = | ApplicationCommandOptionTypes.NUMBER | 'NUMBER' @@ -3048,7 +3050,7 @@ export type CommandOptionSubOptionResolvableType = export type CommandOptionNonChoiceResolvableType = Exclude< CommandOptionDataTypeResolvable, - CommandOptionChoiceResolvableType | CommandOptionSubOptionResolvableType + CommandOptionChoiceResolvableType | CommandOptionSubOptionResolvableType | CommandOptionChannelResolvableType >; export interface BaseApplicationCommandOptionsData { @@ -3076,35 +3078,68 @@ export type ApplicationCommandData = | MessageApplicationCommandData | ChatInputApplicationCommandData; +export interface ApplicationCommandChannelOptionData extends BaseApplicationCommandOptionsData { + type: CommandOptionChannelResolvableType; + channelTypes?: (keyof typeof ChannelTypes | ChannelTypes)[]; + channel_types?: ChannelTypes[]; +} + +export interface ApplicationCommandChannelOption extends BaseApplicationCommandOptionsData { + type: 'CHANNEL'; + channelTypes?: (keyof typeof ChannelTypes)[]; +} + export interface ApplicationCommandChoicesData extends BaseApplicationCommandOptionsData { type: CommandOptionChoiceResolvableType; choices?: ApplicationCommandOptionChoice[]; } +export interface ApplicationCommandChoicesOption extends BaseApplicationCommandOptionsData { + type: Exclude; + choices?: ApplicationCommandOptionChoice[]; +} + export interface ApplicationCommandSubGroupData extends BaseApplicationCommandOptionsData { type: 'SUB_COMMAND_GROUP' | ApplicationCommandOptionTypes.SUB_COMMAND_GROUP; options?: ApplicationCommandSubCommandData[]; } +export interface ApplicationCommandSubGroup extends BaseApplicationCommandOptionsData { + type: 'SUB_COMMAND_GROUP'; + options?: ApplicationCommandSubCommand[]; +} + export interface ApplicationCommandSubCommandData extends BaseApplicationCommandOptionsData { type: 'SUB_COMMAND' | ApplicationCommandOptionTypes.SUB_COMMAND; options?: (ApplicationCommandChoicesData | ApplicationCommandNonOptionsData)[]; } +export interface ApplicationCommandSubCommand extends BaseApplicationCommandOptionsData { + type: 'SUB_COMMAND'; + options?: (ApplicationCommandChoicesOption | ApplicationCommandNonOptions)[]; +} + export interface ApplicationCommandNonOptionsData extends BaseApplicationCommandOptionsData { type: CommandOptionNonChoiceResolvableType; } +export interface ApplicationCommandNonOptions extends BaseApplicationCommandOptionsData { + type: Exclude; +} + export type ApplicationCommandOptionData = | ApplicationCommandSubGroupData | ApplicationCommandNonOptionsData + | ApplicationCommandChannelOptionData | ApplicationCommandChoicesData | ApplicationCommandSubCommandData; -export type ApplicationCommandOption = ApplicationCommandOptionData & { - type: ApplicationCommandOptionType; - options?: ApplicationCommandOption[]; -}; +export type ApplicationCommandOption = + | ApplicationCommandSubGroup + | ApplicationCommandNonOptions + | ApplicationCommandChannelOption + | ApplicationCommandChoicesOption + | ApplicationCommandSubCommand; export interface ApplicationCommandOptionChoice { name: string;