From 10607dbdafe257c5cbf5b952b7eecec4919e8b4a Mon Sep 17 00:00:00 2001 From: Almeida Date: Sun, 6 Mar 2022 15:27:17 +0000 Subject: [PATCH] refactor: remove obsolete builder methods (#7590) --- .../SlashCommands/Options.test.ts | 26 +++++----- .../SlashCommands/SlashCommands.test.ts | 34 +++++-------- .../docs/examples/Slash Command Builders.md | 20 +++----- .../interactions/slashCommands/Assertions.ts | 6 ++- ...plicationCommandOptionChannelTypesMixin.ts | 33 +++++------- ...ndOptionWithChoicesAndAutocompleteMixin.ts | 50 +++++++------------ .../mixins/SharedSlashCommandOptions.ts | 16 +++--- 7 files changed, 76 insertions(+), 109 deletions(-) diff --git a/packages/builders/__tests__/interactions/SlashCommands/Options.test.ts b/packages/builders/__tests__/interactions/SlashCommands/Options.test.ts index 3a8670c6a55f..6f4c8756c2ec 100644 --- a/packages/builders/__tests__/interactions/SlashCommands/Options.test.ts +++ b/packages/builders/__tests__/interactions/SlashCommands/Options.test.ts @@ -29,7 +29,7 @@ const getChannelOption = () => .setName('owo') .setDescription('Testing 123') .setRequired(true) - .addChannelType(ChannelType.GuildText); + .addChannelTypes(ChannelType.GuildText); const getStringOption = () => new SlashCommandStringOption().setName('owo').setDescription('Testing 123').setRequired(true); @@ -100,7 +100,7 @@ describe('Application Command toJSON() results', () => { }); expect( - getIntegerOption().addChoice({ name: 'uwu', value: 1 }).toJSON(), + getIntegerOption().addChoices({ name: 'uwu', value: 1 }).toJSON(), ).toEqual({ name: 'owo', description: 'Testing 123', @@ -143,15 +143,17 @@ describe('Application Command toJSON() results', () => { choices: [], }); - expect(getNumberOption().addChoice({ name: 'uwu', value: 1 }).toJSON()).toEqual({ - name: 'owo', - description: 'Testing 123', - type: ApplicationCommandOptionType.Number, - required: true, - max_value: 10, - min_value: -1.23, - choices: [{ name: 'uwu', value: 1 }], - }); + expect(getNumberOption().addChoices({ name: 'uwu', value: 1 }).toJSON()).toEqual( + { + name: 'owo', + description: 'Testing 123', + type: ApplicationCommandOptionType.Number, + required: true, + max_value: 10, + min_value: -1.23, + choices: [{ name: 'uwu', value: 1 }], + }, + ); }); test('GIVEN a role option THEN calling toJSON should return a valid JSON', () => { @@ -182,7 +184,7 @@ describe('Application Command toJSON() results', () => { }); expect( - getStringOption().addChoice({ name: 'uwu', value: '1' }).toJSON(), + getStringOption().addChoices({ name: 'uwu', value: '1' }).toJSON(), ).toEqual({ name: 'owo', description: 'Testing 123', diff --git a/packages/builders/__tests__/interactions/SlashCommands/SlashCommands.test.ts b/packages/builders/__tests__/interactions/SlashCommands/SlashCommands.test.ts index d0bc768a7d32..d9559417cac6 100644 --- a/packages/builders/__tests__/interactions/SlashCommands/SlashCommands.test.ts +++ b/packages/builders/__tests__/interactions/SlashCommands/SlashCommands.test.ts @@ -87,18 +87,16 @@ describe('Slash Commands', () => { test('GIVEN valid array of options or choices THEN does not throw error', () => { expect(() => SlashCommandAssertions.validateMaxOptionsLength([])).not.toThrowError(); - expect(() => SlashCommandAssertions.validateMaxChoicesLength([])).not.toThrowError(); + expect(() => SlashCommandAssertions.validateChoicesLength(25, [])).not.toThrowError(); }); test('GIVEN invalid options or choices THEN throw error', () => { expect(() => SlashCommandAssertions.validateMaxOptionsLength(null)).toThrowError(); - expect(() => SlashCommandAssertions.validateMaxChoicesLength(null)).toThrowError(); - // Given an array that's too big expect(() => SlashCommandAssertions.validateMaxOptionsLength(largeArray)).toThrowError(); - expect(() => SlashCommandAssertions.validateMaxChoicesLength(largeArray)).toThrowError(); + expect(() => SlashCommandAssertions.validateChoicesLength(1, largeArray)).toThrowError(); }); test('GIVEN valid required parameters THEN does not throw error', () => { @@ -179,31 +177,25 @@ describe('Slash Commands', () => { test('GIVEN a builder with both choices and autocomplete THEN does throw an error', () => { expect(() => getBuilder().addStringOption( - // @ts-expect-error Checking if check works JS-side too - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call - getStringOption().setAutocomplete(true).addChoice('Fancy Pants', 'fp_1'), + getStringOption().setAutocomplete(true).addChoices({ name: 'Fancy Pants', value: 'fp_1' }), ), ).toThrowError(); expect(() => getBuilder().addStringOption( - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call getStringOption() .setAutocomplete(true) - // @ts-expect-error Checking if check works JS-side too - .addChoices([ - ['Fancy Pants', 'fp_1'], - ['Fancy Shoes', 'fs_1'], - ['The Whole shebang', 'all'], - ]), + .addChoices( + { name: 'Fancy Pants', value: 'fp_1' }, + { name: 'Fancy Shoes', value: 'fs_1' }, + { name: 'The Whole shebang', value: 'all' }, + ), ), ).toThrowError(); expect(() => getBuilder().addStringOption( - // @ts-expect-error Checking if check works JS-side too - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call - getStringOption().addChoice('Fancy Pants', 'fp_1').setAutocomplete(true), + getStringOption().addChoices({ name: 'Fancy Pants', value: 'fp_1' }).setAutocomplete(true), ), ).toThrowError(); @@ -231,20 +223,20 @@ describe('Slash Commands', () => { test('GIVEN a builder with valid channel options and channel_types THEN does not throw an error', () => { expect(() => - getBuilder().addChannelOption(getChannelOption().addChannelType(ChannelType.GuildText)), + getBuilder().addChannelOption(getChannelOption().addChannelTypes(ChannelType.GuildText)), ).not.toThrowError(); expect(() => { getBuilder().addChannelOption( - getChannelOption().addChannelTypes([ChannelType.GuildNews, ChannelType.GuildText]), + getChannelOption().addChannelTypes(ChannelType.GuildNews, ChannelType.GuildText), ); }).not.toThrowError(); }); test('GIVEN a builder with valid channel options and channel_types THEN does throw an error', () => { - expect(() => getBuilder().addChannelOption(getChannelOption().addChannelType(100))).toThrowError(); + expect(() => getBuilder().addChannelOption(getChannelOption().addChannelTypes(100))).toThrowError(); - expect(() => getBuilder().addChannelOption(getChannelOption().addChannelTypes([100, 200]))).toThrowError(); + expect(() => getBuilder().addChannelOption(getChannelOption().addChannelTypes(100, 200))).toThrowError(); }); test('GIVEN a builder with invalid number min/max options THEN does throw an error', () => { diff --git a/packages/builders/docs/examples/Slash Command Builders.md b/packages/builders/docs/examples/Slash Command Builders.md index 997e6457fe0f..5c434c0baba5 100644 --- a/packages/builders/docs/examples/Slash Command Builders.md +++ b/packages/builders/docs/examples/Slash Command Builders.md @@ -33,13 +33,7 @@ const boopCommand = new SlashCommandBuilder() option .setName('boop_reminder') .setDescription('How often should we remind you to boop the user') - .addChoice('Every day', 1) - .addChoice('Weekly', 7) - // Or, if you prefer adding more choices at once, you can use an array - .addChoices([ - ['Every three months', 90], - ['Yearly', 365], - ]), + .addChoices({ name: 'Every day', value: 1 }, { name: 'Weekly', value: 7 }), ); // Get the final raw data that can be sent to Discord @@ -71,11 +65,11 @@ const pointsCommand = new SlashCommandBuilder() option .setName('action') .setDescription('What action should be taken with the users points?') - .addChoices([ - ['Add points', 'add'], - ['Remove points', 'remove'], - ['Reset points', 'reset'], - ]) + .addChoices( + { name: 'Add points', value: 'add' }, + { name: 'Remove points', value: 'remove' }, + { name: 'Reset points', value: 'reset' }, + ) .setRequired(true), ) .addIntegerOption((option) => option.setName('points').setDescription('Points to add or remove')), @@ -102,4 +96,4 @@ const pointsCommand = new SlashCommandBuilder() // Get the final raw data that can be sent to Discord const rawData = pointsCommand.toJSON(); -``` \ No newline at end of file +``` diff --git a/packages/builders/src/interactions/slashCommands/Assertions.ts b/packages/builders/src/interactions/slashCommands/Assertions.ts index d409ad71649a..6530d5083e85 100644 --- a/packages/builders/src/interactions/slashCommands/Assertions.ts +++ b/packages/builders/src/interactions/slashCommands/Assertions.ts @@ -52,8 +52,10 @@ export function validateRequired(required: unknown): asserts required is boolean booleanPredicate.parse(required); } -export function validateMaxChoicesLength(choices: APIApplicationCommandOptionChoice[]) { - maxArrayLengthPredicate.parse(choices); +const choicesLengthPredicate = z.number().lte(25); + +export function validateChoicesLength(amountAdding: number, choices?: APIApplicationCommandOptionChoice[]): void { + choicesLengthPredicate.parse((choices?.length ?? 0) + amountAdding); } export function assertReturnOfBuilder< diff --git a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts index 783cdd873a2d..34253368a080 100644 --- a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts +++ b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionChannelTypesMixin.ts @@ -16,40 +16,33 @@ const allowedChannelTypes = [ export type ApplicationCommandOptionAllowedChannelTypes = typeof allowedChannelTypes[number]; -const channelTypePredicate = z.union( - allowedChannelTypes.map((type) => z.literal(type)) as [ - ZodLiteral, - ZodLiteral, - ...ZodLiteral[] - ], +const channelTypesPredicate = z.array( + z.union( + allowedChannelTypes.map((type) => z.literal(type)) as [ + ZodLiteral, + ZodLiteral, + ...ZodLiteral[] + ], + ), ); export class ApplicationCommandOptionChannelTypesMixin { public readonly channel_types?: ApplicationCommandOptionAllowedChannelTypes[]; /** - * Adds a channel type to this option + * Adds channel types to this option * - * @param channelType The type of channel to allow + * @param channelTypes The channel types to add */ - public addChannelType(channelType: ApplicationCommandOptionAllowedChannelTypes) { + public addChannelTypes(...channelTypes: ApplicationCommandOptionAllowedChannelTypes[]) { if (this.channel_types === undefined) { Reflect.set(this, 'channel_types', []); } - channelTypePredicate.parse(channelType); - this.channel_types!.push(channelType); + channelTypesPredicate.parse(channelTypes); - return this; - } + this.channel_types!.push(...channelTypes); - /** - * Adds channel types to this option - * - * @param channelTypes The channel types to add - */ - public addChannelTypes(channelTypes: ApplicationCommandOptionAllowedChannelTypes[]) { - channelTypes.forEach((channelType) => this.addChannelType(channelType)); return this; } } diff --git a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin.ts b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin.ts index e6f954e0e2ab..53e8b56402bc 100644 --- a/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin.ts +++ b/packages/builders/src/interactions/slashCommands/mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin.ts @@ -1,6 +1,6 @@ import { APIApplicationCommandOptionChoice, ApplicationCommandOptionType } from 'discord-api-types/v9'; import { z } from 'zod'; -import { validateMaxChoicesLength } from '../Assertions'; +import { validateChoicesLength } from '../Assertions'; const stringPredicate = z.string().min(1).max(100); const numberPredicate = z.number().gt(-Infinity).lt(Infinity); @@ -17,50 +17,34 @@ export class ApplicationCommandOptionWithChoicesAndAutocompleteMixin): this { - const { name, value } = choice; - if (this.autocomplete) { + public addChoices(...choices: APIApplicationCommandOptionChoice[]): this { + if (choices.length > 0 && this.autocomplete) { throw new RangeError('Autocomplete and choices are mutually exclusive to each other.'); } + choicesPredicate.parse(choices); + if (this.choices === undefined) { Reflect.set(this, 'choices', []); } - validateMaxChoicesLength(this.choices!); + validateChoicesLength(choices.length, this.choices); - // Validate name - stringPredicate.parse(name); + for (const { name, value } of choices) { + // Validate the value + if (this.type === ApplicationCommandOptionType.String) { + stringPredicate.parse(value); + } else { + numberPredicate.parse(value); + } - // Validate the value - if (this.type === ApplicationCommandOptionType.String) { - stringPredicate.parse(value); - } else { - numberPredicate.parse(value); + this.choices!.push({ name, value }); } - this.choices!.push({ name, value }); - - return this; - } - - /** - * Adds multiple choices for this option - * - * @param choices The choices to add - */ - public addChoices(...choices: APIApplicationCommandOptionChoice[]): this { - if (this.autocomplete) { - throw new RangeError('Autocomplete and choices are mutually exclusive to each other.'); - } - - choicesPredicate.parse(choices); - - for (const entry of choices) this.addChoice(entry); return this; } @@ -72,7 +56,7 @@ export class ApplicationCommandOptionWithChoicesAndAutocompleteMixin { input: | SlashCommandStringOption | Omit - | Omit + | Omit | (( builder: SlashCommandStringOption, ) => | SlashCommandStringOption | Omit - | Omit), + | Omit), ) { return this._sharedAddOptionMethod(input, SlashCommandStringOption); } @@ -105,13 +105,13 @@ export class SharedSlashCommandOptions { input: | SlashCommandIntegerOption | Omit - | Omit + | Omit | (( builder: SlashCommandIntegerOption, ) => | SlashCommandIntegerOption | Omit - | Omit), + | Omit), ) { return this._sharedAddOptionMethod(input, SlashCommandIntegerOption); } @@ -125,13 +125,13 @@ export class SharedSlashCommandOptions { input: | SlashCommandNumberOption | Omit - | Omit + | Omit | (( builder: SlashCommandNumberOption, ) => | SlashCommandNumberOption | Omit - | Omit), + | Omit), ) { return this._sharedAddOptionMethod(input, SlashCommandNumberOption); } @@ -140,8 +140,8 @@ export class SharedSlashCommandOptions { input: | T | Omit - | Omit - | ((builder: T) => T | Omit | Omit), + | Omit + | ((builder: T) => T | Omit | Omit), Instance: new () => T, ): ShouldOmitSubcommandFunctions extends true ? Omit : this { const { options } = this;