Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: SlashCommandOption#setType and SharedSlashCommandOptions#addOption #10491

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/builders/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export * from './components/selectMenu/UserSelectMenu.js';
export * as SlashCommandAssertions from './interactions/slashCommands/Assertions.js';
export * from './interactions/slashCommands/SlashCommandBuilder.js';
export * from './interactions/slashCommands/SlashCommandSubcommands.js';
export * from './interactions/slashCommands/options/all.js';
export * from './interactions/slashCommands/options/boolean.js';
export * from './interactions/slashCommands/options/channel.js';
export * from './interactions/slashCommands/options/integer.js';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,46 @@ import { SlashCommandStringOption } from '../options/string.js';
import { SlashCommandUserOption } from '../options/user.js';
import type { ApplicationCommandOptionBase } from './ApplicationCommandOptionBase.js';

/**
* Represents the slash command option type.
*/
export type SlashCommandOptionType =
| SlashCommandAttachmentOption
| SlashCommandBooleanOption
| SlashCommandChannelOption
| SlashCommandIntegerOption
| SlashCommandMentionableOption
| SlashCommandNumberOption
| SlashCommandRoleOption
| SlashCommandStringOption
| SlashCommandUserOption;

/**
* Represents the slash command option type constructors.
*/
type SlashCommandOptionTypeConstructor =
| typeof SlashCommandAttachmentOption
| typeof SlashCommandBooleanOption
| typeof SlashCommandChannelOption
| typeof SlashCommandIntegerOption
| typeof SlashCommandMentionableOption
| typeof SlashCommandNumberOption
| typeof SlashCommandRoleOption
| typeof SlashCommandStringOption
| typeof SlashCommandUserOption;

const optionMap = new Map<string, SlashCommandOptionTypeConstructor>([
['SlashCommandAttachmentOption', SlashCommandAttachmentOption],
['SlashCommandBooleanOption', SlashCommandBooleanOption],
['SlashCommandChannelOption', SlashCommandChannelOption],
['SlashCommandIntegerOption', SlashCommandIntegerOption],
['SlashCommandMentionableOption', SlashCommandMentionableOption],
['SlashCommandNumberOption', SlashCommandNumberOption],
['SlashCommandRoleOption', SlashCommandRoleOption],
['SlashCommandStringOption', SlashCommandStringOption],
['SlashCommandUserOption', SlashCommandUserOption],
]);

/**
* This mixin holds symbols that can be shared in slash command options.
*
Expand All @@ -21,6 +61,33 @@ export class SharedSlashCommandOptions<
> {
public readonly options!: ToAPIApplicationCommandOptions[];

/**
* Adds an option of any type.
*
* @param input - A function that returns an option builder or an already built builder
*/
public addOption(input: SlashCommandOptionType | ((builder: SlashCommandOptionType) => SlashCommandOptionType)) {
if (typeof input === 'function') {
for (const OptionConstructor of optionMap.values()) {
try {
const instance = new OptionConstructor();
const result = input(instance);
const OptionBuilder = optionMap.get(result.constructor.name);
if (OptionBuilder) return this._sharedAddOptionMethod(input, OptionBuilder);
} catch {
// Ignore errors from passing incorrect arguments to input
}
}

throw new Error(`Unsupported option type returned from function input to addOption()`);
}

const OptionBuilder = optionMap.get(input?.constructor.name);
if (OptionBuilder) return this._sharedAddOptionMethod(input, OptionBuilder);

throw new Error(`Unsupported option type passed to addOption(): ${input?.constructor.name}`);
}

/**
* Adds a boolean option.
*
Expand Down
147 changes: 147 additions & 0 deletions packages/builders/src/interactions/slashCommands/options/all.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { ApplicationCommandOptionType } from 'discord-api-types/v10';
import { SlashCommandAttachmentOption } from './attachment.js';
import { SlashCommandBooleanOption } from './boolean.js';
import { SlashCommandChannelOption } from './channel.js';
import { SlashCommandIntegerOption } from './integer.js';
import { SlashCommandMentionableOption } from './mentionable.js';
import { SlashCommandNumberOption } from './number.js';
import { SlashCommandRoleOption } from './role.js';
import { SlashCommandStringOption } from './string.js';
import { SlashCommandUserOption } from './user.js';

/**
* The slash command option types as strings.
*/
export type ApplicationCommandOptionStringType =
| 'attachment'
| 'boolean'
| 'channel'
| 'integer'
| 'mentionable'
| 'number'
| 'role'
| 'string'
| 'user';

/**
* Returns the corresponding slash command option based on the {@link ApplicationCommandOptionType}.
*
* @typeParam OptionType - The type of option
*/
export type ApplicationCommandOptionEnumTypeMap<OptionType extends ApplicationCommandOptionType> =
OptionType extends ApplicationCommandOptionType.Attachment
? SlashCommandAttachmentOption
: OptionType extends ApplicationCommandOptionType.Boolean
? SlashCommandBooleanOption
: OptionType extends ApplicationCommandOptionType.Channel
? SlashCommandChannelOption
: OptionType extends ApplicationCommandOptionType.Integer
? SlashCommandIntegerOption
: OptionType extends ApplicationCommandOptionType.Mentionable
? SlashCommandMentionableOption
: OptionType extends ApplicationCommandOptionType.Number
? SlashCommandNumberOption
: OptionType extends ApplicationCommandOptionType.Role
? SlashCommandRoleOption
: OptionType extends ApplicationCommandOptionType.String
? SlashCommandStringOption
: OptionType extends ApplicationCommandOptionType.User
? SlashCommandUserOption
: never;

/**
* Returns the corresponding slash command option based on the string type.
*
* @typeParam OptionType - The type of option
*/
export type ApplicationCommandOptionStringTypeMap<OptionType extends ApplicationCommandOptionStringType> =
OptionType extends 'attachment'
? SlashCommandAttachmentOption
: OptionType extends 'boolean'
? SlashCommandBooleanOption
: OptionType extends 'channel'
? SlashCommandChannelOption
: OptionType extends 'integer'
? SlashCommandIntegerOption
: OptionType extends 'mentionable'
? SlashCommandMentionableOption
: OptionType extends 'number'
? SlashCommandNumberOption
: OptionType extends 'role'
? SlashCommandRoleOption
: OptionType extends 'string'
? SlashCommandStringOption
: OptionType extends 'user'
? SlashCommandUserOption
: never;

/**
* Returns the corresponding slash command option based on the type.
*
* @typeParam OptionType - The type of option
*/
export type ApplicationCommandOptionTypeMap<
OptionType extends ApplicationCommandOptionStringType | ApplicationCommandOptionType,
> = OptionType extends ApplicationCommandOptionType
? ApplicationCommandOptionEnumTypeMap<OptionType>
: OptionType extends ApplicationCommandOptionStringType
? ApplicationCommandOptionStringTypeMap<OptionType>
: never;

/**
* A base slash command option that can have its type specified.
*/
export class SlashCommandOption {
/* eslint-disable-next-line @typescript-eslint/no-useless-constructor */
public constructor() {}

/**
* Creates and returns the slash command option of the specified type.
*
* @param type - The type of option to create
*/
public setType = <OptionType extends ApplicationCommandOptionStringType | ApplicationCommandOptionType>(
type: OptionType,
) => {
switch (type) {
case ApplicationCommandOptionType.Attachment:
return new SlashCommandAttachmentOption() as ApplicationCommandOptionTypeMap<OptionType>;
case ApplicationCommandOptionType.Boolean:
return new SlashCommandBooleanOption() as ApplicationCommandOptionTypeMap<OptionType>;
case ApplicationCommandOptionType.Channel:
return new SlashCommandChannelOption() as ApplicationCommandOptionTypeMap<OptionType>;
case ApplicationCommandOptionType.Integer:
return new SlashCommandIntegerOption() as ApplicationCommandOptionTypeMap<OptionType>;
case ApplicationCommandOptionType.Mentionable:
return new SlashCommandMentionableOption() as ApplicationCommandOptionTypeMap<OptionType>;
case ApplicationCommandOptionType.Number:
return new SlashCommandNumberOption() as ApplicationCommandOptionTypeMap<OptionType>;
case ApplicationCommandOptionType.Role:
return new SlashCommandRoleOption() as ApplicationCommandOptionTypeMap<OptionType>;
case ApplicationCommandOptionType.String:
return new SlashCommandStringOption() as ApplicationCommandOptionTypeMap<OptionType>;
case ApplicationCommandOptionType.User:
return new SlashCommandUserOption() as ApplicationCommandOptionTypeMap<OptionType>;
case 'attachment':
return new SlashCommandAttachmentOption() as ApplicationCommandOptionTypeMap<OptionType>;
case 'boolean':
return new SlashCommandBooleanOption() as ApplicationCommandOptionTypeMap<OptionType>;
case 'channel':
return new SlashCommandChannelOption() as ApplicationCommandOptionTypeMap<OptionType>;
case 'integer':
return new SlashCommandIntegerOption() as ApplicationCommandOptionTypeMap<OptionType>;
case 'mentionable':
return new SlashCommandMentionableOption() as ApplicationCommandOptionTypeMap<OptionType>;
case 'number':
return new SlashCommandNumberOption() as ApplicationCommandOptionTypeMap<OptionType>;
case 'role':
return new SlashCommandRoleOption() as ApplicationCommandOptionTypeMap<OptionType>;
case 'string':
return new SlashCommandStringOption() as ApplicationCommandOptionTypeMap<OptionType>;
case 'user':
return new SlashCommandUserOption() as ApplicationCommandOptionTypeMap<OptionType>;
default:
throw new Error(`Unsupported option type: ${type}`);
}
};
}