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

View File

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

View File

@ -9,9 +9,7 @@ import {
type EmojiResolvable, type EmojiResolvable,
type When, type When,
} from '../common'; } from '../common';
import type { ButtonInteraction } from '../structures';
import { resolvePartialEmoji } from '../structures/extra/functions'; import { resolvePartialEmoji } from '../structures/extra/functions';
import type { ComponentCallback } from './types';
export type ButtonStylesForID = Exclude<ButtonStyle, ButtonStyle.Link>; 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. * @template Type - The type of the button component.
*/ */
export class Button<Type extends boolean = boolean> { export class Button<Type extends boolean = boolean> {
/** @internal */
__exec?: ComponentCallback<ButtonInteraction>;
/** /**
* Creates a new Button instance. * Creates a new Button instance.
* @param data - The initial data for the button. * @param data - The initial data for the button.
@ -90,16 +85,6 @@ export class Button<Type extends boolean = boolean> {
return this; 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. * Converts the Button instance to its JSON representation.
* @returns The JSON representation of the Button instance. * @returns The JSON representation of the Button instance.

View File

@ -26,7 +26,6 @@ import type {
} from '../structures'; } from '../structures';
import { resolvePartialEmoji } from '../structures/extra/functions'; import { resolvePartialEmoji } from '../structures/extra/functions';
import { BaseComponentBuilder, type OptionValuesLength } from './Base'; import { BaseComponentBuilder, type OptionValuesLength } from './Base';
import type { ComponentCallback } from './types';
export type BuilderSelectMenus = export type BuilderSelectMenus =
| RoleSelectMenu | RoleSelectMenu
@ -63,11 +62,9 @@ function mappedDefault<T extends SelectMenuDefaultValueType>(
*/ */
export class SelectMenu< export class SelectMenu<
Select extends APISelectMenuComponent = APISelectMenuComponent, Select extends APISelectMenuComponent = APISelectMenuComponent,
//@ts-expect-error
Interaction = ComponentInteraction, Interaction = ComponentInteraction,
> extends BaseComponentBuilder<Select> { > extends BaseComponentBuilder<Select> {
/** @internal */
__exec?: ComponentCallback<Interaction>;
/** /**
* Sets the custom ID for the select menu. * Sets the custom ID for the select menu.
* @param id - 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; this.data.disabled = disabled;
return this; 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 { Button } from './Button';
import type { TextInput } from './Modal'; import type { TextInput } from './Modal';
import type { BuilderSelectMenus } from './SelectMenu'; import type { BuilderSelectMenus } from './SelectMenu';
export type ComponentCallback<T = ComponentInteraction> = ( export type ComponentCallback = (
interaction: T, interaction: ComponentInteraction | StringSelectMenuInteraction,
stop: ComponentStopCallback, stop: ComponentStopCallback,
refresh: ComponentRefreshCallback, refresh: ComponentRefreshCallback,
) => any; ) => any;

View File

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

View File

@ -10,7 +10,7 @@ import {
type WebhookMessage, type WebhookMessage,
} from '../..'; } from '../..';
import type { Client, WorkerClient } from '../../client'; 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 type { InteractionCreateBodyRequest, InteractionMessageUpdateBodyRequest } from '../../common/types/write';
import { import {
Message, 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'); return this.client.langs.get(this.interaction?.locale ?? this.client.langs.defaultLang ?? 'en-US');
} }
async write(body: InteractionCreateBodyRequest) { async write<FR extends boolean = false>(
if (this.interaction) return this.interaction.write(body); 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; const options = (this.client as Client | WorkerClient).options?.commands;
return (this.messageResponse = await (this.message! as Message)[ return (this.messageResponse = await (this.message! as Message)[
!this.messageResponse && options?.reply?.(this) ? 'reply' : 'write' !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(); return this.messageResponse!.delete();
} }
editOrReply(body: InteractionCreateBodyRequest | InteractionMessageUpdateBodyRequest) { editOrReply<FR extends boolean = false>(
if (this.interaction) return this.interaction.editOrReply(body as InteractionCreateBodyRequest); 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) { if (this.messageResponse) {
return this.editResponse(body); return this.editResponse(body);
} }
return this.write(body as InteractionCreateBodyRequest); return this.write(body as InteractionCreateBodyRequest, fetchReply);
} }
async fetchResponse(): Promise< async fetchResponse(): Promise<

View File

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

View File

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

View File

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

View File

@ -1,40 +1,17 @@
import { Button, Modal, SelectMenu, type BuilderComponents } from '../builders'; import type { ComponentCallback, ListenerOptions, ModalSubmitCallback } from '../builders/types';
import type { ComponentCallback, ModalSubmitCallback } from '../builders/types';
import type { BaseClient } from '../client/base'; import type { BaseClient } from '../client/base';
import { LimitedCollection } from '../collection'; import { LimitedCollection } from '../collection';
import { import { BaseHandler, magicImport, type Logger, type OnFailCallback } from '../common';
BaseHandler, import type { ComponentInteraction, ModalSubmitInteraction } from '../structures';
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 { ComponentCommand, InteractionCommandType, ModalCommand } from './command'; import { ComponentCommand, InteractionCommandType, ModalCommand } from './command';
import { ComponentsListener } from './listener';
type COMPONENTS = { type COMPONENTS = {
buttons: Partial< components: Partial<Record<string, ComponentCallback>>;
Record< options?: ListenerOptions;
string,
{
callback: ComponentCallback;
}
>
>;
listener: ComponentsListener<BuilderComponents>;
messageId?: string; messageId?: string;
idle?: NodeJS.Timeout; idle?: NodeJS.Timeout;
timeout?: NodeJS.Timeout; timeout?: NodeJS.Timeout;
__run: (customId: string, callback: ComponentCallback) => any;
}; };
export class ComponentHandler extends BaseHandler { export class ComponentHandler extends BaseHandler {
@ -56,28 +33,69 @@ export class ComponentHandler extends BaseHandler {
this.onFail = cb; this.onFail = cb;
} }
hasComponent(id: string, customId: string) { createComponentCollector(messageId: string, options: ListenerOptions = {}) {
return !!this.values.get(id)?.buttons?.[customId]; 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) { async onComponent(ids: [string, string], interaction: ComponentInteraction) {
for (const id of ids) {
const row = this.values.get(id); const row = this.values.get(id);
const component = row?.buttons?.[interaction.customId]; const component = row?.components?.[interaction.customId];
if (!component) return; if (!component) continue;
if (row.listener.options?.filter) { if (row.options?.filter) {
if (!(await row.listener.options.filter(interaction))) return; if (!(await row.options.filter(interaction))) return;
} }
row.idle?.refresh(); row.idle?.refresh();
await component.callback( await component(
interaction, interaction,
reason => { reason => {
row.listener.options?.onStop?.(reason ?? 'stop'); row.options?.onStop?.(reason ?? 'stop');
this.deleteValue(id); this.deleteValue(id);
}, },
() => { () => {
this.resetTimeouts(id); this.resetTimeouts(id);
}, },
); );
break;
}
}
hasComponent(ids: [string, string], customId: string) {
return ids.some(id => this.values.get(id)?.components?.[customId]);
} }
resetTimeouts(id: string) { resetTimeouts(id: string) {
@ -97,107 +115,20 @@ export class ComponentHandler extends BaseHandler {
return this.modals.get(interaction.user.id)?.(interaction); 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) { deleteValue(id: string, reason?: string) {
const component = this.values.get(id); const component = this.values.get(id);
if (component) { if (component) {
if (reason !== undefined) component.listener.options.onStop?.(reason); if (reason !== undefined) component.options?.onStop?.(reason);
clearTimeout(component.timeout); clearTimeout(component.timeout);
clearTimeout(component.idle); clearTimeout(component.idle);
this.values.delete(id); 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) { onMessageDelete(id: string) {
this.deleteValue(id, 'messageDelete'); 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) { async load(componentsDir: string) {
const paths = await this.loadFilesK<{ new (): ModalCommand | ComponentCommand }>( const paths = await this.loadFilesK<{ new (): ModalCommand | ComponentCommand }>(
await this.getFiles(componentsDir), await this.getFiles(componentsDir),

View File

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

View File

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

View File

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