feat: experimental custom handlers

This commit is contained in:
MARCROCK22 2024-03-26 14:39:04 -04:00
parent 168aab508a
commit 9b40239b55
14 changed files with 165 additions and 82 deletions

View File

@ -4,7 +4,7 @@ import type { Adapter } from '../cache';
import { Cache, MemoryAdapter } from '../cache';
import type { RegisteredMiddlewares } from '../commands';
import type { InferWithPrefix, MiddlewareContext } from '../commands/applications/shared';
import { CommandHandler } from '../commands/handler';
import { CommandHandler, type CommandHandlerLike } from '../commands/handler';
import {
ChannelShorter,
EmojiShorter,
@ -25,8 +25,8 @@ import {
} from '../common';
import type { DeepPartial, IntentStrings, OmitInsert, When } from '../common/types/util';
import { ComponentHandler } from '../components/handler';
import { LangsHandler } from '../langs/handler';
import { ComponentHandler, type ComponentHandlerLike } from '../components/handler';
import { LangsHandler, type LangsHandlerLike } from '../langs/handler';
import type {
ChatInputCommandInteraction,
Message,
@ -55,9 +55,9 @@ export class BaseClient {
name: '[Seyfert]',
});
langs = new LangsHandler(this.logger);
commands = new CommandHandler(this.logger, this);
components = new ComponentHandler(this.logger, this);
langs?: LangsHandlerLike = new LangsHandler(this.logger);
commands?: CommandHandlerLike = new CommandHandler(this.logger, this);
components?: ComponentHandlerLike = new ComponentHandler(this.logger, this);
private _applicationId?: string;
private _botId?: string;
@ -99,7 +99,7 @@ export class BaseClient {
return new Router(this.rest).createProxy();
}
setServices({ rest, cache, langs, middlewares }: ServicesOptions) {
setServices({ rest, cache, langs, middlewares, handlers }: ServicesOptions) {
if (rest) {
this.rest = rest;
}
@ -111,13 +111,24 @@ export class BaseClient {
this,
);
}
if (langs) {
if (langs.default) this.langs.defaultLang = langs.default;
if (langs.aliases) this.langs.aliases = Object.entries(langs.aliases);
}
if (middlewares) {
this.middlewares = middlewares;
}
if (handlers) {
if ('components' in handlers) {
this.components = handlers.components;
}
if ('commands' in handlers) {
this.commands = handlers.commands;
}
if ('langs' in handlers) {
this.langs = handlers.langs;
}
}
if (langs) {
if (langs.default) this.langs!.defaultLang = langs.default;
if (langs.aliases) this.langs!.aliases = Object.entries(langs.aliases);
}
}
protected async execute(..._options: unknown[]) {
@ -170,7 +181,7 @@ export class BaseClient {
applicationId ??= await this.getRC().then(x => x.applicationId ?? this.applicationId);
BaseClient.assertString(applicationId, 'applicationId is not a string');
const commands = this.commands.values.map(x => x.toJSON());
const commands = this.commands!.values.map(x => x.toJSON());
const filter = filterSplit(commands, command => !command.guild_id);
await this.proxy.applications(applicationId).commands.put({
@ -197,7 +208,7 @@ export class BaseClient {
async loadCommands(dir?: string) {
dir ??= await this.getRC().then(x => x.commands);
if (dir) {
if (dir && this.commands) {
await this.commands.load(dir, this);
this.logger.info('CommandHandler loaded');
}
@ -205,7 +216,7 @@ export class BaseClient {
async loadComponents(dir?: string) {
dir ??= await this.getRC().then(x => x.components);
if (dir) {
if (dir && this.components) {
await this.components.load(dir);
this.logger.info('ComponentHandler loaded');
}
@ -213,14 +224,14 @@ export class BaseClient {
async loadLangs(dir?: string) {
dir ??= await this.getRC().then(x => x.langs);
if (dir) {
if (dir && this.langs) {
await this.langs.load(dir);
this.logger.info('LangsHandler loaded');
}
}
t(locale: string) {
return this.langs.get(locale);
return this.langs!.get(locale);
}
async getRC<
@ -310,4 +321,9 @@ export interface ServicesOptions {
aliases?: Record<string, LocaleString[]>;
};
middlewares?: Record<string, MiddlewareContext>;
handlers?: {
components?: ComponentHandlerLike;
commands?: CommandHandlerLike;
langs?: LangsHandlerLike;
};
}

View File

@ -1,5 +1,5 @@
import { parentPort, workerData } from 'node:worker_threads';
import type { Command, CommandContext, Message, SubCommand } from '..';
import type { Command, CommandContext, EventHandlerLike, Message, SubCommand } from '..';
import {
GatewayIntentBits,
type DeepPartial,
@ -22,7 +22,7 @@ import { onMessageCreate } from './onmessagecreate';
export class Client<Ready extends boolean = boolean> extends BaseClient {
private __handleGuilds?: Set<string> = new Set();
gateway!: ShardManager;
events = new EventHandler(this.logger);
events?: EventHandlerLike = new EventHandler(this.logger);
me!: If<Ready, ClientUser>;
declare options: ClientOptions | undefined;
memberUpdateHandler = new MemberUpdateHandler();
@ -37,6 +37,9 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
...rest
}: ServicesOptions & {
gateway?: ShardManager;
handlers?: ServicesOptions['handlers'] & {
events?: EventHandlerLike;
};
}) {
super.setServices(rest);
if (gateway) {
@ -48,11 +51,14 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
};
this.gateway = gateway;
}
if (rest.handlers && 'events' in rest.handlers) {
this.events = rest.handlers.events;
}
}
async loadEvents(dir?: string) {
dir ??= await this.getRC().then(x => x.events);
if (dir) {
if (dir && this.events) {
await this.events.load(dir);
this.logger.info('EventHandler loaded');
}
@ -114,21 +120,21 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
}
protected async onPacket(shardId: number, packet: GatewayDispatchPayload) {
await this.events.runEvent('RAW', this, packet, shardId);
await this.events?.runEvent('RAW', this, packet, shardId);
switch (packet.t) {
//// Cases where we must obtain the old data before updating
case 'GUILD_MEMBER_UPDATE':
if (!this.memberUpdateHandler.check(packet.d)) {
return;
}
await this.events.execute(packet.t, packet, this as Client<true>, shardId);
await this.events?.execute(packet.t, packet, this as Client<true>, shardId);
await this.cache.onPacket(packet);
break;
case 'PRESENCE_UPDATE':
if (!this.presenceUpdateHandler.check(packet.d as any)) {
return;
}
await this.events.execute(packet.t, packet, this as Client<true>, shardId);
await this.events?.execute(packet.t, packet, this as Client<true>, shardId);
await this.cache.onPacket(packet);
break;
//rest of the events
@ -153,7 +159,7 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
!((this.gateway.options.intents & GatewayIntentBits.Guilds) === GatewayIntentBits.Guilds)
) {
if ([...this.gateway.values()].every(shard => shard.data.session_id)) {
await this.events.runEvent('BOT_READY', this, this.me, -1);
await this.events?.runEvent('BOT_READY', this, this.me, -1);
}
delete this.__handleGuilds;
}
@ -163,7 +169,7 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
if (this.__handleGuilds?.has(packet.d.id)) {
this.__handleGuilds.delete(packet.d.id);
if (!this.__handleGuilds.size && [...this.gateway.values()].every(shard => shard.data.session_id)) {
await this.events.runEvent('BOT_READY', this, this.me, -1);
await this.events?.runEvent('BOT_READY', this, this.me, -1);
}
if (!this.__handleGuilds.size) delete this.__handleGuilds;
return;
@ -171,7 +177,7 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
break;
}
}
await this.events.execute(packet.t, packet, this as Client<true>, shardId);
await this.events?.execute(packet.t, packet, this as Client<true>, shardId);
break;
}
}

View File

@ -1,11 +1,11 @@
import { ApplicationCommandType, InteractionType, type APIInteraction } from 'discord-api-types/v10';
import {
CommandContext,
type ContextOptionsResolved,
MenuCommandContext,
OptionResolver,
type Command,
type ContextMenuCommand,
type ContextOptionsResolved,
} from '../commands';
import type {
ChatInputCommandInteraction,
@ -28,7 +28,7 @@ export async function onInteractionCreate(
switch (body.type) {
case InteractionType.ApplicationCommandAutocomplete:
{
const parentCommand = self.commands.values.find(x => {
const parentCommand = self.commands?.values.find(x => {
if (x.guild_id && !x.guild_id.includes(body.data.guild_id ?? '')) {
return false;
}
@ -76,7 +76,7 @@ export async function onInteractionCreate(
case ApplicationCommandType.Message:
case ApplicationCommandType.User:
{
const command = self.commands.values.find(x => {
const command = self.commands?.values.find(x => {
if (x.guild_id && !x.guild_id.includes(body.data.guild_id ?? '')) {
return false;
}
@ -135,7 +135,7 @@ export async function onInteractionCreate(
case ApplicationCommandType.ChatInput:
{
const packetData = body.data;
const parentCommand = self.commands.values.find(x => {
const parentCommand = self.commands?.values.find(x => {
if (x.guild_id && !x.guild_id.includes(packetData.guild_id ?? '')) {
return false;
}
@ -206,20 +206,20 @@ export async function onInteractionCreate(
case InteractionType.ModalSubmit:
{
const interaction = BaseInteraction.from(self, body, __reply) as ModalSubmitInteraction;
if (self.components.hasModal(interaction)) {
if (self.components?.hasModal(interaction)) {
await self.components.onModalSubmit(interaction);
} else {
await self.components.executeModal(interaction);
await self.components?.executeModal(interaction);
}
}
break;
case InteractionType.MessageComponent:
{
const interaction = BaseInteraction.from(self, body, __reply) as ComponentInteraction;
if (self.components.hasComponent(body.message.id, interaction.customId)) {
if (self.components?.hasComponent(body.message.id, interaction.customId)) {
await self.components.onComponent(body.message.id, interaction);
} else {
await self.components.executeComponent(interaction);
await self.components?.executeComponent(interaction);
}
}
break;

View File

@ -34,7 +34,7 @@ function getCommandFromContent(
const parentName = commandRaw[0];
const groupName = commandRaw.length === 3 ? commandRaw[1] : undefined;
const subcommandName = groupName ? commandRaw[2] : commandRaw[1];
const parent = self.commands.values.find(x => x.name === parentName);
const parent = self.commands!.values.find(x => x.name === parentName);
const fullCommandName = `${parentName}${
groupName ? ` ${groupName} ${subcommandName}` : `${subcommandName ? ` ${subcommandName}` : ''}`
}`;
@ -48,15 +48,15 @@ function getCommandFromContent(
const command =
groupName || subcommandName
? (parent.options?.find(opt => {
if (opt instanceof SubCommand) {
if (groupName) {
if (opt.group !== groupName) return false;
if (opt instanceof SubCommand) {
if (groupName) {
if (opt.group !== groupName) return false;
}
if (opt.group && !groupName) return false;
return subcommandName === opt.name;
}
if (opt.group && !groupName) return false;
return subcommandName === opt.name;
}
return false;
}) as SubCommand)
return false;
}) as SubCommand)
: parent;
return {
@ -80,7 +80,10 @@ export async function onMessageCreate(
const content = message.content.slice(prefix.length).trimStart();
const { fullCommandName, command, parent } = getCommandFromContent(
content.split(' ').filter(x => x).slice(0, 3),
content
.split(' ')
.filter(x => x)
.slice(0, 3),
self,
);

View File

@ -5,7 +5,7 @@ import type { Cache } from '../cache';
import { WorkerAdapter } from '../cache';
import type { GatewayDispatchPayload, GatewaySendPayload, When } from '../common';
import { GatewayIntentBits, LogLevels, Logger, type DeepPartial } from '../common';
import { EventHandler } from '../events';
import { EventHandler, type EventHandlerLike } from '../events';
import { ClientUser } from '../structures';
import { Shard, type ShardManagerOptions, type WorkerData } from '../websocket';
import type {
@ -21,7 +21,7 @@ import type {
WorkerStart,
} from '../websocket/discord/worker';
import type { ManagerMessages } from '../websocket/discord/workermanager';
import type { BaseClientOptions, StartOptions } from './base';
import type { BaseClientOptions, ServicesOptions, StartOptions } from './base';
import { BaseClient } from './base';
import type { Client } from './client';
import { onInteractionCreate } from './oninteractioncreate';
@ -46,7 +46,7 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
name: `[Worker #${workerData.workerId}]`,
});
events = new EventHandler(this.logger);
events?: EventHandlerLike = new EventHandler(this.logger);
me!: When<Ready, ClientUser>;
promises = new Map<string, { resolve: (value: any) => void; timeout: NodeJS.Timeout }>();
@ -98,6 +98,19 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
return acc / this.shards.size;
}
setServices({
...rest
}: ServicesOptions & {
handlers?: ServicesOptions['handlers'] & {
events?: EventHandlerLike;
};
}) {
super.setServices(rest);
if (rest.handlers && 'events' in rest.handlers) {
this.events = rest.handlers.events;
}
}
async start(options: Omit<DeepPartial<StartOptions>, 'httpConnection' | 'token' | 'connection'> = {}) {
await super.start(options);
await this.loadEvents(options.eventsDir);
@ -106,7 +119,7 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
async loadEvents(dir?: string) {
dir ??= await this.getRC().then(x => x.events);
if (dir) {
if (dir && this.events) {
await this.events.load(dir);
this.logger.info('EventHandler loaded');
}
@ -221,7 +234,7 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
}
break;
case 'BOT_READY':
await this.events.runEvent('BOT_READY', this, this.me, -1);
await this.events?.runEvent('BOT_READY', this, this.me, -1);
break;
case 'API_RESPONSE':
{
@ -302,14 +315,14 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
}
protected async onPacket(packet: GatewayDispatchPayload, shardId: number) {
await this.events.execute('RAW', packet, this as WorkerClient<true>, shardId);
await this.events?.execute('RAW', packet, this as WorkerClient<true>, shardId);
switch (packet.t) {
case 'GUILD_MEMBER_UPDATE':
await this.events.execute(packet.t, packet, this as WorkerClient<true>, shardId);
await this.events?.execute(packet.t, packet, this as WorkerClient<true>, shardId);
await this.cache.onPacket(packet);
break;
case 'PRESENCE_UPDATE':
await this.events.execute(packet.t, packet, this as WorkerClient<true>, shardId);
await this.events?.execute(packet.t, packet, this as WorkerClient<true>, shardId);
await this.cache.onPacket(packet);
break;
//rest of the events
@ -332,7 +345,7 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
type: 'WORKER_READY',
workerId: this.workerId,
} as WorkerReady);
await this.events.runEvent('WORKER_READY', this, this.me, -1);
await this.events?.runEvent('WORKER_READY', this, this.me, -1);
}
delete this.__handleGuilds;
}
@ -352,14 +365,14 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
type: 'WORKER_READY',
workerId: this.workerId,
} as WorkerReady);
await this.events.runEvent('WORKER_READY', this, this.me, -1);
await this.events?.runEvent('WORKER_READY', this, this.me, -1);
}
if (!this.__handleGuilds.size) delete this.__handleGuilds;
return;
}
}
}
await this.events.execute(packet.t, packet, this, shardId);
await this.events?.execute(packet.t, packet, this, shardId);
}
break;
}

View File

@ -54,7 +54,7 @@ export class CommandContext<T extends OptionsRecord = {}, M extends keyof Regist
}
get t() {
return this.client.langs.get(this.interaction?.locale ?? this.client.langs.defaultLang ?? 'en-US');
return this.client.langs!.get(this.interaction?.locale ?? this.client.langs!.defaultLang ?? 'en-US');
}
get fullCommandName() {

View File

@ -60,7 +60,7 @@ export class MenuCommandContext<
}
get t() {
return this.client.langs.get(this.interaction.locale);
return this.client.langs!.get(this.interaction.locale ?? this.client.langs!.defaultLang ?? 'en-US');
}
get fullCommandName() {

View File

@ -5,6 +5,11 @@ import { Command, SubCommand } from './applications/chat';
import { ContextMenuCommand } from './applications/menu';
import type { UsingClient } from './applications/shared';
export interface CommandHandlerLike {
values: CommandHandler['values'];
load: CommandHandler['load'];
}
export class CommandHandler extends BaseHandler {
values: (Command | ContextMenuCommand)[] = [];
protected filter = (path: string) => path.endsWith('.js') || (!path.endsWith('.d.ts') && path.endsWith('.ts'));
@ -122,20 +127,20 @@ export class CommandHandler extends BaseHandler {
if (command.__t) {
command.name_localizations = {};
command.description_localizations = {};
for (const locale of Object.keys(client.langs.values)) {
const locales = this.client.langs.aliases.find(x => x[0] === locale)?.[1] ?? [];
for (const locale of Object.keys(client.langs!.values)) {
const locales = this.client.langs!.aliases.find(x => x[0] === locale)?.[1] ?? [];
if (Object.values<string>(Locale).includes(locale)) locales.push(locale as LocaleString);
if (command.__t.name) {
for (const i of locales) {
const valueName = client.langs.getKey(locale, command.__t.name!);
const valueName = client.langs!.getKey(locale, command.__t.name!);
if (valueName) command.name_localizations[i] = valueName;
}
}
if (command.__t.description) {
for (const i of locales) {
const valueKey = client.langs.getKey(locale, command.__t.description!);
const valueKey = client.langs!.getKey(locale, command.__t.description!);
if (valueKey) command.description_localizations[i] = valueKey;
}
}
@ -146,20 +151,20 @@ export class CommandHandler extends BaseHandler {
if (options instanceof SubCommand || !options.locales) continue;
options.name_localizations = {};
options.description_localizations = {};
for (const locale of Object.keys(client.langs.values)) {
const locales = this.client.langs.aliases.find(x => x[0] === locale)?.[1] ?? [];
for (const locale of Object.keys(client.langs!.values)) {
const locales = this.client.langs!.aliases.find(x => x[0] === locale)?.[1] ?? [];
if (Object.values<string>(Locale).includes(locale)) locales.push(locale as LocaleString);
if (options.locales.name) {
for (const i of locales) {
const valueName = client.langs.getKey(locale, options.locales.name!);
const valueName = client.langs!.getKey(locale, options.locales.name!);
if (valueName) options.name_localizations[i] = valueName;
}
}
if (options.locales.description) {
for (const i of locales) {
const valueKey = client.langs.getKey(locale, options.locales.description!);
const valueKey = client.langs!.getKey(locale, options.locales.description!);
if (valueKey) options.description_localizations[i] = valueKey;
}
}
@ -168,8 +173,8 @@ export class CommandHandler extends BaseHandler {
if (command instanceof Command && command.__tGroups) {
command.groups = {};
for (const locale of Object.keys(client.langs.values)) {
const locales = this.client.langs.aliases.find(x => x[0] === locale)?.[1] ?? [];
for (const locale of Object.keys(client.langs!.values)) {
const locales = this.client.langs!.aliases.find(x => x[0] === locale)?.[1] ?? [];
if (Object.values<string>(Locale).includes(locale)) locales.push(locale as LocaleString);
for (const group in command.__tGroups) {
command.groups[group] ??= {
@ -180,7 +185,7 @@ export class CommandHandler extends BaseHandler {
if (command.__tGroups[group].name) {
for (const i of locales) {
const valueName = client.langs.getKey(locale, command.__tGroups[group].name!);
const valueName = client.langs!.getKey(locale, command.__tGroups[group].name!);
if (valueName) {
command.groups[group].name!.push([i, valueName]);
}
@ -189,7 +194,7 @@ export class CommandHandler extends BaseHandler {
if (command.__tGroups[group].description) {
for (const i of locales) {
const valueKey = client.langs.getKey(locale, command.__tGroups[group].description!);
const valueKey = client.langs!.getKey(locale, command.__tGroups[group].description!);
if (valueKey) {
command.groups[group].description!.push([i, valueKey]);
}

View File

@ -50,7 +50,7 @@ export class MessageShorter extends BaseShorter {
.messages(messageId)
.delete({ reason })
.then(() => {
return this.client.components.onMessageDelete(messageId);
return this.client.components?.onMessageDelete(messageId);
});
}
fetch(messageId: string, channelId: string) {

View File

@ -14,8 +14,28 @@ type COMPONENTS = {
__run: (customId: string | string[] | RegExp, callback: ComponentCallback) => any;
};
export interface ComponentHandlerLike {
readonly values: Map<string, COMPONENTS>;
readonly commands: (ComponentCommand | ModalCommand)[];
readonly modals: Map<string, ModalSubmitCallback> | LimitedCollection<string, ModalSubmitCallback>;
createComponentCollector: ComponentHandler['createComponentCollector'];
hasModal: ComponentHandler['hasModal'];
onModalSubmit: ComponentHandler['onModalSubmit'];
executeModal: ComponentHandler['executeModal'];
hasComponent: ComponentHandler['hasComponent'];
executeComponent: ComponentHandler['executeComponent'];
onComponent: ComponentHandler['onComponent'];
load: ComponentHandler['load'];
onMessageDelete: ComponentHandler['onMessageDelete'];
}
export class ComponentHandler extends BaseHandler {
protected onFail?: OnFailCallback;
onFail?: OnFailCallback;
readonly values = new Map<string, COMPONENTS>();
// 10 minutes timeout, because discord dont send an event when the user cancel the modal
readonly modals = new LimitedCollection<string, ModalSubmitCallback>({ expire: 60e3 * 10 });
@ -99,11 +119,13 @@ export class ComponentHandler extends BaseHandler {
}
hasComponent(id: string, customId: string) {
return this.values.get(id)?.components?.some(x => {
if (typeof x.match === 'string') return x.match === customId;
if (Array.isArray(x.match)) return x.match.includes(customId);
return customId.match(x.match);
});
return (
this.values.get(id)?.components?.some(x => {
if (typeof x.match === 'string') return x.match === customId;
if (Array.isArray(x.match)) return x.match.includes(customId);
return customId.match(x.match);
}) ?? false
);
}
resetTimeouts(id: string) {
@ -164,6 +186,7 @@ export class ComponentHandler extends BaseHandler {
}
async reload(path: string) {
if (!this.client.components) return;
const component = this.client.components.commands.find(
x =>
x.__filePath?.endsWith(`${path}.js`) ||
@ -184,6 +207,7 @@ export class ComponentHandler extends BaseHandler {
}
async reloadAll() {
if (!this.client.components) return;
for (const i of this.client.components.commands) {
if (!i.__filePath) return this.logger.warn('Unknown command dont have __filePath property', i);
await this.reload(i.__filePath);

View File

@ -19,6 +19,13 @@ type EventValue = MakeRequired<ClientEvent, '__filePath'> & { fired?: boolean };
type GatewayEvents = Uppercase<SnakeCase<keyof ClientEvents>>;
export interface EventHandlerLike {
runEvent: EventHandler['runEvent'];
execute: EventHandler['execute'];
load: EventHandler['load'];
values: EventHandler['values'];
}
export class EventHandler extends BaseHandler {
protected onFail: OnFailCallback = err => this.logger.warn('<Client>.events.OnFail', err);
protected filter = (path: string) => path.endsWith('.js') || (!path.endsWith('.d.ts') && path.endsWith('.ts'));
@ -54,7 +61,7 @@ export class EventHandler extends BaseHandler {
case 'MESSAGE_CREATE':
{
const { d: data } = args[0] as GatewayMessageCreateDispatch;
if (args[1].components.values.has(data.interaction?.id ?? data.id)) {
if (args[1].components?.values.has(data.interaction?.id ?? data.id)) {
args[1].components.values.get(data.interaction?.id ?? data.id)!.messageId = data.id;
}
}
@ -62,20 +69,20 @@ export class EventHandler extends BaseHandler {
case 'MESSAGE_DELETE':
{
const { d: data } = args[0] as GatewayMessageDeleteDispatch;
const value = [...args[1].components.values].find(x => x[1].messageId === data.id);
const value = [...(args[1].components?.values ?? [])].find(x => x[1].messageId === data.id);
if (value) {
args[1].components.onMessageDelete(value[0]);
args[1].components!.onMessageDelete(value[0]);
}
}
break;
case 'MESSAGE_DELETE_BULK':
{
const { d: data } = args[0] as GatewayMessageDeleteBulkDispatch;
const values = [...args[1].components.values];
const values = [...(args[1].components?.values ?? [])];
data.ids.forEach(id => {
const value = values.find(x => x[1].messageId === id);
if (value) {
args[1].components.onMessageDelete(value[0]);
args[1].components!.onMessageDelete(value[0]);
}
});
}

View File

@ -1,6 +1,15 @@
import { BaseHandler, type Locale, type LocaleString } from '../common';
import { LangRouter } from './router';
export interface LangsHandlerLike {
getKey: LangsHandler['getKey'];
load: LangsHandler['load'];
values: LangsHandler['values'];
aliases: LangsHandler['aliases'];
get: LangsHandler['get'];
defaultLang?: LangsHandler['defaultLang'];
}
export class LangsHandler extends BaseHandler {
values: Partial<Record<string, any>> = {};
protected filter = (path: string) =>

View File

@ -388,7 +388,7 @@ export class Interaction<
.webhooks(this.applicationId)(this.token)
.messages(messageId)
.delete()
.then(() => this.client.components.onMessageDelete(messageId === '@original' ? this.id : messageId));
.then(() => this.client.components?.onMessageDelete(messageId === '@original' ? this.id : messageId));
}
async createResponse({ files, ...body }: MessageWebhookCreateBodyRequest) {

View File

@ -51,7 +51,7 @@ export class BaseMessage extends DiscordBase {
}
createComponentCollector(options?: ListenerOptions) {
return this.client.components.createComponentCollector(this.id, options);
return this.client.components!.createComponentCollector(this.id, options);
}
get url() {