seyfert/src/commands/handle.ts

894 lines
30 KiB
TypeScript

import {
BaseCommand,
Command,
type CommandAutocompleteOption,
CommandContext,
type CommandOption,
type ContextMenuCommand,
type ContextOptionsResolved,
type EntryPointCommand,
EntryPointContext,
IgnoreCommand,
MenuCommandContext,
type MessageCommandOptionErrors,
type RegisteredMiddlewares,
type SeyfertChannelOption,
type SeyfertIntegerOption,
type SeyfertNumberOption,
type SeyfertStringOption,
SubCommand,
type UsingClient,
} from '.';
import type { Client, WorkerClient } from '../client';
import { type MessageStructure, type OptionResolverStructure, Transformers } from '../client/transformers';
import type { MakeRequired } from '../common';
import { INTEGER_OPTION_VALUE_LIMIT } from '../common/it/constants';
import { ComponentContext, ModalContext } from '../components';
import {
AutocompleteInteraction,
BaseInteraction,
type ChatInputCommandInteraction,
type ComponentInteraction,
type EntryPointInteraction,
type MessageCommandInteraction,
type ModalSubmitInteraction,
type UserCommandInteraction,
type __InternalReplyFunction,
} from '../structures';
import type { PermissionsBitField } from '../structures/extra/Permissions';
import {
type APIApplicationCommandInteraction,
type APIApplicationCommandInteractionDataOption,
type APIInteraction,
type APIInteractionDataResolvedChannel,
ApplicationCommandOptionType,
ApplicationCommandType,
ChannelType,
type GatewayMessageCreateDispatchData,
InteractionContextType,
InteractionType,
} from '../types';
export type CommandOptionWithType = CommandOption & {
type: ApplicationCommandOptionType;
};
export interface CommandFromContent {
command?: Command | SubCommand;
parent?: Command;
fullCommandName: string;
}
export class HandleCommand {
constructor(public client: UsingClient) {}
async autocomplete(
interaction: AutocompleteInteraction,
optionsResolver: OptionResolverStructure,
command?: CommandAutocompleteOption,
) {
// idc, is a YOU problem
if (!command?.autocomplete) {
return this.client.logger.warn(
`${optionsResolver.fullCommandName} ${command?.name} command does not have 'autocomplete' callback`,
);
}
try {
try {
try {
await command.autocomplete(interaction);
} catch (error) {
if (!command.onAutocompleteError)
return this.client.logger.error(
`${optionsResolver.fullCommandName} ${command.name} just threw an error, ${
error ? (typeof error === 'object' && 'message' in error ? error.message : error) : 'Unknown'
}`,
);
await command.onAutocompleteError(interaction, error);
}
} catch (error) {
await optionsResolver.getCommand()?.onInternalError?.(this.client, optionsResolver.getCommand()!, error);
}
} catch (error) {
// pass
}
}
async contextMenu(
command: ContextMenuCommand,
interaction: MessageCommandInteraction | UserCommandInteraction,
context: MenuCommandContext<MessageCommandInteraction | UserCommandInteraction>,
) {
if (context.guildId && command.botPermissions) {
const permissions = this.checkPermissions(interaction.appPermissions, command.botPermissions);
if (permissions) return command.onBotPermissionsFail(context, permissions);
}
const resultGlobal = await this.runGlobalMiddlewares(command, context);
if (typeof resultGlobal === 'boolean') return;
const resultMiddle = await this.runMiddlewares(command, context);
if (typeof resultMiddle === 'boolean') return;
try {
try {
await command.run!(context);
await command.onAfterRun?.(context, undefined);
} catch (error) {
await command.onRunError(context, error);
await command.onAfterRun?.(context, error);
}
} catch (error) {
try {
await command.onInternalError(this.client, command, error);
} catch {
// pass
}
}
}
contextMenuMessage(
command: ContextMenuCommand,
interaction: MessageCommandInteraction,
context: MenuCommandContext<MessageCommandInteraction>,
) {
return this.contextMenu(command, interaction, context);
}
contextMenuUser(
command: ContextMenuCommand,
interaction: UserCommandInteraction,
context: MenuCommandContext<UserCommandInteraction>,
) {
return this.contextMenu(command, interaction, context);
}
async entryPoint(command: EntryPointCommand, interaction: EntryPointInteraction, context: EntryPointContext) {
if (context.guildId && command.botPermissions) {
const permissions = this.checkPermissions(interaction.appPermissions, command.botPermissions);
if (permissions) return command.onBotPermissionsFail(context, permissions);
}
const resultGlobal = await this.runGlobalMiddlewares(command, context);
if (typeof resultGlobal === 'boolean') return;
const resultMiddle = await this.runMiddlewares(command, context);
if (typeof resultMiddle === 'boolean') return;
try {
try {
await command.run!(context);
await command.onAfterRun?.(context, undefined);
} catch (error) {
await command.onRunError(context, error);
await command.onAfterRun?.(context, error);
}
} catch (error) {
try {
await command.onInternalError(this.client, command, error);
} catch {
// pass
}
}
}
async chatInput(
command: Command | SubCommand,
interaction: ChatInputCommandInteraction,
resolver: OptionResolverStructure,
context: CommandContext,
) {
if (context.guildId) {
if (command.botPermissions) {
const permissions = this.checkPermissions(interaction.appPermissions, command.botPermissions);
if (permissions) return command.onBotPermissionsFail?.(context, permissions);
}
if (command.defaultMemberPermissions) {
const permissions = this.checkPermissions(interaction.member!.permissions, command.defaultMemberPermissions);
if (permissions) return command.onPermissionsFail?.(context, permissions);
}
}
if (!(await this.runOptions(command, context, resolver))) return;
const resultGlobal = await this.runGlobalMiddlewares(command, context);
if (typeof resultGlobal === 'boolean') return;
const resultMiddle = await this.runMiddlewares(command, context);
if (typeof resultMiddle === 'boolean') return;
try {
try {
await command.run!(context);
await command.onAfterRun?.(context, undefined);
} catch (error) {
await command.onRunError?.(context, error);
await command.onAfterRun?.(context, error);
}
} catch (error) {
try {
await command.onInternalError?.(this.client, command, error);
} catch {
// pass
}
}
}
async modal(interaction: ModalSubmitInteraction) {
const context = new ModalContext(this.client, interaction);
const extended = this.client.options?.context?.(interaction) ?? {};
Object.assign(context, extended);
await this.client.components?.executeModal(context);
}
async messageComponent(interaction: ComponentInteraction) {
//@ts-expect-error
const context = new ComponentContext(this.client, interaction);
const extended = this.client.options?.context?.(interaction) ?? {};
Object.assign(context, extended);
await this.client.components?.executeComponent(context);
}
async interaction(body: APIInteraction, shardId: number, __reply?: __InternalReplyFunction) {
this.client.debugger?.debug(`[${InteractionType[body.type] ?? body.type}] Interaction received.`);
switch (body.type) {
case InteractionType.ApplicationCommandAutocomplete:
{
const optionsResolver = this.makeResolver(
this.client,
body.data.options ?? [],
this.getCommand<Command>(body.data),
body.guild_id,
body.data.resolved as ContextOptionsResolved,
);
const interaction = new AutocompleteInteraction(this.client, body, optionsResolver, __reply);
const command = optionsResolver.getAutocomplete();
await this.autocomplete(interaction, optionsResolver, command);
}
break;
case InteractionType.ApplicationCommand: {
switch (body.data.type) {
case ApplicationCommandType.Message: {
const data = this.makeMenuCommand(body, shardId, __reply);
if (!data) return;
await this.contextMenuMessage(
data.command,
data.interaction as MessageCommandInteraction,
data.context as MenuCommandContext<MessageCommandInteraction>,
);
break;
}
case ApplicationCommandType.User: {
const data = this.makeMenuCommand(body, shardId, __reply);
if (!data) return;
await this.contextMenuUser(
data.command,
data.interaction as UserCommandInteraction,
data.context as MenuCommandContext<UserCommandInteraction>,
);
break;
}
case ApplicationCommandType.PrimaryEntryPoint: {
const command = this.client.commands?.entryPoint;
if (!command?.run) return;
const interaction = BaseInteraction.from(this.client, body, __reply) as EntryPointInteraction;
const context = new EntryPointContext(this.client, interaction, shardId, command);
const extendContext = this.client.options?.context?.(interaction) ?? {};
Object.assign(context, extendContext);
await this.entryPoint(command, interaction, context);
break;
}
case ApplicationCommandType.ChatInput: {
const parentCommand = this.getCommand<Command>(body.data);
const optionsResolver = this.makeResolver(
this.client,
body.data.options ?? [],
parentCommand,
body.guild_id,
body.data.resolved as ContextOptionsResolved,
);
const command = optionsResolver.getCommand();
if (!command?.run)
return this.client.logger.warn(`${optionsResolver.fullCommandName} command does not have 'run' callback`);
const interaction = BaseInteraction.from(this.client, body, __reply) as ChatInputCommandInteraction;
const context = new CommandContext(this.client, interaction, optionsResolver, shardId, command);
const extendContext = this.client.options?.context?.(interaction) ?? {};
Object.assign(context, extendContext);
await this.chatInput(command, interaction, optionsResolver, context);
break;
}
}
break;
}
case InteractionType.ModalSubmit:
{
const interaction = BaseInteraction.from(this.client, body, __reply) as ModalSubmitInteraction;
if (this.client.components?.hasModal(interaction)) {
await this.client.components.onModalSubmit(interaction);
} else await this.modal(interaction);
}
break;
case InteractionType.MessageComponent:
{
const interaction = BaseInteraction.from(this.client, body, __reply) as ComponentInteraction;
if (this.client.components?.hasComponent(body.message.id, interaction.customId)) {
await this.client.components.onComponent(body.message.id, interaction);
} else await this.messageComponent(interaction);
}
break;
}
}
async message(rawMessage: GatewayMessageCreateDispatchData, shardId: number) {
const self = this.client as Client | WorkerClient;
if (!self.options.commands?.prefix) return;
const message = Transformers.Message(this.client, rawMessage);
const prefixes = (await self.options.commands.prefix(message)).sort((a, b) => b.length - a.length);
const prefix = prefixes.find(x => rawMessage.content.startsWith(x));
if (!(prefix !== undefined && rawMessage.content.startsWith(prefix))) return;
const content = rawMessage.content.slice(prefix.length).trimStart();
const { fullCommandName, command, parent, argsContent } = this.resolveCommandFromContent(
content,
prefix,
rawMessage,
);
if (!command || argsContent === undefined) return;
if (!command.run) return self.logger.warn(`${fullCommandName} command does not have 'run' callback`);
if (!(command.contexts.includes(InteractionContextType.BotDM) || rawMessage.guild_id)) return;
if (!command.contexts.includes(InteractionContextType.Guild) && rawMessage.guild_id) return;
if (command.guildId && !command.guildId?.includes(rawMessage.guild_id!)) return;
const resolved: MakeRequired<ContextOptionsResolved> = {
channels: {},
roles: {},
users: {},
members: {},
attachments: {},
};
const args = this.argsParser(argsContent, command, message);
const { options, errors } = await this.argsOptionsParser(command, rawMessage, args, resolved);
const optionsResolver = this.makeResolver(self, options, parent as Command, rawMessage.guild_id, resolved);
const context = new CommandContext(self, message, optionsResolver, shardId, command);
//@ts-expect-error
const extendContext = self.options?.context?.(message) ?? {};
Object.assign(context, extendContext);
try {
if (errors.length) {
return command.onOptionsError?.(
context,
Object.fromEntries(
errors.map(x => {
return [
x.name,
{
failed: true,
value: x.error,
parseError: x.fullError,
},
];
}),
),
);
}
if (rawMessage.guild_id) {
if (command.defaultMemberPermissions) {
const memberPermissions = await self.members.permissions(rawMessage.guild_id, rawMessage.author.id);
const permissions = this.checkPermissions(memberPermissions, command.defaultMemberPermissions);
const guild = await this.client.guilds.raw(rawMessage.guild_id);
if (permissions && guild.owner_id !== rawMessage.author.id) {
return command.onPermissionsFail?.(context, memberPermissions.keys(permissions));
}
}
if (command.botPermissions) {
const appPermissions = await self.members.permissions(rawMessage.guild_id, self.botId);
const permissions = this.checkPermissions(appPermissions, command.botPermissions);
if (permissions) {
return command.onBotPermissionsFail?.(context, permissions);
}
}
}
if (!(await this.runOptions(command, context, optionsResolver))) return;
const resultGlobal = await this.runGlobalMiddlewares(command, context);
if (typeof resultGlobal === 'boolean') return;
const resultMiddle = await this.runMiddlewares(command, context);
if (typeof resultMiddle === 'boolean') return;
try {
await command.run!(context);
await command.onAfterRun?.(context, undefined);
} catch (error) {
await command.onRunError?.(context, error);
await command.onAfterRun?.(context, error);
}
} catch (error) {
try {
await command.onInternalError?.(this.client, command, error);
} catch {
// http 418
}
}
}
argsParser(content: string, _command: SubCommand | Command, _message: MessageStructure): Record<string, string> {
const args: Record<string, string> = {};
for (const i of content.match(/-(.*?)(?=\s-|$)/gs) ?? []) {
args[i.slice(1).split(' ')[0]] = i.split(' ').slice(1).join(' ');
}
return args;
}
resolveCommandFromContent(
content: string,
_prefix: string,
_message: GatewayMessageCreateDispatchData,
): CommandFromContent & { argsContent?: string } {
const result = this.getCommandFromContent(
content
.split(' ')
.filter(x => x)
.slice(0, 3),
);
if (!result.command) return result;
let newContent = content;
for (const i of result.fullCommandName.split(' ')) {
newContent = newContent.slice(newContent.indexOf(i) + i.length);
}
return {
...result,
argsContent: newContent.slice(1),
};
}
getCommandFromContent(commandRaw: string[]): CommandFromContent {
const rawParentName = commandRaw[0];
const rawGroupName = commandRaw.length === 3 ? commandRaw[1] : undefined;
const rawSubcommandName = rawGroupName ? commandRaw[2] : commandRaw[1];
const parent = this.getParentMessageCommand(rawParentName);
const fullCommandName = `${rawParentName}${
rawGroupName ? ` ${rawGroupName} ${rawSubcommandName}` : `${rawSubcommandName ? ` ${rawSubcommandName}` : ''}`
}`;
if (!(parent instanceof Command)) return { fullCommandName };
if (rawGroupName && !parent.groups?.[rawGroupName] && !parent.groupsAliases?.[rawGroupName])
return this.getCommandFromContent([rawParentName, rawGroupName]);
if (
rawSubcommandName &&
!parent.options?.some(
x => x instanceof SubCommand && (x.name === rawSubcommandName || x.aliases?.includes(rawSubcommandName)),
)
)
return this.getCommandFromContent([rawParentName]);
const groupName = rawGroupName ? parent.groupsAliases?.[rawGroupName] || rawGroupName : undefined;
const command =
groupName || rawSubcommandName
? (parent.options?.find(opt => {
if (opt instanceof SubCommand) {
if (groupName) {
if (opt.group !== groupName) return false;
}
if (opt.group && !groupName) return false;
return rawSubcommandName === opt.name || opt.aliases?.includes(rawSubcommandName);
}
return false;
}) as SubCommand)
: parent;
return {
command,
fullCommandName,
parent,
};
}
makeResolver(...args: Parameters<(typeof Transformers)['OptionResolver']>) {
return Transformers.OptionResolver(...args);
}
getParentMessageCommand(rawParentName: string) {
return this.client.commands!.values.find(
x =>
(!('ignore' in x) || x.ignore !== IgnoreCommand.Message) &&
(x.name === rawParentName || ('aliases' in x ? x.aliases?.includes(rawParentName) : false)),
);
}
getCommand<T extends Command | ContextMenuCommand | EntryPointCommand>(data: {
guild_id?: string;
name: string;
}): T | undefined {
return this.client.commands!.values.find(command => {
if (data.guild_id) {
return command.guildId?.includes(data.guild_id) && command.name === data.name;
}
return command.name === data.name;
}) as T;
}
checkPermissions(app: PermissionsBitField, bot: bigint) {
if (app.has('Administrator')) return;
const permissions = app.missings(...app.values([bot]));
if (permissions.length) {
return app.keys(permissions);
}
return;
}
fetchChannel(_option: CommandOptionWithType, query: string) {
const id = query.match(/[0-9]{17,19}/g)?.[0];
if (id) return this.client.channels.raw(id);
return null;
}
fetchUser(_option: CommandOptionWithType, query: string) {
const id = query.match(/[0-9]{17,19}/g)?.[0];
if (id) return this.client.users.raw(id);
return null;
}
fetchMember(_option: CommandOptionWithType, query: string, guildId: string) {
const id = query.match(/[0-9]{17,19}/g)?.[0];
if (id) return this.client.members.raw(guildId, id);
return null;
}
fetchRole(_option: CommandOptionWithType, query: string, guildId?: string) {
const id = query.match(/[0-9]{17,19}/g)?.[0];
if (id && guildId) return this.client.roles.raw(guildId, id);
return null;
}
async runGlobalMiddlewares(
command: Command | ContextMenuCommand | SubCommand | EntryPointCommand,
context: CommandContext<{}, never> | MenuCommandContext<any> | EntryPointContext,
) {
try {
const resultRunGlobalMiddlewares = await BaseCommand.__runMiddlewares(
context,
(this.client.options?.globalMiddlewares ?? []) as keyof RegisteredMiddlewares,
true,
);
if (resultRunGlobalMiddlewares.pass) {
return false;
}
if ('error' in resultRunGlobalMiddlewares) {
await command.onMiddlewaresError?.(context as never, resultRunGlobalMiddlewares.error ?? 'Unknown error');
return false;
}
return resultRunGlobalMiddlewares;
} catch (e) {
try {
await command.onInternalError?.(this.client, command as never, e);
} catch {
// http 418
}
}
return false;
}
async runMiddlewares(
command: Command | ContextMenuCommand | SubCommand | EntryPointCommand,
context: CommandContext<{}, never> | MenuCommandContext<any> | EntryPointContext,
) {
try {
const resultRunMiddlewares = await BaseCommand.__runMiddlewares(
context,
command.middlewares as keyof RegisteredMiddlewares,
false,
);
if (resultRunMiddlewares.pass) {
return false;
}
if ('error' in resultRunMiddlewares) {
await command.onMiddlewaresError?.(context as never, resultRunMiddlewares.error ?? 'Unknown error');
return false;
}
return resultRunMiddlewares;
} catch (e) {
try {
await command.onInternalError?.(this.client, command as never, e);
} catch {
// http 418
}
}
return false;
}
makeMenuCommand(body: APIApplicationCommandInteraction, shardId: number, __reply?: __InternalReplyFunction) {
const command = this.getCommand<ContextMenuCommand>(body.data);
const interaction = BaseInteraction.from(this.client, body, __reply) as
| UserCommandInteraction
| MessageCommandInteraction;
// idc, is a YOU problem
if (!command?.run)
return this.client.logger.warn(`${command?.name ?? 'Unknown'} command does not have 'run' callback`);
const context = new MenuCommandContext(this.client, interaction, shardId, command);
const extendContext = this.client.options?.context?.(interaction) ?? {};
Object.assign(context, extendContext);
return { command, interaction, context };
}
async runOptions(command: Command | SubCommand, context: CommandContext, resolver: OptionResolverStructure) {
const [erroredOptions, result] = await command.__runOptions(context, resolver);
if (erroredOptions) {
try {
await command.onOptionsError?.(context, result);
} catch (e) {
try {
await command.onInternalError?.(this.client, command, e);
} catch {
// http 418
}
}
return false;
}
return true;
}
async argsOptionsParser(
command: Command | SubCommand,
message: GatewayMessageCreateDispatchData,
args: Record<string, string>,
resolved: MakeRequired<ContextOptionsResolved>,
) {
const options: APIApplicationCommandInteractionDataOption[] = [];
const errors: {
name: string;
error: string;
fullError: MessageCommandOptionErrors;
}[] = [];
let indexAttachment = -1;
for (const i of (command.options ?? []) as (CommandOption & {
type: ApplicationCommandOptionType;
})[]) {
try {
if (!args[i.name] && i.type !== ApplicationCommandOptionType.Attachment) continue;
let value: string | boolean | number | undefined;
switch (i.type) {
case ApplicationCommandOptionType.Attachment:
if (message.attachments[++indexAttachment]) {
value = message.attachments[indexAttachment].id;
resolved.attachments[value] = message.attachments[indexAttachment];
}
break;
case ApplicationCommandOptionType.Boolean:
value = ['yes', 'y', 'true', 'treu'].includes(args[i.name].toLowerCase());
break;
case ApplicationCommandOptionType.Channel:
{
const rawQuery =
message.content.match(/(?<=<#)[0-9]{17,19}(?=>)/g)?.find(x => args[i.name]?.includes(x)) ||
args[i.name];
if (!rawQuery) continue;
const channel =
(await this.client.cache.channels?.raw(rawQuery)) ?? (await this.fetchChannel(i, rawQuery));
if (!channel) break;
if ('channel_types' in i) {
if (!(i as SeyfertChannelOption).channel_types!.includes(channel.type)) {
errors.push({
name: i.name,
error: `The entered channel type is not one of ${(i as SeyfertChannelOption)
.channel_types!.map(t => ChannelType[t])
.join(', ')}`,
fullError: ['CHANNEL_TYPES', (i as SeyfertChannelOption).channel_types!],
});
break;
}
}
value = channel.id;
//discord funny memoentnt!!!!!!!!
resolved.channels[channel.id] = channel as APIInteractionDataResolvedChannel;
}
break;
case ApplicationCommandOptionType.Mentionable:
{
const matches = args[i.name]?.match(/<@[0-9]{17,19}(?=>)|<@&[0-9]{17,19}(?=>)/g) ?? [];
for (const match of matches) {
if (match.includes('&')) {
const rawId = match.slice(3);
if (rawId) {
const role =
(await this.client.cache.roles?.raw(rawId)) ?? (await this.fetchRole(i, rawId, message.guild_id));
if (role) {
value = rawId;
resolved.roles[rawId] = role;
break;
}
}
} else {
const rawId = match.slice(2);
const raw = message.mentions.find(x => rawId === x.id);
if (raw) {
const { member, ...user } = raw;
value = raw.id;
resolved.users[raw.id] = user;
if (member) resolved.members[raw.id] = member;
break;
}
}
}
}
break;
case ApplicationCommandOptionType.Role:
{
const rawQuery = message.mention_roles.find(x => args[i.name]?.includes(x)) || args[i.name];
if (!rawQuery) continue;
const role =
(await this.client.cache.roles?.raw(rawQuery)) ?? (await this.fetchRole(i, rawQuery, message.guild_id));
if (role) {
value = role.id;
resolved.roles[role.id] = role;
}
}
break;
case ApplicationCommandOptionType.User:
{
const rawQuery = message.mentions.find(x => args[i.name]?.includes(x.id))?.id || args[i.name];
if (!rawQuery) continue;
const raw =
message.mentions.find(x => args[i.name]?.includes(x.id)) ??
(await this.client.cache.users?.raw(rawQuery)) ??
(await this.fetchUser(i, rawQuery));
if (raw) {
value = raw.id;
resolved.users[raw.id] = raw;
if (message.guild_id) {
const member =
message.mentions.find(x => args[i.name]?.includes(x.id))?.member ??
(await this.client.cache.members?.raw(value, message.guild_id)) ??
(await this.fetchMember(i, value, message.guild_id));
if (member) resolved.members[value] = member;
}
}
}
break;
case ApplicationCommandOptionType.String:
{
const option = i as SeyfertStringOption;
if (option.choices?.length) {
const choice = option.choices.find(x => x.name === args[i.name]);
if (!choice) {
errors.push({
name: i.name,
error: `The entered choice is invalid. Please choose one of the following options: ${option.choices
.map(x => x.name)
.join(', ')}`,
fullError: ['STRING_INVALID_CHOICE', option.choices],
});
break;
}
value = choice.value;
break;
}
if (option.min_length !== undefined) {
if (args[i.name].length < option.min_length) {
errors.push({
name: i.name,
error: `The entered string has less than ${option.min_length} characters. The minimum required is ${option.min_length} characters`,
fullError: ['STRING_MIN_LENGTH', option.min_length],
});
break;
}
}
if (option.max_length !== undefined) {
if (args[i.name].length > option.max_length) {
errors.push({
name: i.name,
error: `The entered string has more than ${option.max_length} characters. The maximum required is ${option.max_length} characters`,
fullError: ['STRING_MAX_LENGTH', option.max_length],
});
break;
}
}
value = args[i.name];
}
break;
case ApplicationCommandOptionType.Number:
case ApplicationCommandOptionType.Integer:
{
const option = i as SeyfertNumberOption | SeyfertIntegerOption;
if (option.choices?.length) {
const choice = option.choices.find(x => x.name === args[i.name]);
if (!choice) {
errors.push({
name: i.name,
error: `The entered choice is invalid. Please choose one of the following options: ${option.choices
.map(x => x.name)
.join(', ')}`,
fullError: ['NUMBER_INVALID_CHOICE', option.choices],
});
break;
}
value = choice.value;
break;
}
value =
i.type === ApplicationCommandOptionType.Integer
? Math.trunc(Number(args[i.name]))
: Number(args[i.name]);
if (Number.isNaN(value)) {
value = undefined;
errors.push({
name: i.name,
error: 'The entered choice is an invalid number',
fullError: ['NUMBER_NAN', args[i.name]],
});
break;
}
if (value <= -INTEGER_OPTION_VALUE_LIMIT || value >= INTEGER_OPTION_VALUE_LIMIT) {
value = undefined;
errors.push({
name: i.name,
error: 'The entered number must be between -2^53 and 2^53',
fullError: ['NUMBER_OUT_OF_BOUNDS', INTEGER_OPTION_VALUE_LIMIT],
});
break;
}
if (option.min_value !== undefined) {
if (value < option.min_value) {
value = undefined;
errors.push({
name: i.name,
error: `The entered number is less than ${option.min_value}. The minimum allowed is ${option.min_value}`,
fullError: ['NUMBER_MIN_VALUE', option.min_value],
});
break;
}
}
if (option.max_value !== undefined) {
if (value > option.max_value) {
value = undefined;
errors.push({
name: i.name,
error: `The entered number is greater than ${option.max_value}. The maximum allowed is ${option.max_value}`,
fullError: ['NUMBER_MAX_VALUE', option.max_value],
});
break;
}
break;
}
}
break;
}
if (value !== undefined) {
options.push({
name: i.name,
type: i.type,
value,
} as APIApplicationCommandInteractionDataOption);
} else if (i.required)
if (!errors.some(x => x.name === i.name))
errors.push({
error: 'Option is required but returned undefined',
name: i.name,
fullError: ['OPTION_REQUIRED'],
});
} catch (e) {
errors.push({
error: e && typeof e === 'object' && 'message' in e ? (e.message as string) : `${e}`,
name: i.name,
fullError: ['UNKNOWN', e],
});
}
}
return { errors, options };
}
}