collectors

This commit is contained in:
MARCROCK22 2024-03-11 15:33:59 -04:00
parent db8958d2a1
commit c2a722bacd
16 changed files with 193 additions and 301 deletions

104
package-lock.json generated
View File

@ -9,30 +9,30 @@
"version": "0.1.0",
"license": "ISC",
"dependencies": {
"chokidar": "^3.5.3",
"discord-api-types": "^0.37.71",
"magic-bytes.js": "^1.7.0",
"ts-mixer": "^6.0.3",
"chokidar": "^3.6.0",
"discord-api-types": "^0.37.73",
"magic-bytes.js": "^1.10.0",
"ts-mixer": "^6.0.4",
"ws": "^8.16.0"
},
"devDependencies": {
"@biomejs/biome": "1.5.0",
"@types/node": "^20.10.7",
"@biomejs/biome": "1.6.0",
"@types/node": "^20.11.25",
"@types/ws": "^8.5.10",
"husky": "^8.0.3",
"husky": "^9.0.11",
"lint-staged": "^15.2.2",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
},
"optionalDependencies": {
"ioredis": "^5.3.2",
"tweetnacl": "^1.0.3",
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.39.0"
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.42.0"
}
},
"node_modules/@biomejs/biome": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.5.0.tgz",
"integrity": "sha512-ln+o5jbs109qpeDoA+5n+vlAPai3DhlK0tHtZXzQvu4tswFgxNiJCeIXmlW1DYHziTmtBImV3Y0uhbm2iVSE3Q==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.6.0.tgz",
"integrity": "sha512-hvP8K1+CV8qc9eNdXtPwzScVxFSHB448CPKSqX6+8IW8G7bbhBVKGC80BowExJN5+vu+kzsj4xkWa780MAOlJw==",
"dev": true,
"hasInstallScript": true,
"bin": {
@ -46,20 +46,20 @@
"url": "https://opencollective.com/biome"
},
"optionalDependencies": {
"@biomejs/cli-darwin-arm64": "1.5.0",
"@biomejs/cli-darwin-x64": "1.5.0",
"@biomejs/cli-linux-arm64": "1.5.0",
"@biomejs/cli-linux-arm64-musl": "1.5.0",
"@biomejs/cli-linux-x64": "1.5.0",
"@biomejs/cli-linux-x64-musl": "1.5.0",
"@biomejs/cli-win32-arm64": "1.5.0",
"@biomejs/cli-win32-x64": "1.5.0"
"@biomejs/cli-darwin-arm64": "1.6.0",
"@biomejs/cli-darwin-x64": "1.6.0",
"@biomejs/cli-linux-arm64": "1.6.0",
"@biomejs/cli-linux-arm64-musl": "1.6.0",
"@biomejs/cli-linux-x64": "1.6.0",
"@biomejs/cli-linux-x64-musl": "1.6.0",
"@biomejs/cli-win32-arm64": "1.6.0",
"@biomejs/cli-win32-x64": "1.6.0"
}
},
"node_modules/@biomejs/cli-darwin-arm64": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.5.0.tgz",
"integrity": "sha512-3+D7axf04dpadGMOaqb2q+zyQnhWW0o/Imt7TJBWsoE0N3/+28Wht8g3UEHHcUL5FPuGIfsE+NcYntBaaAsEIg==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.6.0.tgz",
"integrity": "sha512-K1Fjqye5pt+Ua+seC7V/2bFjfnqOaEOcQbBQSiiefB/VPNOb6lA5NFIfJ1PskTA3JrMXE1k7iqKQn56qrKFS6A==",
"cpu": [
"arm64"
],
@ -73,9 +73,9 @@
}
},
"node_modules/@biomejs/cli-darwin-x64": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.5.0.tgz",
"integrity": "sha512-8k5aaLWE/B6ZAXLC+z/Vwh9ogyiSaiRIfvg+F9foxuneHl2R/D/2Iy7pvd3Yoi4Kf6/MBdowekPVezGP4/Kbcw==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.6.0.tgz",
"integrity": "sha512-CjEALu6vN9RbcfhaBDoj481mesUIsUjxgQn+/kiMCea+Paypqslhez1I7OwRBJnkzz+Pa+PXdABd7S30eyy6+Q==",
"cpu": [
"x64"
],
@ -89,9 +89,9 @@
}
},
"node_modules/@biomejs/cli-linux-arm64": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.5.0.tgz",
"integrity": "sha512-RiecxG71E1jnqiJZ3FaikVBDRkk2ohIxBo0O4o68g87y6Hug//G0S83sj6Wqyn8DgKMCRWQg+XYMgk5CwLVowA==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.6.0.tgz",
"integrity": "sha512-32LVrC7dAgQT39YZ0ieO/VzzpAflozs9mW5K0oKNef7S4ocCdk89E98eXApxOdei0JTf3vfseDCl1AUIp6MwJw==",
"cpu": [
"arm64"
],
@ -105,9 +105,9 @@
}
},
"node_modules/@biomejs/cli-linux-arm64-musl": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.5.0.tgz",
"integrity": "sha512-+1B3J8tWLTOvP3+00Cap+XhEXMvxwCHvVfuywUsB7Sqd66NWic3wKJuGbGcS3PuCWtGuIFsiQMNAGqiOXG4uBQ==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.6.0.tgz",
"integrity": "sha512-prww6AUuJ+IO/GziN3WjtGM/DNOVuPFxqWrK97wKTZygEDdA+o1qHUN2HeCkSyk084xnzbMSbls5xscAKAn43A==",
"cpu": [
"arm64"
],
@ -121,9 +121,9 @@
}
},
"node_modules/@biomejs/cli-linux-x64": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.5.0.tgz",
"integrity": "sha512-TlTsG+ptSmnDTUsAAYsXyGOXMcFiF8SiwhPdj4YsNkJRgx9M2curEVcTVm66FINIPK6VJTUcEDahFlx3NPUOzA==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.6.0.tgz",
"integrity": "sha512-b6mWu9Cu4w5B3K46wq9SlxKEZEEL6II/6HFNAuZ4YL8mOeQ0FTMU+wNMJFKkmkSE2zvim3xwW3PknmbLKbe3Mg==",
"cpu": [
"x64"
],
@ -137,9 +137,9 @@
}
},
"node_modules/@biomejs/cli-linux-x64-musl": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.5.0.tgz",
"integrity": "sha512-4S2rLluc0WT+XTbLTgcm9+5EEFwJmoGiUEzR6N0P2sIjZD8c5KNf9Ou46BP1Pdg5AgqV+IIClGPK1I80ApSh1Q==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.6.0.tgz",
"integrity": "sha512-NwitWeUKCy8G/rr+rgHPYirnrsOjJEJBWODdaRzweeFNcJjvO6de6AmNdSJzsewzLEaxjOWyoXU03MdzbGz/6Q==",
"cpu": [
"x64"
],
@ -153,9 +153,9 @@
}
},
"node_modules/@biomejs/cli-win32-arm64": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.5.0.tgz",
"integrity": "sha512-sWOi1SR+YqJuXElBncGRnWBR7IN7ni6GQY4Zm/vTpP6nVA0dX5C301eQUW1N/VnFQb6fyrJTcBslDUKyemsN/g==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.6.0.tgz",
"integrity": "sha512-DlNOL6mG+76iZS1gL/UiuMme7jnt+auzo2+u0aUq6UXYsb75juchwlnVLy2UV5CQjVBRB8+RM+KVoXRZ8NlBjQ==",
"cpu": [
"arm64"
],
@ -169,9 +169,9 @@
}
},
"node_modules/@biomejs/cli-win32-x64": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.5.0.tgz",
"integrity": "sha512-OoqgUXyzmRwX466bklOsWS7WdcvWtBuxF94DXATNe7bUiBa2tlW8QX7VVZvPnMKH57E5J619AkB3b5fhzyUhXA==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.6.0.tgz",
"integrity": "sha512-sXBcXIOGuG8/XcHqmnkhLIs0oy6Dp+TkH4Alr4WH/P8mNsp5GcStI/ZwbEiEoxA0P3Fi+oUppQ6srxaY2rSCHg==",
"cpu": [
"x64"
],
@ -386,9 +386,9 @@
}
},
"node_modules/discord-api-types": {
"version": "0.37.71",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.71.tgz",
"integrity": "sha512-oYDVWoiQdblr9DpwOgpi5d78dVhPcoN9YZCCqYZf2T0v9+iICs7k2bYGumoHuYMtaIitpp5aQNs+2guVkgjbOA=="
"version": "0.37.73",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.73.tgz",
"integrity": "sha512-mi915PBUxF1G233EwHKNegNAF/tVfiSRN9+hKwu0G3NpbtLXvWUxCuCjgSyY+QmQ6/Hvpqm0xs5HxzfvhAS20A=="
},
"node_modules/emoji-regex": {
"version": "10.3.0",
@ -450,15 +450,15 @@
}
},
"node_modules/husky": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz",
"integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==",
"version": "9.0.11",
"resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz",
"integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==",
"dev": true,
"bin": {
"husky": "lib/bin.js"
"husky": "bin.mjs"
},
"engines": {
"node": ">=14"
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/typicode"

View File

@ -4,7 +4,9 @@
"main": "./lib/index.js",
"module": "./lib/index.js",
"types": "./lib/index.d.ts",
"files": ["lib/**"],
"files": [
"lib/**"
],
"scripts": {
"build": "tsc --outDir ./lib",
"prepublishOnly": "npm run build",
@ -17,27 +19,30 @@
"author": "MARCROCK22",
"license": "ISC",
"dependencies": {
"chokidar": "^3.5.3",
"discord-api-types": "^0.37.71",
"magic-bytes.js": "^1.7.0",
"ts-mixer": "^6.0.3",
"chokidar": "^3.6.0",
"discord-api-types": "^0.37.73",
"magic-bytes.js": "^1.10.0",
"ts-mixer": "^6.0.4",
"ws": "^8.16.0"
},
"lint-staged": {
"*.ts": ["biome check --apply", "biome format --write"]
"*.ts": [
"biome check --apply",
"biome format --write"
]
},
"devDependencies": {
"@biomejs/biome": "1.6.0",
"@types/node": "^20.10.7",
"@types/node": "^20.11.25",
"@types/ws": "^8.5.10",
"husky": "^8.0.3",
"husky": "^9.0.11",
"lint-staged": "^15.2.2",
"typescript": "^5.3.3"
"typescript": "^5.4.2"
},
"optionalDependencies": {
"ioredis": "^5.3.2",
"tweetnacl": "^1.0.3",
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.39.0"
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.42.0"
},
"homepage": "https://seyfert.dev",
"repository": {
@ -47,7 +52,13 @@
"bugs": {
"url": "https://github.com/tiramisulabs/seyfert"
},
"keywords": ["api", "discord", "bots", "typescript", "botdev"],
"keywords": [
"api",
"discord",
"bots",
"typescript",
"botdev"
],
"publishConfig": {
"access": "public"
},
@ -57,4 +68,4 @@
"url": "https://github.com/socram03"
}
]
}
}

View File

@ -9,9 +9,7 @@ import {
type EmojiResolvable,
type When,
} from '../common';
import type { ButtonInteraction } from '../structures';
import { resolvePartialEmoji } from '../structures/extra/functions';
import type { ComponentCallback } from './types';
export type ButtonStylesForID = Exclude<ButtonStyle, ButtonStyle.Link>;
@ -20,9 +18,6 @@ export type ButtonStylesForID = Exclude<ButtonStyle, ButtonStyle.Link>;
* @template Type - The type of the button component.
*/
export class Button<Type extends boolean = boolean> {
/** @internal */
__exec?: ComponentCallback<ButtonInteraction>;
/**
* Creates a new Button instance.
* @param data - The initial data for the button.
@ -90,16 +85,6 @@ export class Button<Type extends boolean = boolean> {
return this;
}
/**
* Sets the callback function to be executed when the button is interacted with.
* @param func - The callback function to set.
* @returns The modified Button instance.
*/
run(func: ComponentCallback<ButtonInteraction>) {
this.__exec = func;
return this;
}
/**
* Converts the Button instance to its JSON representation.
* @returns The JSON representation of the Button instance.

View File

@ -26,7 +26,6 @@ import type {
} from '../structures';
import { resolvePartialEmoji } from '../structures/extra/functions';
import { BaseComponentBuilder, type OptionValuesLength } from './Base';
import type { ComponentCallback } from './types';
export type BuilderSelectMenus =
| RoleSelectMenu
@ -63,11 +62,9 @@ function mappedDefault<T extends SelectMenuDefaultValueType>(
*/
export class SelectMenu<
Select extends APISelectMenuComponent = APISelectMenuComponent,
//@ts-expect-error
Interaction = ComponentInteraction,
> extends BaseComponentBuilder<Select> {
/** @internal */
__exec?: ComponentCallback<Interaction>;
/**
* Sets the custom ID for the select menu.
* @param id - The custom ID for the select menu.
@ -108,16 +105,6 @@ export class SelectMenu<
this.data.disabled = disabled;
return this;
}
/**
* Sets the callback function to be executed when the select menu is interacted with.
* func - The callback function.
* @returns The current SelectMenu instance.
*/
run(func: ComponentCallback<Interaction>): this {
this.__exec = func;
return this;
}
}
/**

View File

@ -1,10 +1,14 @@
import type { ComponentInteraction, ModalSubmitInteraction } from '../structures/Interaction';
import type {
ComponentInteraction,
ModalSubmitInteraction,
StringSelectMenuInteraction,
} from '../structures/Interaction';
import type { Button } from './Button';
import type { TextInput } from './Modal';
import type { BuilderSelectMenus } from './SelectMenu';
export type ComponentCallback<T = ComponentInteraction> = (
interaction: T,
export type ComponentCallback = (
interaction: ComponentInteraction | StringSelectMenuInteraction,
stop: ComponentStopCallback,
refresh: ComponentRefreshCallback,
) => any;

View File

@ -209,8 +209,8 @@ export async function onInteractionCreate(
case InteractionType.MessageComponent:
{
const interaction = BaseInteraction.from(self, body, __reply) as ComponentInteraction;
if (self.components.hasComponent(body.message.interaction?.id ?? body.message.id, interaction.customId)) {
await self.components.onComponent(body.message.interaction?.id ?? body.message.id, interaction);
if (self.components.hasComponent([body.message.id, body.id], interaction.customId)) {
await self.components.onComponent([body.message.id, body.id], interaction);
} else {
await self.components.executeComponent(interaction);
}

View File

@ -10,7 +10,7 @@ import {
type WebhookMessage,
} from '../..';
import type { Client, WorkerClient } from '../../client';
import { MessageFlags, type If, type UnionToTuple } from '../../common';
import { MessageFlags, type When, type If, type UnionToTuple } from '../../common';
import type { InteractionCreateBodyRequest, InteractionMessageUpdateBodyRequest } from '../../common/types/write';
import {
Message,
@ -56,8 +56,11 @@ export class CommandContext<T extends OptionsRecord = {}, M extends keyof Regist
return this.client.langs.get(this.interaction?.locale ?? this.client.langs.defaultLang ?? 'en-US');
}
async write(body: InteractionCreateBodyRequest) {
if (this.interaction) return this.interaction.write(body);
async write<FR extends boolean = false>(
body: InteractionCreateBodyRequest,
fetchReply?: FR,
): Promise<When<FR, WebhookMessage | Message, void | WebhookMessage | Message>> {
if (this.interaction) return this.interaction.write(body, fetchReply);
const options = (this.client as Client | WorkerClient).options?.commands;
return (this.messageResponse = await (this.message! as Message)[
!this.messageResponse && options?.reply?.(this) ? 'reply' : 'write'
@ -82,12 +85,15 @@ export class CommandContext<T extends OptionsRecord = {}, M extends keyof Regist
return this.messageResponse!.delete();
}
editOrReply(body: InteractionCreateBodyRequest | InteractionMessageUpdateBodyRequest) {
if (this.interaction) return this.interaction.editOrReply(body as InteractionCreateBodyRequest);
editOrReply<FR extends boolean = false>(
body: InteractionCreateBodyRequest | InteractionMessageUpdateBodyRequest,
fetchReply?: FR,
): Promise<When<FR, WebhookMessage | Message, void | WebhookMessage | Message>> {
if (this.interaction) return this.interaction.editOrReply(body as InteractionCreateBodyRequest, fetchReply);
if (this.messageResponse) {
return this.editResponse(body);
}
return this.write(body as InteractionCreateBodyRequest);
return this.write(body as InteractionCreateBodyRequest, fetchReply);
}
async fetchResponse(): Promise<

View File

@ -1,7 +1,8 @@
import { CommandContext, type ReturnCache } from '../..';
import { CommandContext, type WebhookMessage, type ReturnCache } from '../..';
import {
ApplicationCommandType,
MessageFlags,
type When,
toSnakeCase,
type InteractionCreateBodyRequest,
type InteractionMessageUpdateBodyRequest,
@ -61,8 +62,11 @@ export class MenuCommandContext<
return this.client.langs.get(this.interaction.locale);
}
write(body: InteractionCreateBodyRequest) {
return this.interaction.write(body);
write<FR extends boolean = false>(
body: InteractionCreateBodyRequest,
fetchReply?: FR,
): Promise<When<FR, WebhookMessage, void | WebhookMessage>> {
return this.interaction.write(body, fetchReply);
}
get modal() {
@ -81,8 +85,11 @@ export class MenuCommandContext<
return this.interaction.deleteResponse();
}
editOrReply(body: InteractionCreateBodyRequest | InteractionMessageUpdateBodyRequest) {
return this.interaction.editOrReply(body as InteractionCreateBodyRequest);
editOrReply<FR extends boolean = false>(
body: InteractionCreateBodyRequest | InteractionMessageUpdateBodyRequest,
fetchReply?: FR,
): Promise<When<FR, WebhookMessage | Message, void | WebhookMessage | Message>> {
return this.interaction.editOrReply(body as InteractionCreateBodyRequest, fetchReply);
}
fetchResponse() {

View File

@ -24,7 +24,6 @@ export class MessageShorter extends BaseShorter {
files: parsedFiles,
})
.then(message => {
this.client.components.onRequestMessage(body, message);
return new Message(this.client, message);
});
},
@ -38,7 +37,6 @@ export class MessageShorter extends BaseShorter {
files: parsedFiles,
})
.then(message => {
this.client.components.onRequestUpdateMessage(body, message);
return new Message(this.client, message);
});
},

View File

@ -12,17 +12,12 @@ import type {
} from '..';
import type { RawFile } from '../../api';
import type { ActionRow, Attachment, AttachmentBuilder, BuilderComponents, Embed, Modal } from '../../builders';
import type { ComponentsListener } from '../../components/listener';
import type { OmitInsert } from './util';
export interface ResolverProps {
embeds?: Embed[] | APIEmbed[] | undefined;
components?:
| ComponentsListener<BuilderComponents>
| APIActionRowComponent<APIMessageActionRowComponent>[]
| ActionRow<BuilderComponents>[]
| undefined;
components?: APIActionRowComponent<APIMessageActionRowComponent>[] | ActionRow<BuilderComponents>[] | undefined;
files?: AttachmentBuilder[] | Attachment[] | RawFile[] | undefined;
}

View File

@ -1,40 +1,17 @@
import { Button, Modal, SelectMenu, type BuilderComponents } from '../builders';
import type { ComponentCallback, ModalSubmitCallback } from '../builders/types';
import type { ComponentCallback, ListenerOptions, ModalSubmitCallback } from '../builders/types';
import type { BaseClient } from '../client/base';
import { LimitedCollection } from '../collection';
import {
BaseHandler,
InteractionResponseType,
magicImport,
type APIMessage,
type APIModalInteractionResponseCallbackData,
type Logger,
type OnFailCallback,
} from '../common';
import type {
InteractionMessageUpdateBodyRequest,
MessageCreateBodyRequest,
MessageUpdateBodyRequest,
ModalCreateBodyRequest,
ResolverProps,
} from '../common/types/write';
import type { ComponentInteraction, ModalSubmitInteraction, ReplyInteractionBody } from '../structures';
import { BaseHandler, magicImport, type Logger, type OnFailCallback } from '../common';
import type { ComponentInteraction, ModalSubmitInteraction } from '../structures';
import { ComponentCommand, InteractionCommandType, ModalCommand } from './command';
import { ComponentsListener } from './listener';
type COMPONENTS = {
buttons: Partial<
Record<
string,
{
callback: ComponentCallback;
}
>
>;
listener: ComponentsListener<BuilderComponents>;
components: Partial<Record<string, ComponentCallback>>;
options?: ListenerOptions;
messageId?: string;
idle?: NodeJS.Timeout;
timeout?: NodeJS.Timeout;
__run: (customId: string, callback: ComponentCallback) => any;
};
export class ComponentHandler extends BaseHandler {
@ -56,28 +33,69 @@ export class ComponentHandler extends BaseHandler {
this.onFail = cb;
}
hasComponent(id: string, customId: string) {
return !!this.values.get(id)?.buttons?.[customId];
createComponentCollector(messageId: string, options: ListenerOptions = {}) {
this.values.set(messageId, {
components: {},
options,
idle: options.idle
? setTimeout(() => {
this.deleteValue(messageId);
options.onStop?.('idle', () => {
this.createComponentCollector(messageId, options);
});
}, options.idle)
: undefined,
timeout: options.timeout
? setTimeout(() => {
this.deleteValue(messageId);
options.onStop?.('timeout', () => {
this.createComponentCollector(messageId, options);
});
}, options.timeout)
: undefined,
__run: (customId, callback) => {
if (this.values.has(messageId)) {
this.values.get(messageId)!.components[customId] = callback;
}
},
});
return {
run: this.values.get(messageId)!.__run,
stop: (reason?: string) => {
this.deleteValue(messageId);
options.onStop?.(reason, () => {
this.createComponentCollector(messageId, options);
});
},
};
}
async onComponent(id: string, interaction: ComponentInteraction) {
const row = this.values.get(id);
const component = row?.buttons?.[interaction.customId];
if (!component) return;
if (row.listener.options?.filter) {
if (!(await row.listener.options.filter(interaction))) return;
async onComponent(ids: [string, string], interaction: ComponentInteraction) {
for (const id of ids) {
const row = this.values.get(id);
const component = row?.components?.[interaction.customId];
if (!component) continue;
if (row.options?.filter) {
if (!(await row.options.filter(interaction))) return;
}
row.idle?.refresh();
await component(
interaction,
reason => {
row.options?.onStop?.(reason ?? 'stop');
this.deleteValue(id);
},
() => {
this.resetTimeouts(id);
},
);
break;
}
row.idle?.refresh();
await component.callback(
interaction,
reason => {
row.listener.options?.onStop?.(reason ?? 'stop');
this.deleteValue(id);
},
() => {
this.resetTimeouts(id);
},
);
}
hasComponent(ids: [string, string], customId: string) {
return ids.some(id => this.values.get(id)?.components?.[customId]);
}
resetTimeouts(id: string) {
@ -97,107 +115,20 @@ export class ComponentHandler extends BaseHandler {
return this.modals.get(interaction.user.id)?.(interaction);
}
__setComponents(id: string, record: NonNullable<ResolverProps['components']>) {
this.deleteValue(id);
if (!(record instanceof ComponentsListener)) return;
const components: COMPONENTS = {
buttons: {},
listener: record,
};
if ((record.options.idle ?? -1) > 0) {
components.idle = setTimeout(() => {
clearTimeout(components.timeout);
clearTimeout(components.idle);
record.options?.onStop?.('idle', () => {
this.__setComponents(id, record);
});
this.values.delete(id);
}, record.options.idle);
}
if ((record.options.timeout ?? -1) > 0) {
components.timeout = setTimeout(() => {
clearTimeout(components.timeout);
clearTimeout(components.idle);
record.options?.onStop?.('timeout', () => {
this.__setComponents(id, record);
});
this.values.delete(id);
}, record.options.timeout);
}
for (const actionRow of record.components) {
for (const child of actionRow.components) {
if ((child instanceof SelectMenu || child instanceof Button) && 'custom_id' in child.data) {
components.buttons[child.data.custom_id!] = {
callback: child.__exec as ComponentCallback,
};
}
}
}
if (Object.entries(components.buttons).length) {
this.values.set(id, components);
}
}
protected __setModal(id: string, record: APIModalInteractionResponseCallbackData | ModalCreateBodyRequest) {
if ('__exec' in record) {
this.modals.set(id, record.__exec!);
}
}
deleteValue(id: string, reason?: string) {
const component = this.values.get(id);
if (component) {
if (reason !== undefined) component.listener.options.onStop?.(reason);
if (reason !== undefined) component.options?.onStop?.(reason);
clearTimeout(component.timeout);
clearTimeout(component.idle);
this.values.delete(id);
}
}
onRequestInteraction(interactionId: string, interaction: ReplyInteractionBody) {
// @ts-expect-error dapi
if (!interaction.data) {
return;
}
switch (interaction.type) {
case InteractionResponseType.ChannelMessageWithSource:
case InteractionResponseType.UpdateMessage:
if (!interaction.data.components) return;
this.__setComponents(interactionId, interaction.data.components);
break;
case InteractionResponseType.Modal:
if (!(interaction.data instanceof Modal)) return;
this.__setModal(interactionId, interaction.data);
break;
}
}
onMessageDelete(id: string) {
this.deleteValue(id, 'messageDelete');
}
onRequestMessage(body: MessageCreateBodyRequest, message: APIMessage) {
if (!body.components) {
return;
}
this.__setComponents(message.id, body.components);
}
onRequestInteractionUpdate(body: InteractionMessageUpdateBodyRequest, message: APIMessage) {
if (!body.components) {
return;
}
this.__setComponents(message.interaction!.id, body.components);
}
onRequestUpdateMessage(body: MessageUpdateBodyRequest, message: APIMessage) {
if (!body.components) return;
this.__setComponents(message.id, body.components);
}
async load(componentsDir: string) {
const paths = await this.loadFilesK<{ new (): ModalCommand | ComponentCommand }>(
await this.getFiles(componentsDir),

View File

@ -22,7 +22,6 @@ export type MessageComponents =
export type ActionRowMessageComponents = Exclude<MessageComponents, TextInputComponent>;
export * from './command';
export * from './listener';
/**
* Return a new component instance based on the component type.

View File

@ -1,15 +0,0 @@
import type { ActionRow, BuilderComponents, ListenerOptions } from '../builders';
import type { RestOrArray } from '../common';
export class ComponentsListener<T extends BuilderComponents> {
components: ActionRow<T>[] = [];
idle?: NodeJS.Timeout;
timeout?: NodeJS.Timeout;
constructor(readonly options: ListenerOptions = {}) {}
addRows(...row: RestOrArray<ActionRow<T>>) {
this.components = this.components.concat(row.flat());
return this;
}
}

View File

@ -51,7 +51,6 @@ import type {
MessageWebhookCreateBodyRequest,
ModalCreateBodyRequest,
} from '../common/types/write';
import { ComponentsListener } from '../components/listener';
import { InteractionGuildMember, type AllChannels } from './';
import { GuildRole } from './GuildRole';
import { Message, type WebhookMessage } from './Message';
@ -126,14 +125,12 @@ export class BaseInteraction<
case InteractionResponseType.UpdateMessage:
return {
type: body.type,
// @ts-ignore
data: {
// @ts-ignore
...(body.data ?? {}),
// @ts-expect-error
components:
(body.data?.components instanceof ComponentsListener
? body.data.components.components
: body.data!.components
)?.map(x => (x instanceof ActionRow ? x.toJSON() : x)) ?? undefined,
// @ts-ignore
components: body.data?.components?.map(x => (x instanceof ActionRow ? x.toJSON() : x)) ?? undefined,
embeds: body.data?.embeds?.map(x => (x instanceof Embed ? x.toJSON() : x)) ?? undefined,
attachments: body.data?.attachments?.map((x, i) => ({ id: i, ...resolveAttachment(x) })) ?? undefined,
},
@ -169,10 +166,7 @@ export class BaseInteraction<
) {
return {
...body,
components:
(body?.components instanceof ComponentsListener ? body.components.components : body.components)?.map(x =>
x instanceof ActionRow ? x.toJSON() : x,
) ?? undefined,
components: body.components?.map(x => (x instanceof ActionRow ? x.toJSON() : x)) ?? undefined,
embeds: body?.embeds?.map(x => (x instanceof Embed ? x.toJSON() : x)) ?? undefined,
// attachments: body.attachments?.map((x, i) => ({ id: i, ...resolveAttachment(x) })) ?? undefined,
} as T;
@ -199,16 +193,10 @@ export class BaseInteraction<
this.matchReplied(rest, body.type, await resolveFiles(files));
// @ts-expect-error
} else this.matchReplied(body.data, body.type);
this.client.components.onRequestInteraction(
body.type === InteractionResponseType.Modal
? this.user.id
: body.type === InteractionResponseType.UpdateMessage
? this.message!.interaction?.id ?? this.message!.id
: this.id,
body,
);
// @ts-expect-error
if (body.data instanceof Modal)
// @ts-expect-error
this.client.components.modals.set(this.user.id, (body.data as Modal).__exec);
await this.replied;
}
@ -385,8 +373,6 @@ export class Interaction<
body: BaseInteraction.transformBody(data),
files: files ? await resolveFiles(files) : undefined,
});
this.client.components.onRequestInteractionUpdate(body, apiMessage);
return new Message(this.client, apiMessage);
}
@ -414,8 +400,6 @@ export class Interaction<
body: BaseInteraction.transformBody(body),
files: files as RawFile[] | undefined,
});
this.client.components.onRequestMessage(body, apiMessage);
return new Message(this.client, apiMessage);
}
}
@ -452,6 +436,7 @@ export class ComponentInteraction<
declare channelId: string;
declare channel: AllChannels;
declare type: InteractionType.MessageComponent;
declare message: Message;
update(data: ComponentInteractionMessageUpdate) {
return this.reply({

View File

@ -1,3 +1,4 @@
import type { ListenerOptions } from '../builders';
import type { BaseClient } from '../client/base';
import type {
APIChannelMention,
@ -45,6 +46,10 @@ export class BaseMessage extends DiscordBase {
this.patch(data);
}
createComponentCollector(options?: ListenerOptions) {
return this.client.components.createComponentCollector(this.id, options);
}
get url() {
return messageLink(this.channelId, this.id, this.guildId);
}

View File

@ -35,7 +35,6 @@ import type {
StringToNumber,
ToClass,
} from '../common';
import { ComponentsListener } from '../components';
import type { GuildMember } from './GuildMember';
import type { GuildRole } from './GuildRole';
import { Webhook } from './Webhook';
@ -255,13 +254,8 @@ export class MessagesMethods extends DiscordBase {
static transformMessageBody<T>(body: MessageCreateBodyRequest | MessageUpdateBodyRequest) {
return {
...body,
components: body.components
? (body?.components instanceof ComponentsListener ? body.components.components : body.components).map(x =>
'toJSON' in x ? x.toJSON() : x,
)
: undefined,
components: body.components?.map(x => ('toJSON' in x ? x.toJSON() : x)) ?? undefined,
embeds: body.embeds?.map(x => (x instanceof Embed ? x.toJSON() : x)) ?? undefined,
//?
attachments: body.attachments?.map((x, i) => ({ id: i, ...resolveAttachment(x) })) ?? undefined,
} as T;
}