feat: extraProps

This commit is contained in:
MARCROCK22 2024-06-06 01:57:26 +00:00
parent db6134cf95
commit f3b8e3b67d
10 changed files with 992 additions and 968 deletions

View File

@ -1,474 +1,482 @@
import { join } from 'node:path'; import { join } from 'node:path';
import { ApiHandler, Router } from '../api'; import { ApiHandler, Router } from '../api';
import type { Adapter } from '../cache'; import type { Adapter } from '../cache';
import { Cache, MemoryAdapter } from '../cache'; import { Cache, MemoryAdapter } from '../cache';
import type { Command, CommandContext, OnOptionsReturnObject, RegisteredMiddlewares, UsingClient } from '../commands'; import type {
import { IgnoreCommand, type InferWithPrefix, type MiddlewareContext } from '../commands/applications/shared'; Command,
import { CommandHandler } from '../commands/handler'; CommandContext,
import { ExtraProps,
ChannelShorter, OnOptionsReturnObject,
EmojiShorter, RegisteredMiddlewares,
GuildShorter, UsingClient,
InteractionShorter, } from '../commands';
LogLevels, import { IgnoreCommand, type InferWithPrefix, type MiddlewareContext } from '../commands/applications/shared';
Logger, import { CommandHandler } from '../commands/handler';
MemberShorter, import {
MergeOptions, ChannelShorter,
MessageShorter, EmojiShorter,
ReactionShorter, GuildShorter,
RoleShorter, InteractionShorter,
TemplateShorter, LogLevels,
ThreadShorter, Logger,
UsersShorter, MemberShorter,
WebhookShorter, MergeOptions,
filterSplit, MessageShorter,
magicImport, ReactionShorter,
type MakeRequired, RoleShorter,
} from '../common'; TemplateShorter,
ThreadShorter,
import type { LocaleString, RESTPostAPIChannelMessageJSONBody } from 'discord-api-types/rest/v10'; UsersShorter,
import type { Awaitable, DeepPartial, IntentStrings, OmitInsert, PermissionStrings, When } from '../common/types/util'; WebhookShorter,
import { ComponentHandler } from '../components/handler'; filterSplit,
import { LangsHandler } from '../langs/handler'; magicImport,
import type { type MakeRequired,
ChatInputCommandInteraction, } from '../common';
ComponentInteraction,
Message, import type { LocaleString, RESTPostAPIChannelMessageJSONBody } from 'discord-api-types/rest/v10';
MessageCommandInteraction, import type { Awaitable, DeepPartial, IntentStrings, OmitInsert, PermissionStrings, When } from '../common/types/util';
ModalSubmitInteraction, import { ComponentHandler } from '../components/handler';
UserCommandInteraction, import { LangsHandler } from '../langs/handler';
} from '../structures'; import type {
import type { ComponentCommand, ComponentContext, ModalCommand, ModalContext } from '../components'; ChatInputCommandInteraction,
import { promises } from 'node:fs'; ComponentInteraction,
Message,
export class BaseClient { MessageCommandInteraction,
rest!: ApiHandler; ModalSubmitInteraction,
cache!: Cache; UserCommandInteraction,
} from '../structures';
users = new UsersShorter(this); import type { ComponentCommand, ComponentContext, ModalCommand, ModalContext } from '../components';
channels = new ChannelShorter(this); import { promises } from 'node:fs';
guilds = new GuildShorter(this);
messages = new MessageShorter(this); export class BaseClient {
members = new MemberShorter(this); rest!: ApiHandler;
webhooks = new WebhookShorter(this); cache!: Cache;
templates = new TemplateShorter(this);
roles = new RoleShorter(this); users = new UsersShorter(this);
reactions = new ReactionShorter(this); channels = new ChannelShorter(this);
emojis = new EmojiShorter(this); guilds = new GuildShorter(this);
threads = new ThreadShorter(this); messages = new MessageShorter(this);
interactions = new InteractionShorter(this); members = new MemberShorter(this);
webhooks = new WebhookShorter(this);
debugger?: Logger; templates = new TemplateShorter(this);
roles = new RoleShorter(this);
logger = new Logger({ reactions = new ReactionShorter(this);
name: '[Seyfert]', emojis = new EmojiShorter(this);
}); threads = new ThreadShorter(this);
interactions = new InteractionShorter(this);
langs? = new LangsHandler(this.logger);
commands? = new CommandHandler(this.logger, this); debugger?: Logger;
components? = new ComponentHandler(this.logger, this);
logger = new Logger({
private _applicationId?: string; name: '[Seyfert]',
private _botId?: string; });
middlewares?: Record<string, MiddlewareContext>;
langs? = new LangsHandler(this.logger);
protected static assertString(value: unknown, message?: string): asserts value is string { commands? = new CommandHandler(this.logger, this);
if (!(typeof value === 'string' && value !== '')) { components? = new ComponentHandler(this.logger, this);
throw new Error(message ?? 'Value is not a string');
} private _applicationId?: string;
} private _botId?: string;
middlewares?: Record<string, MiddlewareContext>;
protected static getBotIdFromToken(token: string): string {
return Buffer.from(token.split('.')[0], 'base64').toString('ascii'); protected static assertString(value: unknown, message?: string): asserts value is string {
} if (!(typeof value === 'string' && value !== '')) {
throw new Error(message ?? 'Value is not a string');
options: BaseClientOptions; }
}
/**@internal */
static _seyfertConfig?: InternalRuntimeConfigHTTP | InternalRuntimeConfig; protected static getBotIdFromToken(token: string): string {
return Buffer.from(token.split('.')[0], 'base64').toString('ascii');
constructor(options?: BaseClientOptions) { }
this.options = MergeOptions(
{ options: BaseClientOptions;
commands: {
defaults: { /**@internal */
onRunError(context: CommandContext<any>, error: unknown): any { static _seyfertConfig?: InternalRuntimeConfigHTTP | InternalRuntimeConfig;
context.client.logger.fatal(`${context.command.name}.<onRunError>`, context.author.id, error);
}, constructor(options?: BaseClientOptions) {
onOptionsError(context: CommandContext<{}, never>, metadata: OnOptionsReturnObject): any { this.options = MergeOptions(
context.client.logger.fatal(`${context.command.name}.<onOptionsError>`, context.author.id, metadata); {
}, commands: {
onMiddlewaresError(context: CommandContext<{}, never>, error: string): any { defaults: {
context.client.logger.fatal(`${context.command.name}.<onMiddlewaresError>`, context.author.id, error); onRunError(context: CommandContext<any>, error: unknown): any {
}, context.client.logger.fatal(`${context.command.name}.<onRunError>`, context.author.id, error);
onBotPermissionsFail(context: CommandContext<{}, never>, permissions: PermissionStrings): any { },
context.client.logger.fatal( onOptionsError(context: CommandContext<{}, never>, metadata: OnOptionsReturnObject): any {
`${context.command.name}.<onBotPermissionsFail>`, context.client.logger.fatal(`${context.command.name}.<onOptionsError>`, context.author.id, metadata);
context.author.id, },
permissions, onMiddlewaresError(context: CommandContext<{}, never>, error: string): any {
); context.client.logger.fatal(`${context.command.name}.<onMiddlewaresError>`, context.author.id, error);
}, },
onPermissionsFail(context: CommandContext<{}, never>, permissions: PermissionStrings): any { onBotPermissionsFail(context: CommandContext<{}, never>, permissions: PermissionStrings): any {
context.client.logger.fatal( context.client.logger.fatal(
`${context.command.name}.<onPermissionsFail>`, `${context.command.name}.<onBotPermissionsFail>`,
context.author.id, context.author.id,
permissions, permissions,
); );
}, },
onInternalError(client: UsingClient, command: Command, error?: unknown): any { onPermissionsFail(context: CommandContext<{}, never>, permissions: PermissionStrings): any {
client.logger.fatal(`${command.name}.<onInternalError>`, error); context.client.logger.fatal(
}, `${context.command.name}.<onPermissionsFail>`,
}, context.author.id,
}, permissions,
components: { );
defaults: { },
onRunError(context: ComponentContext, error: unknown): any { onInternalError(client: UsingClient, command: Command, error?: unknown): any {
context.client.logger.fatal('ComponentCommand.<onRunError>', context.author.id, error); client.logger.fatal(`${command.name}.<onInternalError>`, error);
}, },
onMiddlewaresError(context: ComponentContext, error: string): any { },
context.client.logger.fatal('ComponentCommand.<onMiddlewaresError>', context.author.id, error); },
}, components: {
onInternalError(client: UsingClient, error?: unknown): any { defaults: {
client.logger.fatal(error); onRunError(context: ComponentContext, error: unknown): any {
}, context.client.logger.fatal('ComponentCommand.<onRunError>', context.author.id, error);
}, },
}, onMiddlewaresError(context: ComponentContext, error: string): any {
modals: { context.client.logger.fatal('ComponentCommand.<onMiddlewaresError>', context.author.id, error);
defaults: { },
onRunError(context: ModalContext, error: unknown): any { onInternalError(client: UsingClient, error?: unknown): any {
context.client.logger.fatal('ComponentCommand.<onRunError>', context.author.id, error); client.logger.fatal(error);
}, },
onMiddlewaresError(context: ModalContext, error: string): any { },
context.client.logger.fatal('ComponentCommand.<onMiddlewaresError>', context.author.id, error); },
}, modals: {
onInternalError(client: UsingClient, error?: unknown): any { defaults: {
client.logger.fatal(error); onRunError(context: ModalContext, error: unknown): any {
}, context.client.logger.fatal('ComponentCommand.<onRunError>', context.author.id, error);
}, },
}, onMiddlewaresError(context: ModalContext, error: string): any {
} satisfies BaseClientOptions, context.client.logger.fatal('ComponentCommand.<onMiddlewaresError>', context.author.id, error);
options, },
); onInternalError(client: UsingClient, error?: unknown): any {
} client.logger.fatal(error);
},
set botId(id: string) { },
this._botId = id; },
} } satisfies BaseClientOptions,
options,
get botId() { );
return this._botId ?? BaseClient.getBotIdFromToken(this.rest.options.token); }
}
set botId(id: string) {
set applicationId(id: string) { this._botId = id;
this._applicationId = id; }
}
get botId() {
get applicationId() { return this._botId ?? BaseClient.getBotIdFromToken(this.rest.options.token);
return this._applicationId ?? this.botId; }
}
set applicationId(id: string) {
get proxy() { this._applicationId = id;
return new Router(this.rest).createProxy(); }
}
get applicationId() {
setServices({ rest, cache, langs, middlewares, handlers }: ServicesOptions) { return this._applicationId ?? this.botId;
if (rest) { }
this.rest = rest;
} get proxy() {
if (cache) { return new Router(this.rest).createProxy();
this.cache = new Cache( }
this.cache?.intents ?? 0,
cache?.adapter ?? this.cache?.adapter ?? new MemoryAdapter(), setServices({ rest, cache, langs, middlewares, handlers }: ServicesOptions) {
cache.disabledCache ?? this.cache?.disabledCache ?? [], if (rest) {
this, this.rest = rest;
); }
} if (cache) {
if (middlewares) { this.cache = new Cache(
this.middlewares = middlewares; this.cache?.intents ?? 0,
} cache?.adapter ?? this.cache?.adapter ?? new MemoryAdapter(),
if (handlers) { cache.disabledCache ?? this.cache?.disabledCache ?? [],
if ('components' in handlers) { this,
if (!handlers.components) { );
this.components = undefined; }
} else if (typeof handlers.components === 'function') { if (middlewares) {
this.components ??= new ComponentHandler(this.logger, this); this.middlewares = middlewares;
this.components.setHandlers({ callback: handlers.components }); }
} else { if (handlers) {
this.components = handlers.components; if ('components' in handlers) {
} if (!handlers.components) {
} this.components = undefined;
if ('commands' in handlers) { } else if (typeof handlers.components === 'function') {
if (!handlers.commands) { this.components ??= new ComponentHandler(this.logger, this);
this.commands = undefined; this.components.setHandlers({ callback: handlers.components });
} else if (typeof handlers.commands === 'object') { } else {
this.commands ??= new CommandHandler(this.logger, this); this.components = handlers.components;
this.commands.setHandlers(handlers.commands); }
} else { }
this.commands = handlers.commands; if ('commands' in handlers) {
} if (!handlers.commands) {
} this.commands = undefined;
if ('langs' in handlers) { } else if (typeof handlers.commands === 'object') {
if (!handlers.langs) { this.commands ??= new CommandHandler(this.logger, this);
this.langs = undefined; this.commands.setHandlers(handlers.commands);
} else if (typeof handlers.langs === 'function') { } else {
this.langs ??= new LangsHandler(this.logger); this.commands = handlers.commands;
this.langs.setHandlers({ callback: handlers.langs }); }
} else { }
this.langs = handlers.langs; if ('langs' in handlers) {
} if (!handlers.langs) {
} this.langs = undefined;
} } else if (typeof handlers.langs === 'function') {
if (langs) { this.langs ??= new LangsHandler(this.logger);
if (langs.default) this.langs!.defaultLang = langs.default; this.langs.setHandlers({ callback: handlers.langs });
if (langs.aliases) this.langs!.aliases = Object.entries(langs.aliases); } else {
} this.langs = handlers.langs;
} }
}
protected async execute(..._options: unknown[]) { }
if ((await this.getRC()).debug) { if (langs) {
this.debugger = new Logger({ if (langs.default) this.langs!.defaultLang = langs.default;
name: '[Debug]', if (langs.aliases) this.langs!.aliases = Object.entries(langs.aliases);
logLevel: LogLevels.Debug, }
}); }
}
} protected async execute(..._options: unknown[]) {
if ((await this.getRC()).debug) {
async start( this.debugger = new Logger({
options: Pick<DeepPartial<StartOptions>, 'langsDir' | 'commandsDir' | 'connection' | 'token' | 'componentsDir'> = { name: '[Debug]',
token: undefined, logLevel: LogLevels.Debug,
langsDir: undefined, });
commandsDir: undefined, }
connection: undefined, }
componentsDir: undefined,
}, async start(
) { options: Pick<DeepPartial<StartOptions>, 'langsDir' | 'commandsDir' | 'connection' | 'token' | 'componentsDir'> = {
await this.loadLangs(options.langsDir); token: undefined,
await this.loadCommands(options.commandsDir); langsDir: undefined,
await this.loadComponents(options.componentsDir); commandsDir: undefined,
connection: undefined,
const { token: tokenRC } = await this.getRC(); componentsDir: undefined,
const token = options?.token ?? tokenRC; },
) {
if (!this.rest) { await this.loadLangs(options.langsDir);
BaseClient.assertString(token, 'token is not a string'); await this.loadCommands(options.commandsDir);
this.rest = new ApiHandler({ await this.loadComponents(options.componentsDir);
token,
baseUrl: 'api/v10', const { token: tokenRC } = await this.getRC();
domain: 'https://discord.com', const token = options?.token ?? tokenRC;
debug: (await this.getRC()).debug,
}); if (!this.rest) {
} BaseClient.assertString(token, 'token is not a string');
this.rest = new ApiHandler({
if (this.cache) { token,
this.cache.__setClient(this); baseUrl: 'api/v10',
} else { domain: 'https://discord.com',
this.cache = new Cache(0, new MemoryAdapter(), [], this); debug: (await this.getRC()).debug,
} });
} }
protected async onPacket(..._packet: unknown[]) { if (this.cache) {
throw new Error('Function not implemented'); this.cache.__setClient(this);
} } else {
this.cache = new Cache(0, new MemoryAdapter(), [], this);
shouldUploadCommands(cachePath: string) { }
return this.commands!.shouldUpload(cachePath).then(async should => { }
if (should) await promises.writeFile(cachePath, JSON.stringify(this.commands!.values.map(x => x.toJSON())));
return should; protected async onPacket(..._packet: unknown[]) {
}); throw new Error('Function not implemented');
} }
async uploadCommands(applicationId?: string) { shouldUploadCommands(cachePath: string) {
applicationId ??= await this.getRC().then(x => x.applicationId ?? this.applicationId); return this.commands!.shouldUpload(cachePath).then(async should => {
BaseClient.assertString(applicationId, 'applicationId is not a string'); if (should) await promises.writeFile(cachePath, JSON.stringify(this.commands!.values.map(x => x.toJSON())));
return should;
const commands = this.commands!.values; });
const filter = filterSplit(commands, command => !command.guildId); }
await this.proxy.applications(applicationId).commands.put({ async uploadCommands(applicationId?: string) {
body: filter.expect.filter(cmd => !('ignore' in cmd) || cmd.ignore !== IgnoreCommand.Slash).map(x => x.toJSON()), applicationId ??= await this.getRC().then(x => x.applicationId ?? this.applicationId);
}); BaseClient.assertString(applicationId, 'applicationId is not a string');
const guilds = new Set<string>(); const commands = this.commands!.values;
const filter = filterSplit(commands, command => !command.guildId);
for (const command of filter.never) {
for (const guild_id of command.guildId!) { await this.proxy.applications(applicationId).commands.put({
guilds.add(guild_id); body: filter.expect.filter(cmd => !('ignore' in cmd) || cmd.ignore !== IgnoreCommand.Slash).map(x => x.toJSON()),
} });
}
const guilds = new Set<string>();
for (const guild of guilds) {
await this.proxy for (const command of filter.never) {
.applications(applicationId) for (const guild_id of command.guildId!) {
.guilds(guild) guilds.add(guild_id);
.commands.put({ }
body: filter.never }
.filter(cmd => cmd.guildId?.includes(guild) && (!('ignore' in cmd) || cmd.ignore !== IgnoreCommand.Slash))
.map(x => x.toJSON()), for (const guild of guilds) {
}); await this.proxy
} .applications(applicationId)
} .guilds(guild)
.commands.put({
async loadCommands(dir?: string) { body: filter.never
dir ??= await this.getRC().then(x => x.commands); .filter(cmd => cmd.guildId?.includes(guild) && (!('ignore' in cmd) || cmd.ignore !== IgnoreCommand.Slash))
if (dir && this.commands) { .map(x => x.toJSON()),
await this.commands.load(dir, this); });
this.logger.info('CommandHandler loaded'); }
} }
}
async loadCommands(dir?: string) {
async loadComponents(dir?: string) { dir ??= await this.getRC().then(x => x.commands);
dir ??= await this.getRC().then(x => x.components); if (dir && this.commands) {
if (dir && this.components) { await this.commands.load(dir, this);
await this.components.load(dir); this.logger.info('CommandHandler loaded');
this.logger.info('ComponentHandler loaded'); }
} }
}
async loadComponents(dir?: string) {
async loadLangs(dir?: string) { dir ??= await this.getRC().then(x => x.components);
dir ??= await this.getRC().then(x => x.langs); if (dir && this.components) {
if (dir && this.langs) { await this.components.load(dir);
await this.langs.load(dir); this.logger.info('ComponentHandler loaded');
this.logger.info('LangsHandler loaded'); }
} }
}
async loadLangs(dir?: string) {
t(locale: string) { dir ??= await this.getRC().then(x => x.langs);
return this.langs!.get(locale); if (dir && this.langs) {
} await this.langs.load(dir);
this.logger.info('LangsHandler loaded');
async getRC< }
T extends InternalRuntimeConfigHTTP | InternalRuntimeConfig = InternalRuntimeConfigHTTP | InternalRuntimeConfig, }
>() {
const seyfertConfig = (BaseClient._seyfertConfig || t(locale: string) {
(await this.options.getRC?.()) || return this.langs!.get(locale);
(await magicImport(join(process.cwd(), 'seyfert.config.js')).then(x => x.default ?? x))) as T; }
const { locations, debug, ...env } = seyfertConfig; async getRC<
T extends InternalRuntimeConfigHTTP | InternalRuntimeConfig = InternalRuntimeConfigHTTP | InternalRuntimeConfig,
const obj = { >() {
debug: !!debug, const seyfertConfig = (BaseClient._seyfertConfig ||
...env, (await this.options.getRC?.()) ||
templates: locations.templates ? join(process.cwd(), locations.base, locations.templates) : undefined, (await magicImport(join(process.cwd(), 'seyfert.config.js')).then(x => x.default ?? x))) as T;
langs: locations.langs ? join(process.cwd(), locations.output, locations.langs) : undefined,
events: const { locations, debug, ...env } = seyfertConfig;
'events' in locations && locations.events ? join(process.cwd(), locations.output, locations.events) : undefined,
components: locations.components ? join(process.cwd(), locations.output, locations.components) : undefined, const obj = {
commands: locations.commands ? join(process.cwd(), locations.output, locations.commands) : undefined, debug: !!debug,
base: join(process.cwd(), locations.base), ...env,
output: join(process.cwd(), locations.output), templates: locations.templates ? join(process.cwd(), locations.base, locations.templates) : undefined,
}; langs: locations.langs ? join(process.cwd(), locations.output, locations.langs) : undefined,
events:
BaseClient._seyfertConfig = seyfertConfig; 'events' in locations && locations.events ? join(process.cwd(), locations.output, locations.events) : undefined,
components: locations.components ? join(process.cwd(), locations.output, locations.components) : undefined,
return obj; commands: locations.commands ? join(process.cwd(), locations.output, locations.commands) : undefined,
} base: join(process.cwd(), locations.base),
} output: join(process.cwd(), locations.output),
};
export interface BaseClientOptions {
context?: ( BaseClient._seyfertConfig = seyfertConfig;
interaction:
| ChatInputCommandInteraction<boolean> return obj;
| UserCommandInteraction<boolean> }
| MessageCommandInteraction<boolean> }
| ComponentInteraction
| ModalSubmitInteraction export interface BaseClientOptions {
| When<InferWithPrefix, Message, never>, context?: (
) => {}; interaction:
globalMiddlewares?: readonly (keyof RegisteredMiddlewares)[]; | ChatInputCommandInteraction<boolean>
commands?: { | UserCommandInteraction<boolean>
defaults?: { | MessageCommandInteraction<boolean>
onRunError?: Command['onRunError']; | ComponentInteraction
onPermissionsFail?: Command['onPermissionsFail']; | ModalSubmitInteraction
onBotPermissionsFail?: Command['onBotPermissionsFail']; | When<InferWithPrefix, Message, never>,
onInternalError?: Command['onInternalError']; ) => {};
onMiddlewaresError?: Command['onMiddlewaresError']; globalMiddlewares?: readonly (keyof RegisteredMiddlewares)[];
onOptionsError?: Command['onOptionsError']; commands?: {
onAfterRun?: Command['onAfterRun']; defaults?: {
}; onRunError?: Command['onRunError'];
}; onPermissionsFail?: Command['onPermissionsFail'];
components?: { onBotPermissionsFail?: Command['onBotPermissionsFail'];
defaults?: { onInternalError?: Command['onInternalError'];
onRunError?: ComponentCommand['onRunError']; onMiddlewaresError?: Command['onMiddlewaresError'];
onInternalError?: ComponentCommand['onInternalError']; onOptionsError?: Command['onOptionsError'];
onMiddlewaresError?: ComponentCommand['onMiddlewaresError']; onAfterRun?: Command['onAfterRun'];
onAfterRun?: ComponentCommand['onAfterRun']; props?: ExtraProps;
}; };
}; };
modals?: { components?: {
defaults?: { defaults?: {
onRunError?: ModalCommand['onRunError']; onRunError?: ComponentCommand['onRunError'];
onInternalError?: ModalCommand['onInternalError']; onInternalError?: ComponentCommand['onInternalError'];
onMiddlewaresError?: ModalCommand['onMiddlewaresError']; onMiddlewaresError?: ComponentCommand['onMiddlewaresError'];
onAfterRun?: ModalCommand['onAfterRun']; onAfterRun?: ComponentCommand['onAfterRun'];
}; };
}; };
allowedMentions?: Omit<NonNullable<RESTPostAPIChannelMessageJSONBody['allowed_mentions']>, 'parse'> & { modals?: {
parse?: ('everyone' | 'roles' | 'users')[]; //nice types, d-api defaults?: {
}; onRunError?: ModalCommand['onRunError'];
getRC?(): Awaitable<InternalRuntimeConfig | InternalRuntimeConfigHTTP>; onInternalError?: ModalCommand['onInternalError'];
} onMiddlewaresError?: ModalCommand['onMiddlewaresError'];
onAfterRun?: ModalCommand['onAfterRun'];
export interface StartOptions { };
eventsDir: string; };
langsDir: string; allowedMentions?: Omit<NonNullable<RESTPostAPIChannelMessageJSONBody['allowed_mentions']>, 'parse'> & {
commandsDir: string; parse?: ('everyone' | 'roles' | 'users')[]; //nice types, d-api
componentsDir: string; };
connection: { intents: number }; getRC?(): Awaitable<InternalRuntimeConfig | InternalRuntimeConfigHTTP>;
httpConnection: { }
publicKey: string;
port: number; export interface StartOptions {
useUWS: boolean; eventsDir: string;
}; langsDir: string;
token: string; commandsDir: string;
} componentsDir: string;
connection: { intents: number };
interface RC extends Variables { httpConnection: {
debug?: boolean; publicKey: string;
locations: { port: number;
base: string; useUWS: boolean;
output: string; };
commands?: string; token: string;
langs?: string; }
templates?: string;
events?: string; interface RC extends Variables {
components?: string; debug?: boolean;
}; locations: {
} base: string;
output: string;
export interface Variables { commands?: string;
token: string; langs?: string;
intents?: number; templates?: string;
applicationId?: string; events?: string;
port?: number; components?: string;
publicKey?: string; };
} }
export type InternalRuntimeConfigHTTP = Omit< export interface Variables {
MakeRequired<RC, 'publicKey' | 'port' | 'applicationId'>, token: string;
'intents' | 'locations' intents?: number;
> & { locations: Omit<RC['locations'], 'events'> }; applicationId?: string;
export type RuntimeConfigHTTP = Omit<MakeRequired<RC, 'publicKey' | 'applicationId'>, 'intents' | 'locations'> & { port?: number;
locations: Omit<RC['locations'], 'events'>; publicKey?: string;
}; }
export type InternalRuntimeConfig = Omit<MakeRequired<RC, 'intents'>, 'publicKey' | 'port'>; export type InternalRuntimeConfigHTTP = Omit<
export type RuntimeConfig = OmitInsert< MakeRequired<RC, 'publicKey' | 'port' | 'applicationId'>,
InternalRuntimeConfig, 'intents' | 'locations'
'intents', > & { locations: Omit<RC['locations'], 'events'> };
{ intents?: IntentStrings | number[] | number } export type RuntimeConfigHTTP = Omit<MakeRequired<RC, 'publicKey' | 'applicationId'>, 'intents' | 'locations'> & {
>; locations: Omit<RC['locations'], 'events'>;
};
export interface ServicesOptions {
rest?: ApiHandler; export type InternalRuntimeConfig = Omit<MakeRequired<RC, 'intents'>, 'publicKey' | 'port'>;
cache?: { adapter?: Adapter; disabledCache?: Cache['disabledCache'] }; export type RuntimeConfig = OmitInsert<
langs?: { InternalRuntimeConfig,
default?: string; 'intents',
aliases?: Record<string, LocaleString[]>; { intents?: IntentStrings | number[] | number }
}; >;
middlewares?: Record<string, MiddlewareContext>;
handlers?: { export interface ServicesOptions {
components?: ComponentHandler | ComponentHandler['callback']; rest?: ApiHandler;
commands?: CommandHandler | Parameters<CommandHandler['setHandlers']>[0]; cache?: { adapter?: Adapter; disabledCache?: Cache['disabledCache'] };
langs?: LangsHandler | LangsHandler['callback']; langs?: {
}; default?: string;
} aliases?: Record<string, LocaleString[]>;
};
middlewares?: Record<string, MiddlewareContext>;
handlers?: {
components?: ComponentHandler | ComponentHandler['callback'];
commands?: CommandHandler | Parameters<CommandHandler['setHandlers']>[0];
langs?: LangsHandler | LangsHandler['callback'];
};
}

View File

@ -24,6 +24,7 @@ import type { OptionResolver } from '../optionresolver';
import type { CommandContext } from './chatcontext'; import type { CommandContext } from './chatcontext';
import type { import type {
DefaultLocale, DefaultLocale,
ExtraProps,
IgnoreCommand, IgnoreCommand,
OKFunction, OKFunction,
OnOptionsReturnObject, OnOptionsReturnObject,
@ -138,6 +139,8 @@ export class BaseCommand {
aliases?: string[]; aliases?: string[];
props: ExtraProps = {};
/** @internal */ /** @internal */
async __runOptions( async __runOptions(
ctx: CommandContext<{}, never>, ctx: CommandContext<{}, never>,

View File

@ -7,7 +7,7 @@ import type {
import { magicImport, type PermissionStrings } from '../../common'; import { magicImport, type PermissionStrings } from '../../common';
import type { RegisteredMiddlewares } from '../decorators'; import type { RegisteredMiddlewares } from '../decorators';
import type { MenuCommandContext } from './menucontext'; import type { MenuCommandContext } from './menucontext';
import type { UsingClient } from './shared'; import type { ExtraProps, UsingClient } from './shared';
export abstract class ContextMenuCommand { export abstract class ContextMenuCommand {
middlewares: (keyof RegisteredMiddlewares)[] = []; middlewares: (keyof RegisteredMiddlewares)[] = [];
@ -28,6 +28,8 @@ export abstract class ContextMenuCommand {
name_localizations?: Partial<Record<LocaleString, string>>; name_localizations?: Partial<Record<LocaleString, string>>;
description_localizations?: Partial<Record<LocaleString, string>>; description_localizations?: Partial<Record<LocaleString, string>>;
props: ExtraProps = {};
toJSON() { toJSON() {
return { return {
name: this.name, name: this.name,

View File

@ -13,6 +13,7 @@ export type InferWithPrefix = InternalOptions extends { withPrefix: infer P } ?
export interface GlobalMetadata {} export interface GlobalMetadata {}
export interface DefaultLocale {} export interface DefaultLocale {}
export interface ExtendContext {} export interface ExtendContext {}
export interface ExtraProps {}
export interface UsingClient extends BaseClient {} export interface UsingClient extends BaseClient {}
export type ParseClient<T extends BaseClient> = T; export type ParseClient<T extends BaseClient> = T;
export interface InternalOptions {} export interface InternalOptions {}

View File

@ -7,7 +7,7 @@ import {
} from 'discord-api-types/v10'; } from 'discord-api-types/v10';
import type { FlatObjectKeys, PermissionStrings } from '../common'; import type { FlatObjectKeys, PermissionStrings } from '../common';
import type { CommandOption, OptionsRecord, SubCommand } from './applications/chat'; import type { CommandOption, OptionsRecord, SubCommand } from './applications/chat';
import type { DefaultLocale, IgnoreCommand, MiddlewareContext } from './applications/shared'; import type { DefaultLocale, ExtraProps, IgnoreCommand, MiddlewareContext } from './applications/shared';
export interface RegisteredMiddlewares {} export interface RegisteredMiddlewares {}
@ -23,6 +23,7 @@ type DeclareOptions =
contexts?: (keyof typeof InteractionContextType)[]; contexts?: (keyof typeof InteractionContextType)[];
ignore?: IgnoreCommand; ignore?: IgnoreCommand;
aliases?: string[]; aliases?: string[];
props?: ExtraProps;
} }
| (Omit< | (Omit<
{ {
@ -34,6 +35,7 @@ type DeclareOptions =
nsfw?: boolean; nsfw?: boolean;
integrationTypes?: (keyof typeof ApplicationIntegrationType)[]; integrationTypes?: (keyof typeof ApplicationIntegrationType)[];
contexts?: (keyof typeof InteractionContextType)[]; contexts?: (keyof typeof InteractionContextType)[];
props?: ExtraProps;
}, },
'type' | 'description' 'type' | 'description'
> & { > & {
@ -157,6 +159,7 @@ export function Declare(declare: DeclareOptions) {
class extends target { class extends target {
name = declare.name; name = declare.name;
nsfw = declare.nsfw; nsfw = declare.nsfw;
props = declare.props;
contexts = contexts =
declare.contexts?.map(i => InteractionContextType[i]) ?? declare.contexts?.map(i => InteractionContextType[i]) ??
Object.values(InteractionContextType).filter(x => typeof x === 'number'); Object.values(InteractionContextType).filter(x => typeof x === 'number');

View File

@ -214,6 +214,8 @@ export class CommandHandler extends BaseHandler {
commandInstance.onRunError ??= client.options?.commands?.defaults?.onRunError; commandInstance.onRunError ??= client.options?.commands?.defaults?.onRunError;
commandInstance.__filePath = command.path; commandInstance.__filePath = command.path;
commandInstance.options ??= [] as NonNullable<Command['options']>; commandInstance.options ??= [] as NonNullable<Command['options']>;
console.log(commandInstance, commandInstance.props);
commandInstance.props ??= client.options.commands?.defaults?.props ?? {};
if (commandInstance.__autoload) { if (commandInstance.__autoload) {
//@AutoLoad //@AutoLoad
const options = await this.getFiles(dirname(command.path)); const options = await this.getFiles(dirname(command.path));
@ -239,35 +241,36 @@ export class CommandHandler extends BaseHandler {
option.onMiddlewaresError = option.onMiddlewaresError =
option.onMiddlewaresError?.bind(option) ?? option.onMiddlewaresError?.bind(option) ??
commandInstance.onMiddlewaresError?.bind(commandInstance) ?? commandInstance.onMiddlewaresError?.bind(commandInstance) ??
this.client.options?.commands?.defaults?.onMiddlewaresError; this.client.options.commands?.defaults?.onMiddlewaresError;
option.onRunError = option.onRunError =
option.onRunError?.bind(option) ?? option.onRunError?.bind(option) ??
commandInstance.onRunError?.bind(commandInstance) ?? commandInstance.onRunError?.bind(commandInstance) ??
this.client.options?.commands?.defaults?.onRunError; this.client.options.commands?.defaults?.onRunError;
option.onOptionsError = option.onOptionsError =
option.onOptionsError?.bind(option) ?? option.onOptionsError?.bind(option) ??
commandInstance.onOptionsError?.bind(commandInstance) ?? commandInstance.onOptionsError?.bind(commandInstance) ??
this.client.options?.commands?.defaults?.onOptionsError; this.client.options.commands?.defaults?.onOptionsError;
option.onInternalError = option.onInternalError =
option.onInternalError?.bind(option) ?? option.onInternalError?.bind(option) ??
commandInstance.onInternalError?.bind(commandInstance) ?? commandInstance.onInternalError?.bind(commandInstance) ??
this.client.options?.commands?.defaults?.onInternalError; this.client.options.commands?.defaults?.onInternalError;
option.onAfterRun = option.onAfterRun =
option.onAfterRun?.bind(option) ?? option.onAfterRun?.bind(option) ??
commandInstance.onAfterRun?.bind(commandInstance) ?? commandInstance.onAfterRun?.bind(commandInstance) ??
this.client.options?.commands?.defaults?.onAfterRun; this.client.options.commands?.defaults?.onAfterRun;
option.onBotPermissionsFail = option.onBotPermissionsFail =
option.onBotPermissionsFail?.bind(option) ?? option.onBotPermissionsFail?.bind(option) ??
commandInstance.onBotPermissionsFail?.bind(commandInstance) ?? commandInstance.onBotPermissionsFail?.bind(commandInstance) ??
this.client.options?.commands?.defaults?.onBotPermissionsFail; this.client.options.commands?.defaults?.onBotPermissionsFail;
option.onPermissionsFail = option.onPermissionsFail =
option.onPermissionsFail?.bind(option) ?? option.onPermissionsFail?.bind(option) ??
commandInstance.onPermissionsFail?.bind(commandInstance) ?? commandInstance.onPermissionsFail?.bind(commandInstance) ??
this.client.options?.commands?.defaults?.onPermissionsFail; this.client.options.commands?.defaults?.onPermissionsFail;
option.botPermissions ??= commandInstance.botPermissions; option.botPermissions ??= commandInstance.botPermissions;
option.defaultMemberPermissions ??= commandInstance.defaultMemberPermissions; option.defaultMemberPermissions ??= commandInstance.defaultMemberPermissions;
option.contexts ??= commandInstance.contexts; option.contexts ??= commandInstance.contexts;
option.integrationTypes ??= commandInstance.integrationTypes; option.integrationTypes ??= commandInstance.integrationTypes;
option.props ??= commandInstance.props;
} }
} }

View File

@ -1,30 +1,32 @@
import { ComponentType } from 'discord-api-types/v10'; import { ComponentType } from 'discord-api-types/v10';
import type { ContextComponentCommandInteractionMap, ComponentContext } from './componentcontext'; import type { ContextComponentCommandInteractionMap, ComponentContext } from './componentcontext';
import type { RegisteredMiddlewares, UsingClient } from '../commands'; import type { ExtraProps, RegisteredMiddlewares, UsingClient } from '../commands';
export const InteractionCommandType = { export const InteractionCommandType = {
COMPONENT: 0, COMPONENT: 0,
MODAL: 1, MODAL: 1,
} as const; } as const;
export interface ComponentCommand { export interface ComponentCommand {
__filePath?: string; __filePath?: string;
} }
export abstract class ComponentCommand { export abstract class ComponentCommand {
type = InteractionCommandType.COMPONENT; type = InteractionCommandType.COMPONENT;
abstract componentType: keyof ContextComponentCommandInteractionMap; abstract componentType: keyof ContextComponentCommandInteractionMap;
abstract filter(context: ComponentContext<typeof this.componentType>): Promise<boolean> | boolean; abstract filter(context: ComponentContext<typeof this.componentType>): Promise<boolean> | boolean;
abstract run(context: ComponentContext<typeof this.componentType>): any; abstract run(context: ComponentContext<typeof this.componentType>): any;
get cType(): number { middlewares: (keyof RegisteredMiddlewares)[] = [];
return ComponentType[this.componentType];
} props: ExtraProps = {};
onAfterRun?(context: ComponentContext, error: unknown | undefined): any; get cType(): number {
onRunError?(context: ComponentContext, error: unknown): any; return ComponentType[this.componentType];
onMiddlewaresError?(context: ComponentContext, error: string): any; }
onInternalError?(client: UsingClient, error?: unknown): any;
onAfterRun?(context: ComponentContext, error: unknown | undefined): any;
middlewares: (keyof RegisteredMiddlewares)[] = []; onRunError?(context: ComponentContext, error: unknown): any;
} onMiddlewaresError?(context: ComponentContext, error: string): any;
onInternalError?(client: UsingClient, error?: unknown): any;
}

View File

@ -1,243 +1,243 @@
import { ComponentType, MessageFlags } from 'discord-api-types/v10'; import { ComponentType, MessageFlags } from 'discord-api-types/v10';
import type { import type {
AllChannels, AllChannels,
ButtonInteraction, ButtonInteraction,
ChannelSelectMenuInteraction, ChannelSelectMenuInteraction,
ComponentCommand, ComponentCommand,
Guild, Guild,
GuildMember, GuildMember,
MentionableSelectMenuInteraction, MentionableSelectMenuInteraction,
Message, Message,
ReturnCache, ReturnCache,
RoleSelectMenuInteraction, RoleSelectMenuInteraction,
StringSelectMenuInteraction, StringSelectMenuInteraction,
UserSelectMenuInteraction, UserSelectMenuInteraction,
WebhookMessage, WebhookMessage,
} from '..'; } from '..';
import type { CommandMetadata, ExtendContext, GlobalMetadata, RegisteredMiddlewares, UsingClient } from '../commands'; import type { CommandMetadata, ExtendContext, GlobalMetadata, RegisteredMiddlewares, UsingClient } from '../commands';
import { BaseContext } from '../commands/basecontext'; import { BaseContext } from '../commands/basecontext';
import type { import type {
ComponentInteractionMessageUpdate, ComponentInteractionMessageUpdate,
InteractionCreateBodyRequest, InteractionCreateBodyRequest,
InteractionMessageUpdateBodyRequest, InteractionMessageUpdateBodyRequest,
ModalCreateBodyRequest, ModalCreateBodyRequest,
UnionToTuple, UnionToTuple,
When, When,
} from '../common'; } from '../common';
export interface ComponentContext< export interface ComponentContext<
Type extends keyof ContextComponentCommandInteractionMap = keyof ContextComponentCommandInteractionMap, Type extends keyof ContextComponentCommandInteractionMap = keyof ContextComponentCommandInteractionMap,
> extends BaseContext, > extends BaseContext,
ExtendContext {} ExtendContext {}
/** /**
* Represents a context for interacting with components in a Discord bot. * Represents a context for interacting with components in a Discord bot.
* @template Type - The type of component interaction. * @template Type - The type of component interaction.
*/ */
export class ComponentContext< export class ComponentContext<
Type extends keyof ContextComponentCommandInteractionMap, Type extends keyof ContextComponentCommandInteractionMap,
M extends keyof RegisteredMiddlewares = never, M extends keyof RegisteredMiddlewares = never,
> extends BaseContext { > extends BaseContext {
/** /**
* Creates a new instance of the ComponentContext class. * Creates a new instance of the ComponentContext class.
* @param client - The UsingClient instance. * @param client - The UsingClient instance.
* @param interaction - The component interaction object. * @param interaction - The component interaction object.
*/ */
constructor( constructor(
readonly client: UsingClient, readonly client: UsingClient,
public interaction: ContextComponentCommandInteractionMap[Type], public interaction: ContextComponentCommandInteractionMap[Type],
) { ) {
super(client); super(client);
} }
command?: ComponentCommand; command!: ComponentCommand;
metadata: CommandMetadata<UnionToTuple<M>> = {} as never; metadata: CommandMetadata<UnionToTuple<M>> = {} as never;
globalMetadata: GlobalMetadata = {}; globalMetadata: GlobalMetadata = {};
/** /**
* Gets the language object for the interaction's locale. * Gets the language object for the interaction's locale.
*/ */
get t() { get t() {
return this.client.t(this.interaction?.locale ?? this.client.langs?.defaultLang ?? 'en-US'); return this.client.t(this.interaction?.locale ?? this.client.langs?.defaultLang ?? 'en-US');
} }
/** /**
* Gets the custom ID of the interaction. * Gets the custom ID of the interaction.
*/ */
get customId() { get customId() {
return this.interaction.customId; return this.interaction.customId;
} }
/** /**
* Writes a response to the interaction. * Writes a response to the interaction.
* @param body - The body of the response. * @param body - The body of the response.
* @param fetchReply - Whether to fetch the reply or not. * @param fetchReply - Whether to fetch the reply or not.
*/ */
write<FR extends boolean = false>(body: InteractionCreateBodyRequest, fetchReply?: FR) { write<FR extends boolean = false>(body: InteractionCreateBodyRequest, fetchReply?: FR) {
return this.interaction.write(body, fetchReply); return this.interaction.write(body, fetchReply);
} }
/** /**
* Defers the reply to the interaction. * Defers the reply to the interaction.
* @param ephemeral - Whether the reply should be ephemeral or not. * @param ephemeral - Whether the reply should be ephemeral or not.
*/ */
deferReply(ephemeral = false) { deferReply(ephemeral = false) {
return this.interaction.deferReply(ephemeral ? MessageFlags.Ephemeral : undefined); return this.interaction.deferReply(ephemeral ? MessageFlags.Ephemeral : undefined);
} }
/** /**
* Edits the response of the interaction. * Edits the response of the interaction.
* @param body - The updated body of the response. * @param body - The updated body of the response.
*/ */
editResponse(body: InteractionMessageUpdateBodyRequest) { editResponse(body: InteractionMessageUpdateBodyRequest) {
return this.interaction.editResponse(body); return this.interaction.editResponse(body);
} }
/** /**
* Updates the interaction with new data. * Updates the interaction with new data.
* @param body - The updated body of the interaction. * @param body - The updated body of the interaction.
*/ */
update(body: ComponentInteractionMessageUpdate) { update(body: ComponentInteractionMessageUpdate) {
return this.interaction.update(body); return this.interaction.update(body);
} }
/** /**
* Edits the response or replies to the interaction. * Edits the response or replies to the interaction.
* @param body - The body of the response or updated body of the interaction. * @param body - The body of the response or updated body of the interaction.
* @param fetchReply - Whether to fetch the reply or not. * @param fetchReply - Whether to fetch the reply or not.
*/ */
editOrReply<FR extends boolean = false>( editOrReply<FR extends boolean = false>(
body: InteractionCreateBodyRequest | InteractionMessageUpdateBodyRequest, body: InteractionCreateBodyRequest | InteractionMessageUpdateBodyRequest,
fetchReply?: FR, fetchReply?: FR,
): Promise<When<FR, WebhookMessage | Message, void | WebhookMessage | Message>> { ): Promise<When<FR, WebhookMessage | Message, void | WebhookMessage | Message>> {
return this.interaction.editOrReply(body as InteractionCreateBodyRequest, fetchReply); return this.interaction.editOrReply(body as InteractionCreateBodyRequest, fetchReply);
} }
/** /**
* Deletes the response of the interaction. * Deletes the response of the interaction.
* @returns A promise that resolves when the response is deleted. * @returns A promise that resolves when the response is deleted.
*/ */
deleteResponse() { deleteResponse() {
return this.interaction.deleteResponse(); return this.interaction.deleteResponse();
} }
modal(body: ModalCreateBodyRequest) { modal(body: ModalCreateBodyRequest) {
return this.interaction.modal(body); return this.interaction.modal(body);
} }
/** /**
* Gets the channel of the interaction. * Gets the channel of the interaction.
* @param mode - The mode to fetch the channel. * @param mode - The mode to fetch the channel.
* @returns A promise that resolves to the channel. * @returns A promise that resolves to the channel.
*/ */
channel(mode?: 'rest' | 'flow'): Promise<AllChannels>; channel(mode?: 'rest' | 'flow'): Promise<AllChannels>;
channel(mode?: 'cache'): ReturnCache<AllChannels>; channel(mode?: 'cache'): ReturnCache<AllChannels>;
channel(mode: 'cache' | 'rest' | 'flow' = 'cache') { channel(mode: 'cache' | 'rest' | 'flow' = 'cache') {
if (this.interaction?.channel && mode === 'cache') if (this.interaction?.channel && mode === 'cache')
return this.client.cache.adapter.isAsync ? Promise.resolve(this.interaction.channel) : this.interaction.channel; return this.client.cache.adapter.isAsync ? Promise.resolve(this.interaction.channel) : this.interaction.channel;
return this.client.channels.fetch(this.channelId, mode === 'rest'); return this.client.channels.fetch(this.channelId, mode === 'rest');
} }
/** /**
* Gets the bot member in the guild of the interaction. * Gets the bot member in the guild of the interaction.
* @param mode - The mode to fetch the member. * @param mode - The mode to fetch the member.
* @returns A promise that resolves to the bot member. * @returns A promise that resolves to the bot member.
*/ */
me(mode?: 'rest' | 'flow'): Promise<GuildMember>; me(mode?: 'rest' | 'flow'): Promise<GuildMember>;
me(mode?: 'cache'): ReturnCache<GuildMember | undefined>; me(mode?: 'cache'): ReturnCache<GuildMember | undefined>;
me(mode: 'cache' | 'rest' | 'flow' = 'cache') { me(mode: 'cache' | 'rest' | 'flow' = 'cache') {
if (!this.guildId) if (!this.guildId)
return mode === 'cache' ? (this.client.cache.adapter.isAsync ? Promise.resolve() : undefined) : Promise.resolve(); return mode === 'cache' ? (this.client.cache.adapter.isAsync ? Promise.resolve() : undefined) : Promise.resolve();
switch (mode) { switch (mode) {
case 'cache': case 'cache':
return this.client.cache.members?.get(this.client.botId, this.guildId); return this.client.cache.members?.get(this.client.botId, this.guildId);
default: default:
return this.client.members.fetch(this.guildId, this.client.botId, mode === 'rest'); return this.client.members.fetch(this.guildId, this.client.botId, mode === 'rest');
} }
} }
/** /**
* Gets the guild of the interaction. * Gets the guild of the interaction.
* @param mode - The mode to fetch the guild. * @param mode - The mode to fetch the guild.
* @returns A promise that resolves to the guild. * @returns A promise that resolves to the guild.
*/ */
guild(mode?: 'rest' | 'flow'): Promise<Guild<'cached' | 'api'> | undefined>; guild(mode?: 'rest' | 'flow'): Promise<Guild<'cached' | 'api'> | undefined>;
guild(mode?: 'cache'): ReturnCache<Guild<'cached'> | undefined>; guild(mode?: 'cache'): ReturnCache<Guild<'cached'> | undefined>;
guild(mode: 'cache' | 'rest' | 'flow' = 'cache') { guild(mode: 'cache' | 'rest' | 'flow' = 'cache') {
if (!this.guildId) if (!this.guildId)
return ( return (
mode === 'cache' ? (this.client.cache.adapter.isAsync ? Promise.resolve() : undefined) : Promise.resolve() mode === 'cache' ? (this.client.cache.adapter.isAsync ? Promise.resolve() : undefined) : Promise.resolve()
) as any; ) as any;
switch (mode) { switch (mode) {
case 'cache': case 'cache':
return this.client.cache.guilds?.get(this.guildId); return this.client.cache.guilds?.get(this.guildId);
default: default:
return this.client.guilds.fetch(this.guildId, mode === 'rest'); return this.client.guilds.fetch(this.guildId, mode === 'rest');
} }
} }
/** /**
* Gets the ID of the guild of the interaction. * Gets the ID of the guild of the interaction.
*/ */
get guildId() { get guildId() {
return this.interaction.guildId; return this.interaction.guildId;
} }
/** /**
* Gets the ID of the channel of the interaction. * Gets the ID of the channel of the interaction.
*/ */
get channelId() { get channelId() {
return this.interaction.channelId!; return this.interaction.channelId!;
} }
/** /**
* Gets the author of the interaction. * Gets the author of the interaction.
*/ */
get author() { get author() {
return this.interaction.user; return this.interaction.user;
} }
/** /**
* Gets the member of the interaction. * Gets the member of the interaction.
*/ */
get member() { get member() {
return this.interaction.member; return this.interaction.member;
} }
isComponent(): this is ComponentContext<keyof ContextComponentCommandInteractionMap> { isComponent(): this is ComponentContext<keyof ContextComponentCommandInteractionMap> {
return true; return true;
} }
isButton(): this is ComponentContext<'Button'> { isButton(): this is ComponentContext<'Button'> {
return this.interaction.data.componentType === ComponentType.Button; return this.interaction.data.componentType === ComponentType.Button;
} }
isChannelSelectMenu(): this is ComponentContext<'ChannelSelect'> { isChannelSelectMenu(): this is ComponentContext<'ChannelSelect'> {
return this.interaction.componentType === ComponentType.ChannelSelect; return this.interaction.componentType === ComponentType.ChannelSelect;
} }
isRoleSelectMenu(): this is ComponentContext<'RoleSelect'> { isRoleSelectMenu(): this is ComponentContext<'RoleSelect'> {
return this.interaction.componentType === ComponentType.RoleSelect; return this.interaction.componentType === ComponentType.RoleSelect;
} }
isMentionableSelectMenu(): this is ComponentContext<'MentionableSelect'> { isMentionableSelectMenu(): this is ComponentContext<'MentionableSelect'> {
return this.interaction.componentType === ComponentType.MentionableSelect; return this.interaction.componentType === ComponentType.MentionableSelect;
} }
isUserSelectMenu(): this is ComponentContext<'UserSelect'> { isUserSelectMenu(): this is ComponentContext<'UserSelect'> {
return this.interaction.componentType === ComponentType.UserSelect; return this.interaction.componentType === ComponentType.UserSelect;
} }
isStringSelectMenu(): this is ComponentContext<'StringSelect'> { isStringSelectMenu(): this is ComponentContext<'StringSelect'> {
return this.interaction.componentType === ComponentType.StringSelect; return this.interaction.componentType === ComponentType.StringSelect;
} }
} }
export interface ContextComponentCommandInteractionMap { export interface ContextComponentCommandInteractionMap {
Button: ButtonInteraction; Button: ButtonInteraction;
StringSelect: StringSelectMenuInteraction; StringSelect: StringSelectMenuInteraction;
UserSelect: UserSelectMenuInteraction; UserSelect: UserSelectMenuInteraction;
RoleSelect: RoleSelectMenuInteraction; RoleSelect: RoleSelectMenuInteraction;
MentionableSelect: MentionableSelectMenuInteraction; MentionableSelect: MentionableSelectMenuInteraction;
ChannelSelect: ChannelSelectMenuInteraction; ChannelSelect: ChannelSelectMenuInteraction;
} }

View File

@ -1,20 +1,22 @@
import type { RegisteredMiddlewares, UsingClient } from '../commands'; import type { ExtraProps, RegisteredMiddlewares, UsingClient } from '../commands';
import { InteractionCommandType } from './componentcommand'; import { InteractionCommandType } from './componentcommand';
import type { ModalContext } from './modalcontext'; import type { ModalContext } from './modalcontext';
export interface ModalCommand { export interface ModalCommand {
__filePath?: string; __filePath?: string;
} }
export abstract class ModalCommand { export abstract class ModalCommand {
type = InteractionCommandType.MODAL; type = InteractionCommandType.MODAL;
abstract filter(context: ModalContext): Promise<boolean> | boolean; abstract filter(context: ModalContext): Promise<boolean> | boolean;
abstract run(context: ModalContext): any; abstract run(context: ModalContext): any;
middlewares: (keyof RegisteredMiddlewares)[] = []; middlewares: (keyof RegisteredMiddlewares)[] = [];
onAfterRun?(context: ModalContext, error: unknown | undefined): any; props: ExtraProps = {};
onRunError?(context: ModalContext, error: unknown): any;
onMiddlewaresError?(context: ModalContext, error: string): any; onAfterRun?(context: ModalContext, error: unknown | undefined): any;
onInternalError?(client: UsingClient, error?: unknown): any; onRunError?(context: ModalContext, error: unknown): any;
} onMiddlewaresError?(context: ModalContext, error: string): any;
onInternalError?(client: UsingClient, error?: unknown): any;
}

View File

@ -1,192 +1,192 @@
import { MessageFlags } from 'discord-api-types/v10'; import { MessageFlags } from 'discord-api-types/v10';
import type { import type {
AllChannels, AllChannels,
Guild, Guild,
GuildMember, GuildMember,
Message, Message,
ModalCommand, ModalCommand,
ModalSubmitInteraction, ModalSubmitInteraction,
ReturnCache, ReturnCache,
WebhookMessage, WebhookMessage,
} from '..'; } from '..';
import type { CommandMetadata, ExtendContext, GlobalMetadata, RegisteredMiddlewares, UsingClient } from '../commands'; import type { CommandMetadata, ExtendContext, GlobalMetadata, RegisteredMiddlewares, UsingClient } from '../commands';
import { BaseContext } from '../commands/basecontext'; import { BaseContext } from '../commands/basecontext';
import type { import type {
InteractionCreateBodyRequest, InteractionCreateBodyRequest,
InteractionMessageUpdateBodyRequest, InteractionMessageUpdateBodyRequest,
ModalCreateBodyRequest, ModalCreateBodyRequest,
UnionToTuple, UnionToTuple,
When, When,
} from '../common'; } from '../common';
export interface ModalContext extends BaseContext, ExtendContext {} export interface ModalContext extends BaseContext, ExtendContext {}
/** /**
* Represents a context for interacting with components in a Discord bot. * Represents a context for interacting with components in a Discord bot.
* @template Type - The type of component interaction. * @template Type - The type of component interaction.
*/ */
export class ModalContext<M extends keyof RegisteredMiddlewares = never> extends BaseContext { export class ModalContext<M extends keyof RegisteredMiddlewares = never> extends BaseContext {
/** /**
* Creates a new instance of the ComponentContext class. * Creates a new instance of the ComponentContext class.
* @param client - The UsingClient instance. * @param client - The UsingClient instance.
* @param interaction - The component interaction object. * @param interaction - The component interaction object.
*/ */
constructor( constructor(
readonly client: UsingClient, readonly client: UsingClient,
public interaction: ModalSubmitInteraction, public interaction: ModalSubmitInteraction,
) { ) {
super(client); super(client);
} }
command?: ModalCommand; command!: ModalCommand;
metadata: CommandMetadata<UnionToTuple<M>> = {} as never; metadata: CommandMetadata<UnionToTuple<M>> = {} as never;
globalMetadata: GlobalMetadata = {}; globalMetadata: GlobalMetadata = {};
get customId() { get customId() {
return this.interaction.customId; return this.interaction.customId;
} }
get components() { get components() {
return this.interaction.components; return this.interaction.components;
} }
/** /**
* Gets the language object for the interaction's locale. * Gets the language object for the interaction's locale.
*/ */
get t() { get t() {
return this.client.t(this.interaction?.locale ?? this.client.langs?.defaultLang ?? 'en-US'); return this.client.t(this.interaction?.locale ?? this.client.langs?.defaultLang ?? 'en-US');
} }
/** /**
* Writes a response to the interaction. * Writes a response to the interaction.
* @param body - The body of the response. * @param body - The body of the response.
* @param fetchReply - Whether to fetch the reply or not. * @param fetchReply - Whether to fetch the reply or not.
*/ */
write<FR extends boolean = false>(body: InteractionCreateBodyRequest, fetchReply?: FR) { write<FR extends boolean = false>(body: InteractionCreateBodyRequest, fetchReply?: FR) {
return this.interaction.write(body, fetchReply); return this.interaction.write(body, fetchReply);
} }
/** /**
* Defers the reply to the interaction. * Defers the reply to the interaction.
* @param ephemeral - Whether the reply should be ephemeral or not. * @param ephemeral - Whether the reply should be ephemeral or not.
*/ */
deferReply(ephemeral = false) { deferReply(ephemeral = false) {
return this.interaction.deferReply(ephemeral ? MessageFlags.Ephemeral : undefined); return this.interaction.deferReply(ephemeral ? MessageFlags.Ephemeral : undefined);
} }
/** /**
* Edits the response of the interaction. * Edits the response of the interaction.
* @param body - The updated body of the response. * @param body - The updated body of the response.
*/ */
editResponse(body: InteractionMessageUpdateBodyRequest) { editResponse(body: InteractionMessageUpdateBodyRequest) {
return this.interaction.editResponse(body); return this.interaction.editResponse(body);
} }
/** /**
* Edits the response or replies to the interaction. * Edits the response or replies to the interaction.
* @param body - The body of the response or updated body of the interaction. * @param body - The body of the response or updated body of the interaction.
* @param fetchReply - Whether to fetch the reply or not. * @param fetchReply - Whether to fetch the reply or not.
*/ */
editOrReply<FR extends boolean = false>( editOrReply<FR extends boolean = false>(
body: InteractionCreateBodyRequest | InteractionMessageUpdateBodyRequest, body: InteractionCreateBodyRequest | InteractionMessageUpdateBodyRequest,
fetchReply?: FR, fetchReply?: FR,
): Promise<When<FR, WebhookMessage | Message, void | WebhookMessage | Message>> { ): Promise<When<FR, WebhookMessage | Message, void | WebhookMessage | Message>> {
return this.interaction.editOrReply(body as InteractionCreateBodyRequest, fetchReply); return this.interaction.editOrReply(body as InteractionCreateBodyRequest, fetchReply);
} }
/** /**
* Deletes the response of the interaction. * Deletes the response of the interaction.
* @returns A promise that resolves when the response is deleted. * @returns A promise that resolves when the response is deleted.
*/ */
deleteResponse() { deleteResponse() {
return this.interaction.deleteResponse(); return this.interaction.deleteResponse();
} }
modal(body: ModalCreateBodyRequest) { modal(body: ModalCreateBodyRequest) {
//@ts-expect-error //@ts-expect-error
return this.interaction.modal(body); return this.interaction.modal(body);
} }
/** /**
* Gets the channel of the interaction. * Gets the channel of the interaction.
* @param mode - The mode to fetch the channel. * @param mode - The mode to fetch the channel.
* @returns A promise that resolves to the channel. * @returns A promise that resolves to the channel.
*/ */
channel(mode?: 'rest' | 'flow'): Promise<AllChannels>; channel(mode?: 'rest' | 'flow'): Promise<AllChannels>;
channel(mode?: 'cache'): ReturnCache<AllChannels>; channel(mode?: 'cache'): ReturnCache<AllChannels>;
channel(mode: 'cache' | 'rest' | 'flow' = 'cache') { channel(mode: 'cache' | 'rest' | 'flow' = 'cache') {
if (this.interaction?.channel && mode === 'cache') if (this.interaction?.channel && mode === 'cache')
return this.client.cache.adapter.isAsync ? Promise.resolve(this.interaction.channel) : this.interaction.channel; return this.client.cache.adapter.isAsync ? Promise.resolve(this.interaction.channel) : this.interaction.channel;
return this.client.channels.fetch(this.channelId, mode === 'rest'); return this.client.channels.fetch(this.channelId, mode === 'rest');
} }
/** /**
* Gets the bot member in the guild of the interaction. * Gets the bot member in the guild of the interaction.
* @param mode - The mode to fetch the member. * @param mode - The mode to fetch the member.
* @returns A promise that resolves to the bot member. * @returns A promise that resolves to the bot member.
*/ */
me(mode?: 'rest' | 'flow'): Promise<GuildMember>; me(mode?: 'rest' | 'flow'): Promise<GuildMember>;
me(mode?: 'cache'): ReturnCache<GuildMember | undefined>; me(mode?: 'cache'): ReturnCache<GuildMember | undefined>;
me(mode: 'cache' | 'rest' | 'flow' = 'cache') { me(mode: 'cache' | 'rest' | 'flow' = 'cache') {
if (!this.guildId) if (!this.guildId)
return mode === 'cache' ? (this.client.cache.adapter.isAsync ? Promise.resolve() : undefined) : Promise.resolve(); return mode === 'cache' ? (this.client.cache.adapter.isAsync ? Promise.resolve() : undefined) : Promise.resolve();
switch (mode) { switch (mode) {
case 'cache': case 'cache':
return this.client.cache.members?.get(this.client.botId, this.guildId); return this.client.cache.members?.get(this.client.botId, this.guildId);
default: default:
return this.client.members.fetch(this.guildId, this.client.botId, mode === 'rest'); return this.client.members.fetch(this.guildId, this.client.botId, mode === 'rest');
} }
} }
/** /**
* Gets the guild of the interaction. * Gets the guild of the interaction.
* @param mode - The mode to fetch the guild. * @param mode - The mode to fetch the guild.
* @returns A promise that resolves to the guild. * @returns A promise that resolves to the guild.
*/ */
guild(mode?: 'rest' | 'flow'): Promise<Guild<'cached' | 'api'> | undefined>; guild(mode?: 'rest' | 'flow'): Promise<Guild<'cached' | 'api'> | undefined>;
guild(mode?: 'cache'): ReturnCache<Guild<'cached'> | undefined>; guild(mode?: 'cache'): ReturnCache<Guild<'cached'> | undefined>;
guild(mode: 'cache' | 'rest' | 'flow' = 'cache') { guild(mode: 'cache' | 'rest' | 'flow' = 'cache') {
if (!this.guildId) if (!this.guildId)
return ( return (
mode === 'cache' ? (this.client.cache.adapter.isAsync ? Promise.resolve() : undefined) : Promise.resolve() mode === 'cache' ? (this.client.cache.adapter.isAsync ? Promise.resolve() : undefined) : Promise.resolve()
) as any; ) as any;
switch (mode) { switch (mode) {
case 'cache': case 'cache':
return this.client.cache.guilds?.get(this.guildId); return this.client.cache.guilds?.get(this.guildId);
default: default:
return this.client.guilds.fetch(this.guildId, mode === 'rest'); return this.client.guilds.fetch(this.guildId, mode === 'rest');
} }
} }
/** /**
* Gets the ID of the guild of the interaction. * Gets the ID of the guild of the interaction.
*/ */
get guildId() { get guildId() {
return this.interaction.guildId; return this.interaction.guildId;
} }
/** /**
* Gets the ID of the channel of the interaction. * Gets the ID of the channel of the interaction.
*/ */
get channelId() { get channelId() {
return this.interaction.channelId!; return this.interaction.channelId!;
} }
/** /**
* Gets the author of the interaction. * Gets the author of the interaction.
*/ */
get author() { get author() {
return this.interaction.user; return this.interaction.user;
} }
/** /**
* Gets the member of the interaction. * Gets the member of the interaction.
*/ */
get member() { get member() {
return this.interaction.member; return this.interaction.member;
} }
isModal(): this is ModalContext { isModal(): this is ModalContext {
return true; return true;
} }
} }