Skip to content

Commit

Permalink
feat: allow RestOrArray for command option builders (#10175)
Browse files Browse the repository at this point in the history
* 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 <github@almeidx.dev>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
3 people committed Mar 24, 2024
1 parent ddc927f commit a1a3a95
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 19 deletions.
Expand Up @@ -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),
Expand Down Expand Up @@ -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(() => {
Expand Down
@@ -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.
Expand Down Expand Up @@ -41,12 +42,12 @@ export class ApplicationCommandOptionChannelTypesMixin {
*
* @param channelTypes - The channel types
*/
public addChannelTypes(...channelTypes: ApplicationCommandOptionAllowedChannelTypes[]) {
public addChannelTypes(...channelTypes: RestOrArray<ApplicationCommandOptionAllowedChannelTypes>) {
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;
}
Expand Down
@@ -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);
Expand Down Expand Up @@ -31,20 +32,21 @@ export class ApplicationCommandOptionWithChoicesMixin<ChoiceType extends number
*
* @param choices - The choices to add
*/
public addChoices(...choices: APIApplicationCommandOptionChoice<ChoiceType>[]): this {
if (choices.length > 0 && 'autocomplete' in this && this.autocomplete) {
public addChoices(...choices: RestOrArray<APIApplicationCommandOptionChoice<ChoiceType>>): 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);
Expand All @@ -63,15 +65,16 @@ export class ApplicationCommandOptionWithChoicesMixin<ChoiceType extends number
*
* @param choices - The choices to set
*/
public setChoices<Input extends APIApplicationCommandOptionChoice<ChoiceType>[]>(...choices: Input): this {
if (choices.length > 0 && 'autocomplete' in this && this.autocomplete) {
public setChoices<Input extends APIApplicationCommandOptionChoice<ChoiceType>>(...choices: RestOrArray<Input>): 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;
}
Expand Down

0 comments on commit a1a3a95

Please sign in to comment.