seyfert/src/commands/optionresolver.ts

244 lines
7.1 KiB
TypeScript

import { Attachment } from '../builders';
import {
type GuildMemberStructure,
type GuildRoleStructure,
type InteractionGuildMemberStructure,
Transformers,
type UserStructure,
} from '../client/transformers';
import type { MakeRequired } from '../common';
import { type AllChannels, channelFrom } from '../structures';
import {
type APIApplicationCommandInteractionDataOption,
type APIAttachment,
type APIGuildMember,
type APIInteractionDataResolvedChannel,
type APIInteractionGuildMember,
type APIRole,
type APIUser,
ApplicationCommandOptionType,
} from '../types';
import type { Command, CommandAutocompleteOption, CommandOption, SubCommand } from './applications/chat';
import type { UsingClient } from './applications/shared';
export type ContextOptionsResolved = {
members?: Record<string, APIGuildMember | Omit<APIGuildMember, 'user'> | APIInteractionGuildMember>;
users?: Record<string, APIUser>;
roles?: Record<string, APIRole>;
channels?: Record<string, APIInteractionDataResolvedChannel>;
attachments?: Record<string, APIAttachment>;
};
export class OptionResolver {
readonly options: OptionResolved[];
public hoistedOptions: OptionResolved[];
private subCommand: string | null = null;
private group: string | null = null;
constructor(
private client: UsingClient,
options: APIApplicationCommandInteractionDataOption[],
public parent?: Command,
public guildId?: string,
public resolved?: ContextOptionsResolved,
) {
this.hoistedOptions = this.options = options.map(option => this.transformOption(option, resolved));
if (this.hoistedOptions[0]?.type === ApplicationCommandOptionType.Subcommand) {
this.subCommand = this.hoistedOptions[0].name;
this.hoistedOptions = this.hoistedOptions[0].options ?? [];
}
if (this.hoistedOptions[0]?.type === ApplicationCommandOptionType.SubcommandGroup) {
this.group = this.hoistedOptions[0].name;
this.subCommand = this.hoistedOptions[0].options![0]!.name;
this.hoistedOptions = this.hoistedOptions[0].options![0].options ?? [];
}
}
get fullCommandName() {
return `${this.parent?.name}${
this.group ? ` ${this.group} ${this.subCommand}` : this.subCommand ? ` ${this.subCommand}` : ''
}`;
}
getCommand() {
if (this.subCommand) {
return (this.parent?.options as SubCommand[] | undefined)?.find(
x => (this.group ? x.group === this.group : true) && x.name === this.subCommand,
);
}
return this.parent;
}
getAutocompleteValue(): string | undefined {
return this.hoistedOptions.find(option => option.focused)?.value as string;
}
getAutocomplete() {
return (this.getCommand()?.options as CommandOption[] | undefined)?.find(
option => option.name === this.hoistedOptions.find(x => x.focused)?.name,
) as CommandAutocompleteOption | undefined;
}
getParent() {
return this.parent?.name;
}
getSubCommand() {
return this.subCommand;
}
getGroup() {
return this.group;
}
get(name: string) {
return this.options.find(opt => opt.name === name);
}
getHoisted(name: string) {
return this.hoistedOptions.find(x => x.name === name);
}
getValue(
name: string,
):
| AllChannels
| Attachment
| boolean
| number
| string
| UserStructure
| GuildMemberStructure
| InteractionGuildMemberStructure
| GuildRoleStructure
| undefined {
const option = this.getHoisted(name);
if (!option) {
return;
}
switch (option.type) {
case ApplicationCommandOptionType.Attachment:
return option.attachment!;
case ApplicationCommandOptionType.Boolean:
return option.value as boolean;
case ApplicationCommandOptionType.Channel:
return option.channel!;
case ApplicationCommandOptionType.Integer:
case ApplicationCommandOptionType.Number:
return option.value as number;
case ApplicationCommandOptionType.Role:
return option.role;
case ApplicationCommandOptionType.String:
return option.value as string;
case ApplicationCommandOptionType.User:
return option.member ?? option.user;
case ApplicationCommandOptionType.Mentionable:
return option.member ?? option.user ?? option.role;
default:
return;
}
}
private getTypedOption(name: string, allow: ApplicationCommandOptionType[]) {
const option = this.getHoisted(name);
if (!option) {
throw new Error('Bad Option');
}
if (!allow.includes(option.type)) {
throw new Error('Bad Option');
}
return option;
}
getChannel(name: string, required?: true): AllChannels;
getChannel(name: string): AllChannels | undefined {
const option = this.getTypedOption(name, [ApplicationCommandOptionType.Channel]);
return option.channel;
}
getString(name: string, required?: true): string;
getString(name: string): string | null {
const option = this.getTypedOption(name, [ApplicationCommandOptionType.String]);
return option.value as string;
}
transformOption(option: APIApplicationCommandInteractionDataOption, resolved?: ContextOptionsResolved) {
const resolve: OptionResolved = {
...option,
};
if ('value' in option) {
resolve.value = option.value;
}
if ('options' in option) {
resolve.options = option.options?.map(x => this.transformOption(x, resolved));
}
if (resolved) {
const value = resolve.value as string;
const user = resolved.users?.[value];
if (user) {
resolve.user = Transformers.User(this.client, user);
}
const member = resolved.members?.[value];
if (member) {
resolve.member =
'permissions' in member
? Transformers.InteractionGuildMember(this.client, member, user!, this.guildId!)
: Transformers.GuildMember(this.client, member, user!, this.guildId!);
}
const channel = resolved.channels?.[value];
if (channel) {
resolve.channel = channelFrom(channel, this.client);
}
const role = resolved.roles?.[value];
if (role) {
resolve.role = Transformers.GuildRole(this.client, role, this.guildId!);
}
const attachment = resolved.attachments?.[value];
if (attachment) {
resolve.attachment = attachment instanceof Attachment ? attachment : new Attachment(this.client, attachment);
}
}
return resolve;
}
}
export interface OptionResolved {
name: string;
type: ApplicationCommandOptionType;
value?: string | number | boolean;
options?: OptionResolved[];
user?: UserStructure;
member?: GuildMemberStructure | InteractionGuildMemberStructure;
attachment?: Attachment;
channel?: AllChannels;
role?: GuildRoleStructure;
focused?: boolean;
}
export type OptionResolvedWithValue = MakeRequired<Pick<OptionResolved, 'name' | 'value' | 'focused'>, 'value'> & {
type:
| ApplicationCommandOptionType.Boolean
| ApplicationCommandOptionType.Integer
| ApplicationCommandOptionType.Number
| ApplicationCommandOptionType.String;
};
export type OptionResolvedWithProp = Exclude<
OptionResolved,
{
type:
| ApplicationCommandOptionType.Boolean
| ApplicationCommandOptionType.Integer
| ApplicationCommandOptionType.Number
| ApplicationCommandOptionType.String;
}
>;