chore: monorepo setup (#7175)

This commit is contained in:
Noel
2022-01-07 17:18:25 +01:00
committed by GitHub
parent 780b7ed39f
commit 16390efe6e
504 changed files with 25459 additions and 22830 deletions

View File

@@ -0,0 +1,33 @@
import { z } from 'zod';
import { ApplicationCommandType } from 'discord-api-types/v9';
import type { ContextMenuCommandType } from './ContextMenuCommandBuilder';
export function validateRequiredParameters(name: string, type: number) {
// Assert name matches all conditions
validateName(name);
// Assert type is valid
validateType(type);
}
const namePredicate = z
.string()
.min(1)
.max(32)
.regex(/^( *[\p{L}\p{N}_-]+ *)+$/u);
export function validateName(name: unknown): asserts name is string {
namePredicate.parse(name);
}
const typePredicate = z.union([z.literal(ApplicationCommandType.User), z.literal(ApplicationCommandType.Message)]);
export function validateType(type: unknown): asserts type is ContextMenuCommandType {
typePredicate.parse(type);
}
const booleanPredicate = z.boolean();
export function validateDefaultPermission(value: unknown): asserts value is boolean {
booleanPredicate.parse(value);
}

View File

@@ -0,0 +1,83 @@
import { validateRequiredParameters, validateName, validateType, validateDefaultPermission } from './Assertions';
import type { ApplicationCommandType, RESTPostAPIApplicationCommandsJSONBody } from 'discord-api-types/v9';
export class ContextMenuCommandBuilder {
/**
* The name of this context menu command
*/
public readonly name: string = undefined!;
/**
* The type of this context menu command
*/
public readonly type: ContextMenuCommandType = undefined!;
/**
* Whether the command is enabled by default when the app is added to a guild
*
* @default true
*/
public readonly defaultPermission: boolean | undefined = undefined;
/**
* Sets the name
*
* @param name The name
*/
public setName(name: string) {
// Assert the name matches the conditions
validateName(name);
Reflect.set(this, 'name', name);
return this;
}
/**
* Sets the type
*
* @param type The type
*/
public setType(type: ContextMenuCommandType) {
// Assert the type is valid
validateType(type);
Reflect.set(this, 'type', type);
return this;
}
/**
* Sets whether the command is enabled by default when the application is added to a guild.
*
* **Note**: If set to `false`, you will have to later `PUT` the permissions for this command.
*
* @param value Whether or not to enable this command by default
*
* @see https://discord.com/developers/docs/interactions/application-commands#permissions
*/
public setDefaultPermission(value: boolean) {
// Assert the value matches the conditions
validateDefaultPermission(value);
Reflect.set(this, 'defaultPermission', value);
return this;
}
/**
* Returns the final data that should be sent to Discord.
*
* **Note:** Calling this function will validate required properties based on their conditions.
*/
public toJSON(): RESTPostAPIApplicationCommandsJSONBody {
validateRequiredParameters(this.name, this.type);
return {
name: this.name,
type: this.type,
default_permission: this.defaultPermission,
};
}
}
export type ContextMenuCommandType = ApplicationCommandType.User | ApplicationCommandType.Message;

View File

@@ -0,0 +1,84 @@
import is from '@sindresorhus/is';
import type { APIApplicationCommandOptionChoice } from 'discord-api-types/v9';
import { z } from 'zod';
import type { ApplicationCommandOptionBase } from './mixins/ApplicationCommandOptionBase';
import type { ToAPIApplicationCommandOptions } from './SlashCommandBuilder';
import type { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands';
export function validateRequiredParameters(
name: string,
description: string,
options: ToAPIApplicationCommandOptions[],
) {
// Assert name matches all conditions
validateName(name);
// Assert description conditions
validateDescription(description);
// Assert options conditions
validateMaxOptionsLength(options);
}
const namePredicate = z
.string()
.min(1)
.max(32)
.regex(/^[\P{Lu}\p{N}_-]+$/u);
export function validateName(name: unknown): asserts name is string {
namePredicate.parse(name);
}
const descriptionPredicate = z.string().min(1).max(100);
export function validateDescription(description: unknown): asserts description is string {
descriptionPredicate.parse(description);
}
const booleanPredicate = z.boolean();
export function validateDefaultPermission(value: unknown): asserts value is boolean {
booleanPredicate.parse(value);
}
export function validateRequired(required: unknown): asserts required is boolean {
booleanPredicate.parse(required);
}
const maxArrayLengthPredicate = z.unknown().array().max(25);
export function validateMaxOptionsLength(options: unknown): asserts options is ToAPIApplicationCommandOptions[] {
maxArrayLengthPredicate.parse(options);
}
export function validateMaxChoicesLength(choices: APIApplicationCommandOptionChoice[]) {
maxArrayLengthPredicate.parse(choices);
}
export function assertReturnOfBuilder<
T extends ApplicationCommandOptionBase | SlashCommandSubcommandBuilder | SlashCommandSubcommandGroupBuilder,
>(input: unknown, ExpectedInstanceOf: new () => T): asserts input is T {
const instanceName = ExpectedInstanceOf.name;
if (is.nullOrUndefined(input)) {
throw new TypeError(
`Expected to receive a ${instanceName} builder, got ${input === null ? 'null' : 'undefined'} instead.`,
);
}
if (is.primitive(input)) {
throw new TypeError(`Expected to receive a ${instanceName} builder, got a primitive (${typeof input}) instead.`);
}
if (!(input instanceof ExpectedInstanceOf)) {
const casted = input as Record<PropertyKey, unknown>;
const constructorName = is.function_(input) ? input.name : casted.constructor.name;
const stringTag = Reflect.get(casted, Symbol.toStringTag) as string | undefined;
const fullResultName = stringTag ? `${constructorName} [${stringTag}]` : constructorName;
throw new TypeError(`Expected to receive a ${instanceName} builder, got ${fullResultName} instead.`);
}
}

View File

@@ -0,0 +1,137 @@
import type { APIApplicationCommandOption, RESTPostAPIApplicationCommandsJSONBody } from 'discord-api-types/v9';
import { mix } from 'ts-mixer';
import {
assertReturnOfBuilder,
validateDefaultPermission,
validateMaxOptionsLength,
validateRequiredParameters,
} from './Assertions';
import { SharedSlashCommandOptions } from './mixins/SharedSlashCommandOptions';
import { SharedNameAndDescription } from './mixins/NameAndDescription';
import { SlashCommandSubcommandBuilder, SlashCommandSubcommandGroupBuilder } from './SlashCommandSubcommands';
@mix(SharedSlashCommandOptions, SharedNameAndDescription)
export class SlashCommandBuilder {
/**
* The name of this slash command
*/
public readonly name: string = undefined!;
/**
* The description of this slash command
*/
public readonly description: string = undefined!;
/**
* The options of this slash command
*/
public readonly options: ToAPIApplicationCommandOptions[] = [];
/**
* Whether the command is enabled by default when the app is added to a guild
*
* @default true
*/
public readonly defaultPermission: boolean | undefined = undefined;
/**
* Returns the final data that should be sent to Discord.
*
* **Note:** Calling this function will validate required properties based on their conditions.
*/
public toJSON(): RESTPostAPIApplicationCommandsJSONBody {
validateRequiredParameters(this.name, this.description, this.options);
return {
name: this.name,
description: this.description,
options: this.options.map((option) => option.toJSON()),
default_permission: this.defaultPermission,
};
}
/**
* Sets whether the command is enabled by default when the application is added to a guild.
*
* **Note**: If set to `false`, you will have to later `PUT` the permissions for this command.
*
* @param value Whether or not to enable this command by default
*
* @see https://discord.com/developers/docs/interactions/application-commands#permissions
*/
public setDefaultPermission(value: boolean) {
// Assert the value matches the conditions
validateDefaultPermission(value);
Reflect.set(this, 'defaultPermission', value);
return this;
}
/**
* Adds a new subcommand group to this command
*
* @param input A function that returns a subcommand group builder, or an already built builder
*/
public addSubcommandGroup(
input:
| SlashCommandSubcommandGroupBuilder
| ((subcommandGroup: SlashCommandSubcommandGroupBuilder) => SlashCommandSubcommandGroupBuilder),
): SlashCommandSubcommandsOnlyBuilder {
const { options } = this;
// First, assert options conditions - we cannot have more than 25 options
validateMaxOptionsLength(options);
// Get the final result
const result = typeof input === 'function' ? input(new SlashCommandSubcommandGroupBuilder()) : input;
assertReturnOfBuilder(result, SlashCommandSubcommandGroupBuilder);
// Push it
options.push(result);
return this;
}
/**
* Adds a new subcommand to this command
*
* @param input A function that returns a subcommand builder, or an already built builder
*/
public addSubcommand(
input:
| SlashCommandSubcommandBuilder
| ((subcommandGroup: SlashCommandSubcommandBuilder) => SlashCommandSubcommandBuilder),
): SlashCommandSubcommandsOnlyBuilder {
const { options } = this;
// First, assert options conditions - we cannot have more than 25 options
validateMaxOptionsLength(options);
// Get the final result
const result = typeof input === 'function' ? input(new SlashCommandSubcommandBuilder()) : input;
assertReturnOfBuilder(result, SlashCommandSubcommandBuilder);
// Push it
options.push(result);
return this;
}
}
export interface SlashCommandBuilder extends SharedNameAndDescription, SharedSlashCommandOptions {}
export interface SlashCommandSubcommandsOnlyBuilder
extends SharedNameAndDescription,
Pick<SlashCommandBuilder, 'toJSON' | 'addSubcommand' | 'addSubcommandGroup'> {}
export interface SlashCommandOptionsOnlyBuilder
extends SharedNameAndDescription,
SharedSlashCommandOptions,
Pick<SlashCommandBuilder, 'toJSON'> {}
export interface ToAPIApplicationCommandOptions {
toJSON(): APIApplicationCommandOption;
}

View File

@@ -0,0 +1,109 @@
import {
APIApplicationCommandSubcommandGroupOption,
APIApplicationCommandSubcommandOption,
ApplicationCommandOptionType,
} from 'discord-api-types/v9';
import { mix } from 'ts-mixer';
import { assertReturnOfBuilder, validateMaxOptionsLength, validateRequiredParameters } from './Assertions';
import type { ApplicationCommandOptionBase } from './mixins/ApplicationCommandOptionBase';
import { SharedNameAndDescription } from './mixins/NameAndDescription';
import { SharedSlashCommandOptions } from './mixins/SharedSlashCommandOptions';
import type { ToAPIApplicationCommandOptions } from './SlashCommandBuilder';
/**
* Represents a folder for subcommands
*
* For more information, go to https://discord.com/developers/docs/interactions/slash-commands#subcommands-and-subcommand-groups
*/
@mix(SharedNameAndDescription)
export class SlashCommandSubcommandGroupBuilder implements ToAPIApplicationCommandOptions {
/**
* The name of this subcommand group
*/
public readonly name: string = undefined!;
/**
* The description of this subcommand group
*/
public readonly description: string = undefined!;
/**
* The subcommands part of this subcommand group
*/
public readonly options: SlashCommandSubcommandBuilder[] = [];
/**
* Adds a new subcommand to this group
*
* @param input A function that returns a subcommand builder, or an already built builder
*/
public addSubcommand(
input:
| SlashCommandSubcommandBuilder
| ((subcommandGroup: SlashCommandSubcommandBuilder) => SlashCommandSubcommandBuilder),
) {
const { options } = this;
// First, assert options conditions - we cannot have more than 25 options
validateMaxOptionsLength(options);
// Get the final result
const result = typeof input === 'function' ? input(new SlashCommandSubcommandBuilder()) : input;
assertReturnOfBuilder(result, SlashCommandSubcommandBuilder);
// Push it
options.push(result);
return this;
}
public toJSON(): APIApplicationCommandSubcommandGroupOption {
validateRequiredParameters(this.name, this.description, this.options);
return {
type: ApplicationCommandOptionType.SubcommandGroup,
name: this.name,
description: this.description,
options: this.options.map((option) => option.toJSON()),
};
}
}
export interface SlashCommandSubcommandGroupBuilder extends SharedNameAndDescription {}
/**
* Represents a subcommand
*
* For more information, go to https://discord.com/developers/docs/interactions/slash-commands#subcommands-and-subcommand-groups
*/
@mix(SharedNameAndDescription, SharedSlashCommandOptions)
export class SlashCommandSubcommandBuilder implements ToAPIApplicationCommandOptions {
/**
* The name of this subcommand
*/
public readonly name: string = undefined!;
/**
* The description of this subcommand
*/
public readonly description: string = undefined!;
/**
* The options of this subcommand
*/
public readonly options: ApplicationCommandOptionBase[] = [];
public toJSON(): APIApplicationCommandSubcommandOption {
validateRequiredParameters(this.name, this.description, this.options);
return {
type: ApplicationCommandOptionType.Subcommand,
name: this.name,
description: this.description,
options: this.options.map((option) => option.toJSON()),
};
}
}
export interface SlashCommandSubcommandBuilder extends SharedNameAndDescription, SharedSlashCommandOptions<false> {}

View File

@@ -0,0 +1,16 @@
export abstract class ApplicationCommandNumericOptionMinMaxValueMixin {
public readonly max_value?: number;
public readonly min_value?: number;
/**
* Sets the maximum number value of this option
* @param max The maximum value this option can be
*/
public abstract setMaxValue(max: number): this;
/**
* Sets the minimum number value of this option
* @param min The minimum value this option can be
*/
public abstract setMinValue(min: number): this;
}

View File

@@ -0,0 +1,32 @@
import type { APIApplicationCommandBasicOption, ApplicationCommandOptionType } from 'discord-api-types/v9';
import { validateRequiredParameters, validateRequired } from '../Assertions';
import { SharedNameAndDescription } from './NameAndDescription';
export abstract class ApplicationCommandOptionBase extends SharedNameAndDescription {
public abstract readonly type: ApplicationCommandOptionType;
public readonly required = false;
/**
* Marks the option as required
*
* @param required If this option should be required
*/
public setRequired(required: boolean) {
// Assert that you actually passed a boolean
validateRequired(required);
Reflect.set(this, 'required', required);
return this;
}
public abstract toJSON(): APIApplicationCommandBasicOption;
protected runRequiredValidations() {
validateRequiredParameters(this.name, this.description, []);
// Assert that you actually passed a boolean
validateRequired(this.required);
}
}

View File

@@ -0,0 +1,55 @@
import { ChannelType } from 'discord-api-types/v9';
import { z, ZodLiteral } from 'zod';
// Only allow valid channel types to be used. (This can't be dynamic because const enums are erased at runtime)
const allowedChannelTypes = [
ChannelType.GuildText,
ChannelType.GuildVoice,
ChannelType.GuildCategory,
ChannelType.GuildNews,
ChannelType.GuildStore,
ChannelType.GuildNewsThread,
ChannelType.GuildPublicThread,
ChannelType.GuildPrivateThread,
ChannelType.GuildStageVoice,
] as const;
export type ApplicationCommandOptionAllowedChannelTypes = typeof allowedChannelTypes[number];
const channelTypePredicate = z.union(
allowedChannelTypes.map((type) => z.literal(type)) as [
ZodLiteral<ChannelType>,
ZodLiteral<ChannelType>,
...ZodLiteral<ChannelType>[]
],
);
export class ApplicationCommandOptionChannelTypesMixin {
public readonly channel_types?: ApplicationCommandOptionAllowedChannelTypes[];
/**
* Adds a channel type to this option
*
* @param channelType The type of channel to allow
*/
public addChannelType(channelType: ApplicationCommandOptionAllowedChannelTypes) {
if (this.channel_types === undefined) {
Reflect.set(this, 'channel_types', []);
}
channelTypePredicate.parse(channelType);
this.channel_types!.push(channelType);
return this;
}
/**
* 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;
}
}

View File

@@ -0,0 +1,102 @@
import { APIApplicationCommandOptionChoice, ApplicationCommandOptionType } from 'discord-api-types/v9';
import { z } from 'zod';
import { validateMaxChoicesLength } from '../Assertions';
const stringPredicate = z.string().min(1).max(100);
const numberPredicate = z.number().gt(-Infinity).lt(Infinity);
const choicesPredicate = z.tuple([stringPredicate, z.union([stringPredicate, numberPredicate])]).array();
const booleanPredicate = z.boolean();
export class ApplicationCommandOptionWithChoicesAndAutocompleteMixin<T extends string | number> {
public readonly choices?: APIApplicationCommandOptionChoice<T>[];
public readonly autocomplete?: boolean;
// Since this is present and this is a mixin, this is needed
public readonly type!: ApplicationCommandOptionType;
/**
* Adds a choice for this option
*
* @param name The name of the choice
* @param value The value of the choice
*/
public addChoice(name: string, value: T): Omit<this, 'setAutocomplete'> {
if (this.autocomplete) {
throw new RangeError('Autocomplete and choices are mutually exclusive to each other.');
}
if (this.choices === undefined) {
Reflect.set(this, 'choices', []);
}
validateMaxChoicesLength(this.choices!);
// Validate name
stringPredicate.parse(name);
// Validate the value
if (this.type === ApplicationCommandOptionType.String) {
stringPredicate.parse(value);
} else {
numberPredicate.parse(value);
}
this.choices!.push({ name, value });
return this;
}
/**
* Adds multiple choices for this option
*
* @param choices The choices to add
*/
public addChoices(choices: [name: string, value: T][]): Omit<this, 'setAutocomplete'> {
if (this.autocomplete) {
throw new RangeError('Autocomplete and choices are mutually exclusive to each other.');
}
choicesPredicate.parse(choices);
for (const [label, value] of choices) this.addChoice(label, value);
return this;
}
public setChoices<Input extends [name: string, value: T][]>(
choices: Input,
): Input extends []
? this & Pick<ApplicationCommandOptionWithChoicesAndAutocompleteMixin<T>, 'setAutocomplete'>
: Omit<this, 'setAutocomplete'> {
if (choices.length > 0 && this.autocomplete) {
throw new RangeError('Autocomplete and choices are mutually exclusive to each other.');
}
choicesPredicate.parse(choices);
Reflect.set(this, 'choices', []);
for (const [label, value] of choices) this.addChoice(label, value);
return this;
}
/**
* Marks the option as autocompletable
* @param autocomplete If this option should be autocompletable
*/
public setAutocomplete<U extends boolean>(
autocomplete: U,
): U extends true
? Omit<this, 'addChoice' | 'addChoices'>
: this & Pick<ApplicationCommandOptionWithChoicesAndAutocompleteMixin<T>, 'addChoice' | 'addChoices'> {
// Assert that you actually passed a boolean
booleanPredicate.parse(autocomplete);
if (autocomplete && Array.isArray(this.choices) && this.choices.length > 0) {
throw new RangeError('Autocomplete and choices are mutually exclusive to each other.');
}
Reflect.set(this, 'autocomplete', autocomplete);
return this;
}
}

View File

@@ -0,0 +1,34 @@
import { validateDescription, validateName } from '../Assertions';
export class SharedNameAndDescription {
public readonly name!: string;
public readonly description!: string;
/**
* Sets the name
*
* @param name The name
*/
public setName(name: string): this {
// Assert the name matches the conditions
validateName(name);
Reflect.set(this, 'name', name);
return this;
}
/**
* Sets the description
*
* @param description The description
*/
public setDescription(description: string) {
// Assert the description matches the conditions
validateDescription(description);
Reflect.set(this, 'description', description);
return this;
}
}

View File

@@ -0,0 +1,150 @@
import { assertReturnOfBuilder, validateMaxOptionsLength } from '../Assertions';
import type { ApplicationCommandOptionBase } from './ApplicationCommandOptionBase';
import { SlashCommandBooleanOption } from '../options/boolean';
import { SlashCommandChannelOption } from '../options/channel';
import { SlashCommandIntegerOption } from '../options/integer';
import { SlashCommandMentionableOption } from '../options/mentionable';
import { SlashCommandNumberOption } from '../options/number';
import { SlashCommandRoleOption } from '../options/role';
import { SlashCommandStringOption } from '../options/string';
import { SlashCommandUserOption } from '../options/user';
import type { ToAPIApplicationCommandOptions } from '../SlashCommandBuilder';
export class SharedSlashCommandOptions<ShouldOmitSubcommandFunctions = true> {
public readonly options!: ToAPIApplicationCommandOptions[];
/**
* Adds a boolean option
*
* @param input A function that returns an option builder, or an already built builder
*/
public addBooleanOption(
input: SlashCommandBooleanOption | ((builder: SlashCommandBooleanOption) => SlashCommandBooleanOption),
) {
return this._sharedAddOptionMethod(input, SlashCommandBooleanOption);
}
/**
* Adds a user option
*
* @param input A function that returns an option builder, or an already built builder
*/
public addUserOption(input: SlashCommandUserOption | ((builder: SlashCommandUserOption) => SlashCommandUserOption)) {
return this._sharedAddOptionMethod(input, SlashCommandUserOption);
}
/**
* Adds a channel option
*
* @param input A function that returns an option builder, or an already built builder
*/
public addChannelOption(
input: SlashCommandChannelOption | ((builder: SlashCommandChannelOption) => SlashCommandChannelOption),
) {
return this._sharedAddOptionMethod(input, SlashCommandChannelOption);
}
/**
* Adds a role option
*
* @param input A function that returns an option builder, or an already built builder
*/
public addRoleOption(input: SlashCommandRoleOption | ((builder: SlashCommandRoleOption) => SlashCommandRoleOption)) {
return this._sharedAddOptionMethod(input, SlashCommandRoleOption);
}
/**
* Adds a mentionable option
*
* @param input A function that returns an option builder, or an already built builder
*/
public addMentionableOption(
input: SlashCommandMentionableOption | ((builder: SlashCommandMentionableOption) => SlashCommandMentionableOption),
) {
return this._sharedAddOptionMethod(input, SlashCommandMentionableOption);
}
/**
* Adds a string option
*
* @param input A function that returns an option builder, or an already built builder
*/
public addStringOption(
input:
| SlashCommandStringOption
| Omit<SlashCommandStringOption, 'setAutocomplete'>
| Omit<SlashCommandStringOption, 'addChoice' | 'addChoices'>
| ((
builder: SlashCommandStringOption,
) =>
| SlashCommandStringOption
| Omit<SlashCommandStringOption, 'setAutocomplete'>
| Omit<SlashCommandStringOption, 'addChoice' | 'addChoices'>),
) {
return this._sharedAddOptionMethod(input, SlashCommandStringOption);
}
/**
* Adds an integer option
*
* @param input A function that returns an option builder, or an already built builder
*/
public addIntegerOption(
input:
| SlashCommandIntegerOption
| Omit<SlashCommandIntegerOption, 'setAutocomplete'>
| Omit<SlashCommandIntegerOption, 'addChoice' | 'addChoices'>
| ((
builder: SlashCommandIntegerOption,
) =>
| SlashCommandIntegerOption
| Omit<SlashCommandIntegerOption, 'setAutocomplete'>
| Omit<SlashCommandIntegerOption, 'addChoice' | 'addChoices'>),
) {
return this._sharedAddOptionMethod(input, SlashCommandIntegerOption);
}
/**
* Adds a number option
*
* @param input A function that returns an option builder, or an already built builder
*/
public addNumberOption(
input:
| SlashCommandNumberOption
| Omit<SlashCommandNumberOption, 'setAutocomplete'>
| Omit<SlashCommandNumberOption, 'addChoice' | 'addChoices'>
| ((
builder: SlashCommandNumberOption,
) =>
| SlashCommandNumberOption
| Omit<SlashCommandNumberOption, 'setAutocomplete'>
| Omit<SlashCommandNumberOption, 'addChoice' | 'addChoices'>),
) {
return this._sharedAddOptionMethod(input, SlashCommandNumberOption);
}
private _sharedAddOptionMethod<T extends ApplicationCommandOptionBase>(
input:
| T
| Omit<T, 'setAutocomplete'>
| Omit<T, 'addChoice' | 'addChoices'>
| ((builder: T) => T | Omit<T, 'setAutocomplete'> | Omit<T, 'addChoice' | 'addChoices'>),
Instance: new () => T,
): ShouldOmitSubcommandFunctions extends true ? Omit<this, 'addSubcommand' | 'addSubcommandGroup'> : this {
const { options } = this;
// First, assert options conditions - we cannot have more than 25 options
validateMaxOptionsLength(options);
// Get the final result
const result = typeof input === 'function' ? input(new Instance()) : input;
assertReturnOfBuilder(result, Instance);
// Push it
options.push(result);
return this;
}
}

View File

@@ -0,0 +1,12 @@
import { APIApplicationCommandBooleanOption, ApplicationCommandOptionType } from 'discord-api-types/v9';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
export class SlashCommandBooleanOption extends ApplicationCommandOptionBase {
public readonly type = ApplicationCommandOptionType.Boolean as const;
public toJSON(): APIApplicationCommandBooleanOption {
this.runRequiredValidations();
return { ...this };
}
}

View File

@@ -0,0 +1,17 @@
import { APIApplicationCommandChannelOption, ApplicationCommandOptionType } from 'discord-api-types/v9';
import { mix } from 'ts-mixer';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
import { ApplicationCommandOptionChannelTypesMixin } from '../mixins/ApplicationCommandOptionChannelTypesMixin';
@mix(ApplicationCommandOptionChannelTypesMixin)
export class SlashCommandChannelOption extends ApplicationCommandOptionBase {
public override readonly type = ApplicationCommandOptionType.Channel as const;
public toJSON(): APIApplicationCommandChannelOption {
this.runRequiredValidations();
return { ...this };
}
}
export interface SlashCommandChannelOption extends ApplicationCommandOptionChannelTypesMixin {}

View File

@@ -0,0 +1,46 @@
import { APIApplicationCommandIntegerOption, ApplicationCommandOptionType } from 'discord-api-types/v9';
import { mix } from 'ts-mixer';
import { z } from 'zod';
import { ApplicationCommandNumericOptionMinMaxValueMixin } from '../mixins/ApplicationCommandNumericOptionMinMaxValueMixin';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin';
const numberValidator = z.number().int().nonnegative();
@mix(ApplicationCommandNumericOptionMinMaxValueMixin, ApplicationCommandOptionWithChoicesAndAutocompleteMixin)
export class SlashCommandIntegerOption
extends ApplicationCommandOptionBase
implements ApplicationCommandNumericOptionMinMaxValueMixin
{
public readonly type = ApplicationCommandOptionType.Integer as const;
public setMaxValue(max: number): this {
numberValidator.parse(max);
Reflect.set(this, 'max_value', max);
return this;
}
public setMinValue(min: number): this {
numberValidator.parse(min);
Reflect.set(this, 'min_value', min);
return this;
}
public toJSON(): APIApplicationCommandIntegerOption {
this.runRequiredValidations();
if (this.autocomplete && Array.isArray(this.choices) && this.choices.length > 0) {
throw new RangeError('Autocomplete and choices are mutually exclusive to each other.');
}
return { ...this };
}
}
export interface SlashCommandIntegerOption
extends ApplicationCommandNumericOptionMinMaxValueMixin,
ApplicationCommandOptionWithChoicesAndAutocompleteMixin<number> {}

View File

@@ -0,0 +1,12 @@
import { APIApplicationCommandMentionableOption, ApplicationCommandOptionType } from 'discord-api-types/v9';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
export class SlashCommandMentionableOption extends ApplicationCommandOptionBase {
public readonly type = ApplicationCommandOptionType.Mentionable as const;
public toJSON(): APIApplicationCommandMentionableOption {
this.runRequiredValidations();
return { ...this };
}
}

View File

@@ -0,0 +1,46 @@
import { APIApplicationCommandNumberOption, ApplicationCommandOptionType } from 'discord-api-types/v9';
import { mix } from 'ts-mixer';
import { z } from 'zod';
import { ApplicationCommandNumericOptionMinMaxValueMixin } from '../mixins/ApplicationCommandNumericOptionMinMaxValueMixin';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin';
const numberValidator = z.number().nonnegative();
@mix(ApplicationCommandNumericOptionMinMaxValueMixin, ApplicationCommandOptionWithChoicesAndAutocompleteMixin)
export class SlashCommandNumberOption
extends ApplicationCommandOptionBase
implements ApplicationCommandNumericOptionMinMaxValueMixin
{
public readonly type = ApplicationCommandOptionType.Number as const;
public setMaxValue(max: number): this {
numberValidator.parse(max);
Reflect.set(this, 'max_value', max);
return this;
}
public setMinValue(min: number): this {
numberValidator.parse(min);
Reflect.set(this, 'min_value', min);
return this;
}
public toJSON(): APIApplicationCommandNumberOption {
this.runRequiredValidations();
if (this.autocomplete && Array.isArray(this.choices) && this.choices.length > 0) {
throw new RangeError('Autocomplete and choices are mutually exclusive to each other.');
}
return { ...this };
}
}
export interface SlashCommandNumberOption
extends ApplicationCommandNumericOptionMinMaxValueMixin,
ApplicationCommandOptionWithChoicesAndAutocompleteMixin<number> {}

View File

@@ -0,0 +1,12 @@
import { APIApplicationCommandRoleOption, ApplicationCommandOptionType } from 'discord-api-types/v9';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
export class SlashCommandRoleOption extends ApplicationCommandOptionBase {
public override readonly type = ApplicationCommandOptionType.Role as const;
public toJSON(): APIApplicationCommandRoleOption {
this.runRequiredValidations();
return { ...this };
}
}

View File

@@ -0,0 +1,21 @@
import { APIApplicationCommandStringOption, ApplicationCommandOptionType } from 'discord-api-types/v9';
import { mix } from 'ts-mixer';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
import { ApplicationCommandOptionWithChoicesAndAutocompleteMixin } from '../mixins/ApplicationCommandOptionWithChoicesAndAutocompleteMixin';
@mix(ApplicationCommandOptionWithChoicesAndAutocompleteMixin)
export class SlashCommandStringOption extends ApplicationCommandOptionBase {
public readonly type = ApplicationCommandOptionType.String as const;
public toJSON(): APIApplicationCommandStringOption {
this.runRequiredValidations();
if (this.autocomplete && Array.isArray(this.choices) && this.choices.length > 0) {
throw new RangeError('Autocomplete and choices are mutually exclusive to each other.');
}
return { ...this };
}
}
export interface SlashCommandStringOption extends ApplicationCommandOptionWithChoicesAndAutocompleteMixin<string> {}

View File

@@ -0,0 +1,12 @@
import { APIApplicationCommandUserOption, ApplicationCommandOptionType } from 'discord-api-types/v9';
import { ApplicationCommandOptionBase } from '../mixins/ApplicationCommandOptionBase';
export class SlashCommandUserOption extends ApplicationCommandOptionBase {
public readonly type = ApplicationCommandOptionType.User as const;
public toJSON(): APIApplicationCommandUserOption {
this.runRequiredValidations();
return { ...this };
}
}