From a1a3a95c94194a8ab789d567a778b376e13ea973 Mon Sep 17 00:00:00 2001 From: LeCarbonator <18158911+LeCarbonator@users.noreply.github.com> Date: Sun, 24 Mar 2024 19:12:05 +0100 Subject: [PATCH] feat: allow RestOrArray for command option builders (#10175) * feat(builders): allow RestOrArray for command option builders change ApplicationCommandOption methods to allow both rest and array params, which previously wasn't consistent with other builders. * chore: merge imports --------- Co-authored-by: almeidx Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../SlashCommands/SlashCommands.test.ts | 17 ++++++++------- ...plicationCommandOptionChannelTypesMixin.ts | 5 +++-- ...pplicationCommandOptionWithChoicesMixin.ts | 21 +++++++++++-------- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/packages/builders/__tests__/interactions/SlashCommands/SlashCommands.test.ts b/packages/builders/__tests__/interactions/SlashCommands/SlashCommands.test.ts index f58159632..88a33ef94 100644 --- a/packages/builders/__tests__/interactions/SlashCommands/SlashCommands.test.ts +++ b/packages/builders/__tests__/interactions/SlashCommands/SlashCommands.test.ts @@ -144,23 +144,22 @@ describe('Slash Commands', () => { integer .setName('iscool') .setDescription('Are we cool or what?') - .addChoices({ name: 'Very cool', value: 1_000 }), + .addChoices({ name: 'Very cool', value: 1_000 }) + .addChoices([{ name: 'Even cooler', value: 2_000 }]), ) .addNumberOption((number) => number .setName('iscool') .setDescription('Are we cool or what?') - .addChoices({ name: 'Very cool', value: 1.5 }), + .addChoices({ name: 'Very cool', value: 1.5 }) + .addChoices([{ name: 'Even cooler', value: 2.5 }]), ) .addStringOption((string) => string .setName('iscool') .setDescription('Are we cool or what?') - .addChoices( - { name: 'Fancy Pants', value: 'fp_1' }, - { name: 'Fancy Shoes', value: 'fs_1' }, - { name: 'The Whole shebang', value: 'all' }, - ), + .addChoices({ name: 'Fancy Pants', value: 'fp_1' }, { name: 'Fancy Shoes', value: 'fs_1' }) + .addChoices([{ name: 'The Whole shebang', value: 'all' }]), ) .addIntegerOption((integer) => integer.setName('iscool').setDescription('Are we cool or what?').setAutocomplete(true), @@ -229,7 +228,9 @@ describe('Slash Commands', () => { test('GIVEN a builder with valid channel options and channel_types THEN does not throw an error', () => { expect(() => - getBuilder().addChannelOption(getChannelOption().addChannelTypes(ChannelType.GuildText)), + getBuilder().addChannelOption( + getChannelOption().addChannelTypes(ChannelType.GuildText).addChannelTypes([ChannelType.GuildVoice]), + ), ).not.toThrowError(); expect(() => { diff --git a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts index 4d112fb71..83cb16b2b 100644 --- a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts +++ b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts @@ -1,5 +1,6 @@ import { s } from '@sapphire/shapeshift'; import { ChannelType } from 'discord-api-types/v10'; +import { normalizeArray, type RestOrArray } from '../../../util/normalizeArray'; /** * The allowed channel types used for a channel option in a slash command builder. @@ -41,12 +42,12 @@ export class ApplicationCommandOptionChannelTypesMixin { * * @param channelTypes - The channel types */ - public addChannelTypes(...channelTypes: ApplicationCommandOptionAllowedChannelTypes[]) { + public addChannelTypes(...channelTypes: RestOrArray) { if (this.channel_types === undefined) { Reflect.set(this, 'channel_types', []); } - this.channel_types!.push(...channelTypesPredicate.parse(channelTypes)); + this.channel_types!.push(...channelTypesPredicate.parse(normalizeArray(channelTypes))); return this; } diff --git a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesMixin.ts b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesMixin.ts index 05480ed22..6bc75d24a 100644 --- a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesMixin.ts +++ b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesMixin.ts @@ -1,5 +1,6 @@ import { s } from '@sapphire/shapeshift'; import { ApplicationCommandOptionType, type APIApplicationCommandOptionChoice } from 'discord-api-types/v10'; +import { normalizeArray, type RestOrArray } from '../../../util/normalizeArray.js'; import { localizationMapPredicate, validateChoicesLength } from '../Assertions.js'; const stringPredicate = s.string.lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100); @@ -31,20 +32,21 @@ export class ApplicationCommandOptionWithChoicesMixin[]): this { - if (choices.length > 0 && 'autocomplete' in this && this.autocomplete) { + public addChoices(...choices: RestOrArray>): this { + const normalizedChoices = normalizeArray(choices); + if (normalizedChoices.length > 0 && 'autocomplete' in this && this.autocomplete) { throw new RangeError('Autocomplete and choices are mutually exclusive to each other.'); } - choicesPredicate.parse(choices); + choicesPredicate.parse(normalizedChoices); if (this.choices === undefined) { Reflect.set(this, 'choices', []); } - validateChoicesLength(choices.length, this.choices); + validateChoicesLength(normalizedChoices.length, this.choices); - for (const { name, name_localizations, value } of choices) { + for (const { name, name_localizations, value } of normalizedChoices) { // Validate the value if (this.type === ApplicationCommandOptionType.String) { stringPredicate.parse(value); @@ -63,15 +65,16 @@ export class ApplicationCommandOptionWithChoicesMixin[]>(...choices: Input): this { - if (choices.length > 0 && 'autocomplete' in this && this.autocomplete) { + public setChoices>(...choices: RestOrArray): this { + const normalizedChoices = normalizeArray(choices); + if (normalizedChoices.length > 0 && 'autocomplete' in this && this.autocomplete) { throw new RangeError('Autocomplete and choices are mutually exclusive to each other.'); } - choicesPredicate.parse(choices); + choicesPredicate.parse(normalizedChoices); Reflect.set(this, 'choices', []); - this.addChoices(...choices); + this.addChoices(normalizedChoices); return this; }