Compare commits

...

46 Commits
v3.1.1 ... main

Author SHA1 Message Date
6d5b162a2d
chore: bump version to 3.2.4 2025-06-23 10:13:49 -04:00
a15eb2035f
Merge branch 'main' of https://github.com/tiramisulabs/seyfert 2025-06-23 10:04:21 -04:00
c4d1c1ce90
fix(api): handle 429 in shared scopes 2025-06-23 10:04:07 -04:00
eadf63450b chore: bump version to 3.2.3 2025-06-22 23:55:03 -04:00
0720d0c170 fix: accept null as content value 2025-06-22 00:20:52 -04:00
798f648955
feat: waitFor modals (#346)
* feat: modal#waitFor

* refactor: update waitFor method to improve promise handling and timeout logic

* feat: enhance modal handling with options for wait time and improved promise resolution

---------

Co-authored-by: MARCROCK22 <marcos22dev@gmail.com>
2025-06-19 23:12:42 -04:00
6e037ae964 chore: bump version to 3.2.2 2025-06-17 16:48:01 -04:00
0608c7ad85 fix: i dont know why it was like that 2025-06-17 16:47:46 -04:00
db38f49ca9 chore: bump 3.2.1 2025-06-17 16:19:49 -04:00
325a39f1bf ci: add npm token verification step in dev workflow 2025-06-14 19:42:42 -04:00
e810b6eb52 refactor: update customId matching logic and rename asyncRun to waitFor 2025-06-14 19:24:53 -04:00
Free 公園
b84d462ce2
feat: use regex in customId component command (#345) 2025-06-13 22:08:47 -04:00
61413181bb Merge branch 'main' of https://github.com/tiramisulabs/seyfert 2025-06-13 20:34:56 -04:00
30a066e68d feat: awaitable collector 2025-06-13 20:34:43 -04:00
95619a8a89 refactor: change listActiveThreads method to remove async keyword 2025-06-13 13:38:26 -04:00
84806f3c54 Merge branch 'main' of https://github.com/tiramisulabs/seyfert 2025-06-13 13:30:49 -04:00
422cfb2a80 feat: list active threads 2025-06-13 04:11:07 -04:00
David
3f6c6dc4d4
fix: add error handling for undefined locale (#344) 2025-06-13 01:11:56 -04:00
d6a405469d fix: revert, rerun later 2025-06-12 22:32:09 -04:00
1903257e46 fix: streamline publish workflow by removing unnecessary steps 2025-06-12 22:27:50 -04:00
0b00e2d19b fix: add id-token write permission to publish workflow 2025-06-12 22:23:18 -04:00
970ed980cf chore: update version 3.2.0 2025-06-12 22:14:20 -04:00
935cce99f6 fix: mentionableMenu accept channels 2025-06-12 18:12:53 -04:00
5de23ffe58
fix: Error with ThreadChannel#webhooks for bad patchClass interaction (#341) 2025-05-23 14:53:50 -04:00
e4f715515c fix: the fix 2025-05-23 06:43:57 -04:00
7998577b07 fix: workers correctly assing ws properties 2025-05-23 06:15:08 -04:00
05cdc20d7f fix: remove unused ObjectToLower type declarations from index.ts 2025-05-22 17:02:06 -04:00
e095edd20f fix: update ObjectToLower and ObjectToSnake types to preserve numeric keys 2025-05-22 17:01:12 -04:00
d9aef4335d fix: xd 2025-05-20 20:20:11 -04:00
3350570334 fix: make pendingGuilds optional and update related logic for guild handling 2025-05-19 18:16:47 -04:00
c20f2fd0a3 feat: implement heartbeater for managing worker heartbeat messages 2025-05-17 12:58:39 -04:00
0d8ad177b7 fix: replace ws.removeListener with ws.removeEventListener for 'pong' event 2025-05-16 08:46:25 -04:00
089cfab6da fix: replace ws.on with ws.addEventListener for 'pong' event 2025-05-16 08:18:52 -04:00
fb9c59a51b feat: add resetTimeouts method to ComponentHandler 2025-05-12 18:12:10 -04:00
92ab65be7b feat: make deferReplyResponse and reply awaitable 2025-05-08 21:09:15 -04:00
23c9c2a710 feat: workerClient.calculateShardId 2025-05-07 18:41:46 -04:00
b4324f9487 fix: type error 2025-05-05 20:01:16 -04:00
f74b75fcef fix: change return types from 'never' to 'undefined' for various API methods 2025-04-29 08:30:01 -04:00
9cbde76ad0 fix: update promise return types to undefined for delete and post methods 2025-04-29 08:25:05 -04:00
66b5ca34a9 fix: dx 2025-04-28 04:04:35 -04:00
4574ca018f
fix: xd 2025-04-28 03:44:17 -04:00
34ca3e293e
fix(roles): delete don't return role anymore 2025-04-28 03:42:46 -04:00
a358bb0a04 chore: this thing 2025-04-27 21:30:28 -04:00
6cf75e9ad2 chore: 3.1.2 2025-04-27 21:24:24 -04:00
60108b4c42 fix: empty data 2025-04-27 16:25:30 -04:00
e8c686a1df chore: update dependencies 2025-04-27 14:27:21 -04:00
101 changed files with 1158 additions and 1283 deletions

View File

@ -12,8 +12,10 @@ jobs:
permissions: permissions:
id-token: write id-token: write
if: github.repository == 'tiramisulabs/seyfert' if: github.repository == 'tiramisulabs/seyfert'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
steps: steps:
- name: check out code - name: Check out code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Node - name: Install Node
@ -29,12 +31,13 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: pnpm install --frozen-lockfile run: pnpm install --frozen-lockfile
- name: Verify npm token
run: npm whoami
- name: Publish dev tag - name: Publish dev tag
id: publish id: publish
run: | run: |
new_version=$(npm version prerelease --preid dev-${{github.run_id}} --no-git-tag-version) new_version=$(npm version prerelease --preid dev-${{github.run_id}} --no-git-tag-version)
echo "New version: $new_version" echo "New version: $new_version"
npm config set //registry.npmjs.org/:_authToken ${NODE_AUTH_TOKEN} npm config set //registry.npmjs.org/:_authToken ${NODE_AUTH_TOKEN}
npm publish --provenance --tag=dev npm publish --provenance --tag=dev
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -1,80 +0,0 @@
name: Create Release
on:
push:
branches:
- build
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: check out code
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'
- uses: pnpm/action-setup@v4
with:
version: 9
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build project
run: pnpm run build
- name: Get version from package.json
id: get_version
run: echo "::set-output name=version::$(node -p "require('./package.json').version")"
- name: Set repository variable
run: echo "REPO=${{ github.repository }}" >> $GITHUB_ENV
- name: Get and format commits
run: |
last_tag=$(git tag --sort=-v:refname | head -n 1)
if [ -z "$last_tag" ]; then
git log --pretty=format:"%h - %s" > commits.txt
else
git log ${last_tag}..HEAD --pretty=format:"%h - %s" > commits.txt
fi
sed -e "s|^\([a-f0-9]\+\) - \(.*\)|- [\1](https://github.com/$REPO/commit/\1) - \2|" commits.txt > formatted_commits.txt
echo "Changes in this release:" > release_body.txt
cat formatted_commits.txt >> release_body.txt
- name: Create tag
run: |
git tag v${{ steps.get_version.outputs.version }}
git push origin v${{ steps.get_version.outputs.version }}
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ steps.get_version.outputs.version }}
release_name: Release v${{ steps.get_version.outputs.version }}
body_path: release_body.txt
draft: false
prerelease: false
- name: Zip dist folder
run: zip -r dist.zip src
- name: Upload Release Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./dist.zip
asset_name: dist.zip
asset_content_type: application/zip

View File

@ -1 +1 @@
npx lint-staged npx biome check --write

View File

@ -1,5 +1,5 @@
{ {
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", "$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
"vcs": { "vcs": {
"enabled": true, "enabled": true,
"clientKind": "git", "clientKind": "git",
@ -8,7 +8,16 @@
}, },
"files": { "files": {
"ignoreUnknown": true, "ignoreUnknown": true,
"ignore": ["node_modules/", "build", "lib", "__test__", "package.json", "tsconfig.json", ".vscode"] "includes": [
"**/src/**",
"!/node_modules/",
"!/build",
"!/lib",
"!/__test__",
"!/package.json",
"!/tsconfig.json",
"!/.vscode"
]
}, },
"formatter": { "formatter": {
"enabled": true, "enabled": true,
@ -18,14 +27,17 @@
"lineEnding": "crlf", "lineEnding": "crlf",
"formatWithErrors": true "formatWithErrors": true
}, },
"organizeImports": { "assist": {
"enabled": true "actions": {
"source": {
"organizeImports": "on"
}
}
}, },
"linter": { "linter": {
"enabled": true, "enabled": true,
"rules": { "rules": {
"recommended": false, "recommended": false,
"all": true,
"security": { "security": {
"noGlobalEval": "off" "noGlobalEval": "off"
}, },
@ -41,9 +53,7 @@
}, },
"correctness": { "correctness": {
"noNodejsModules": "off", "noNodejsModules": "off",
"useImportExtensions": "off", "useImportExtensions": "off"
"noUnusedFunctionParameters": "off",
"noUnusedVariables": "off"
}, },
"style": { "style": {
"noDefaultExport": "off", "noDefaultExport": "off",
@ -56,8 +66,7 @@
"noParameterAssign": "off", "noParameterAssign": "off",
"useFilenamingConvention": "off", "useFilenamingConvention": "off",
"useEnumInitializers": "off", "useEnumInitializers": "off",
"useExplicitLengthCheck": "off", "useExplicitLengthCheck": "off"
"noNamespaceImport": "off"
}, },
"complexity": { "complexity": {
"noForEach": "off", "noForEach": "off",
@ -69,7 +78,8 @@
"noBarrelFile": "off", "noBarrelFile": "off",
"noDelete": "off", "noDelete": "off",
"noReExportAll": "off", "noReExportAll": "off",
"useTopLevelRegex": "off" "useTopLevelRegex": "off",
"noNamespaceImport": "off"
} }
} }
}, },

View File

@ -1,6 +1,6 @@
{ {
"name": "seyfert", "name": "seyfert",
"version": "3.1.1", "version": "3.2.4",
"description": "The most advanced framework for discord bots", "description": "The most advanced framework for discord bots",
"main": "./lib/index.js", "main": "./lib/index.js",
"module": "./lib/index.js", "module": "./lib/index.js",
@ -21,15 +21,14 @@
"author": "MARCROCK22", "author": "MARCROCK22",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@biomejs/biome": "1.9.4", "@biomejs/biome": "2.0.0",
"@changesets/cli": "^2.28.1", "@changesets/cli": "^2.29.4",
"@commitlint/cli": "^19.8.0", "@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.0", "@commitlint/config-conventional": "^19.8.1",
"@types/node": "^22.14.0", "@types/node": "^24.0.3",
"husky": "^9.1.7", "husky": "^9.1.7",
"lint-staged": "^15.5.0", "typescript": "^5.8.3",
"typescript": "^5.8.2", "vitest": "^3.2.4"
"vitest": "^3.1.1"
}, },
"homepage": "https://seyfert.dev", "homepage": "https://seyfert.dev",
"repository": { "repository": {
@ -65,11 +64,6 @@
"url": "https://github.com/Drylozu" "url": "https://github.com/Drylozu"
} }
], ],
"lint-staged": {
"*.ts": [
"biome check --write"
]
},
"pnpm": { "pnpm": {
"onlyBuiltDependencies": [ "onlyBuiltDependencies": [
"@biomejs/biome", "@biomejs/biome",

1311
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -71,7 +71,7 @@ export interface ApplicationRoutes {
patch( patch(
args: RestArguments<RESTPatchAPIApplicationGuildCommandJSONBody>, args: RestArguments<RESTPatchAPIApplicationGuildCommandJSONBody>,
): Promise<RESTPatchAPIApplicationGuildCommandResult>; ): Promise<RESTPatchAPIApplicationGuildCommandResult>;
delete(args?: RestArgumentsNoBody): Promise<never>; delete(args?: RestArgumentsNoBody): Promise<undefined>;
permissions: { permissions: {
get(args?: RestArgumentsNoBody): Promise<RESTGetAPIGuildApplicationCommandsPermissionsResult>; get(args?: RestArgumentsNoBody): Promise<RESTGetAPIGuildApplicationCommandsPermissionsResult>;
put( put(
@ -92,7 +92,7 @@ export interface ApplicationRoutes {
patch( patch(
args: RestArguments<RESTPatchAPIApplicationCommandJSONBody>, args: RestArguments<RESTPatchAPIApplicationCommandJSONBody>,
): Promise<RESTPatchAPIApplicationCommandResult>; ): Promise<RESTPatchAPIApplicationCommandResult>;
delete(args?: RestArgumentsNoBody): Promise<never>; delete(args?: RestArgumentsNoBody): Promise<undefined>;
}; };
}; };
'role-connections': { 'role-connections': {
@ -122,10 +122,10 @@ export interface ApplicationRoutes {
( (
id: string, id: string,
): { ): {
get(args?: RestArgumentsNoBody<never>): Promise<RESTGetAPIEntitlementResult>; get(args?: RestArgumentsNoBody): Promise<RESTGetAPIEntitlementResult>;
delete(args?: RestArgumentsNoBody): Promise<never>; delete(args?: RestArgumentsNoBody): Promise<undefined>;
consume: { consume: {
post(args?: RestArgumentsNoBody): Promise<never>; post(args?: RestArgumentsNoBody): Promise<undefined>;
}; };
}; };
}; };

View File

@ -190,7 +190,7 @@ export interface ChannelRoutes {
post(args: RestArguments<RESTPostAPIChannelWebhookJSONBody>): Promise<RESTPostAPIChannelWebhookResult>; post(args: RestArguments<RESTPostAPIChannelWebhookJSONBody>): Promise<RESTPostAPIChannelWebhookResult>;
}; };
'voice-status': { 'voice-status': {
put(args: RestArguments<{ status: string | null }>): Promise<never>; put(args: RestArguments<{ status: string | null }>): Promise<undefined>;
}; };
polls(messageId: string): { polls(messageId: string): {
answers(id: ValidAnswerId): { answers(id: ValidAnswerId): {
@ -201,7 +201,7 @@ export interface ChannelRoutes {
}; };
}; };
'send-soundboard-sound': { 'send-soundboard-sound': {
post(args: RestArguments<RESTPostAPISendSoundboardSound>): Promise<never>; post(args: RestArguments<RESTPostAPISendSoundboardSound>): Promise<undefined>;
}; };
}; };
} }

View File

@ -39,10 +39,10 @@ import type {
RESTGetAPIGuildRolesResult, RESTGetAPIGuildRolesResult,
RESTGetAPIGuildScheduledEventQuery, RESTGetAPIGuildScheduledEventQuery,
RESTGetAPIGuildScheduledEventResult, RESTGetAPIGuildScheduledEventResult,
RESTGetAPIGuildScheduledEventUsersQuery,
RESTGetAPIGuildScheduledEventUsersResult,
RESTGetAPIGuildScheduledEventsQuery, RESTGetAPIGuildScheduledEventsQuery,
RESTGetAPIGuildScheduledEventsResult, RESTGetAPIGuildScheduledEventsResult,
RESTGetAPIGuildScheduledEventUsersQuery,
RESTGetAPIGuildScheduledEventUsersResult,
RESTGetAPIGuildSoundboardSoundsResult, RESTGetAPIGuildSoundboardSoundsResult,
RESTGetAPIGuildStickerResult, RESTGetAPIGuildStickerResult,
RESTGetAPIGuildStickersResult, RESTGetAPIGuildStickersResult,
@ -107,12 +107,12 @@ import type {
RESTPostAPIGuildSoundboardSoundResult, RESTPostAPIGuildSoundboardSoundResult,
RESTPostAPIGuildStickerFormDataBody, RESTPostAPIGuildStickerFormDataBody,
RESTPostAPIGuildStickerResult, RESTPostAPIGuildStickerResult,
RESTPostAPIGuildTemplatesJSONBody,
RESTPostAPIGuildTemplatesResult,
RESTPostAPIGuildsJSONBody, RESTPostAPIGuildsJSONBody,
RESTPostAPIGuildsMFAJSONBody, RESTPostAPIGuildsMFAJSONBody,
RESTPostAPIGuildsMFAResult, RESTPostAPIGuildsMFAResult,
RESTPostAPIGuildsResult, RESTPostAPIGuildsResult,
RESTPostAPIGuildTemplatesJSONBody,
RESTPostAPIGuildTemplatesResult,
RESTPostAPITemplateCreateGuildJSONBody, RESTPostAPITemplateCreateGuildJSONBody,
RESTPostAPITemplateCreateGuildResult, RESTPostAPITemplateCreateGuildResult,
RESTPutAPIGuildBanJSONBody, RESTPutAPIGuildBanJSONBody,
@ -361,7 +361,7 @@ export interface GuildRoutes {
patch( patch(
args?: RestArguments<RESTPatchAPIGuildSoundboardSound>, args?: RestArguments<RESTPatchAPIGuildSoundboardSound>,
): Promise<RESTPatchAPIGuildSoundboardSoundResult>; ): Promise<RESTPatchAPIGuildSoundboardSoundResult>;
delete(args?: RestArgumentsNoBody): Promise<never>; delete(args?: RestArgumentsNoBody): Promise<undefined>;
}; };
}; };
}; };

View File

@ -1,11 +1,11 @@
import { type UUID, randomUUID } from 'node:crypto'; import { randomUUID, type UUID } from 'node:crypto';
import { type Awaitable, BASE_HOST, Logger, delay, lazyLoadPackage, snowflakeToTimestamp } from '../common'; import { type Awaitable, BASE_HOST, delay, Logger, lazyLoadPackage, snowflakeToTimestamp } from '../common';
import { toArrayBuffer, toBuffer } from '../common/it/utils'; import { toArrayBuffer, toBuffer } from '../common/it/utils';
import type { WorkerData } from '../websocket'; import type { WorkerData } from '../websocket';
import type { WorkerSendApiRequest } from '../websocket/discord/worker'; import type { WorkerSendApiRequest } from '../websocket/discord/worker';
import { Bucket } from './bucket';
import { CDNRouter, Router } from './Router'; import { CDNRouter, Router } from './Router';
import type { APIRoutes } from './Routes'; import type { APIRoutes } from './Routes';
import { Bucket } from './bucket';
import { import {
type ApiHandlerInternalOptions, type ApiHandlerInternalOptions,
type ApiHandlerOptions, type ApiHandlerOptions,
@ -291,17 +291,19 @@ export class ApiHandler {
await this.onRatelimit?.(response, request); await this.onRatelimit?.(response, request);
const content = `${JSON.stringify(request)} `; const content = `${JSON.stringify(request)} `;
let retryAfter = let retryAfter: number | undefined;
const data = JSON.parse(result);
if (data.retry_after) retryAfter = Math.ceil(data.retry_after * 1000);
retryAfter ??=
Number(response.headers.get('x-ratelimit-reset-after') || response.headers.get('retry-after')) * 1000; Number(response.headers.get('x-ratelimit-reset-after') || response.headers.get('retry-after')) * 1000;
if (Number.isNaN(retryAfter)) { if (Number.isNaN(retryAfter)) {
try { this.debugger?.warn(`${route} Could not extract retry_after from 429 response. ${result}`);
retryAfter = JSON.parse(result).retry_after * 1000; next();
} catch (err) { reject(new Error('Could not extract retry_after from 429 response.'));
this.debugger?.warn(`Unexpected error: ${err}`); return false;
reject(err);
return false;
}
} }
this.debugger?.info( this.debugger?.info(
@ -384,11 +386,7 @@ export class ApiHandler {
} }
} }
parseRequest(options: { parseRequest(options: { url: string; headers: RequestHeaders; request: ApiRequestOptions }) {
url: string;
headers: RequestHeaders;
request: ApiRequestOptions;
}) {
let finalUrl = options.url; let finalUrl = options.url;
let data: string | FormData | undefined; let data: string | FormData | undefined;
if (options.request.auth) { if (options.request.auth) {

View File

@ -1,4 +1,4 @@
export * from './api';
export * from './Router'; export * from './Router';
export * from './Routes'; export * from './Routes';
export * from './api';
export * from './shared'; export * from './shared';

View File

@ -1,6 +1,6 @@
import { type ActionRow, fromComponent } from '.';
import { type ColorResolvable, type RestOrArray, resolveColor } from '../common'; import { type ColorResolvable, type RestOrArray, resolveColor } from '../common';
import { type APIContainerComponent, ComponentType } from '../types'; import { type APIContainerComponent, ComponentType } from '../types';
import { type ActionRow, fromComponent } from '.';
import { BaseComponentBuilder } from './Base'; import { BaseComponentBuilder } from './Base';
import type { File } from './File'; import type { File } from './File';
import type { MediaGallery } from './MediaGallery'; import type { MediaGallery } from './MediaGallery';

View File

@ -1,6 +1,6 @@
import { type Button, fromComponent } from '.';
import type { RestOrArray } from '../common'; import type { RestOrArray } from '../common';
import { type APISectionComponent, ComponentType } from '../types'; import { type APISectionComponent, ComponentType } from '../types';
import { type Button, fromComponent } from '.';
import { BaseComponentBuilder } from './Base'; import { BaseComponentBuilder } from './Base';
import type { TextDisplay } from './TextDisplay'; import type { TextDisplay } from './TextDisplay';
import type { Thumbnail } from './Thumbnail'; import type { Thumbnail } from './Thumbnail';
@ -9,7 +9,7 @@ export class Section<
Ac extends Button | Thumbnail = Button | Thumbnail, Ac extends Button | Thumbnail = Button | Thumbnail,
> extends BaseComponentBuilder<APISectionComponent> { > extends BaseComponentBuilder<APISectionComponent> {
components: TextDisplay[]; components: TextDisplay[];
accessory!: Ac; accessory?: Ac;
constructor({ components, accessory, ...data }: Partial<APISectionComponent> = {}) { constructor({ components, accessory, ...data }: Partial<APISectionComponent> = {}) {
super({ type: ComponentType.Section, ...data }); super({ type: ComponentType.Section, ...data });
this.components = (components?.map(component => fromComponent(component)) ?? []) as TextDisplay[]; this.components = (components?.map(component => fromComponent(component)) ?? []) as TextDisplay[];
@ -46,6 +46,7 @@ export class Section<
* @returns The JSON representation of this section * @returns The JSON representation of this section
*/ */
toJSON() { toJSON() {
if (!this.accessory) throw new Error('Cannot convert to JSON without an accessory.');
return { return {
...this.data, ...this.data,
components: this.components.map(component => component.toJSON()), components: this.components.map(component => component.toJSON()),

View File

@ -161,7 +161,7 @@ export class RoleSelectMenu extends SelectMenu<APIRoleSelectComponent> {
} }
} }
export type MentionableDefaultElement = { id: string; type: keyof Omit<typeof SelectMenuDefaultValueType, 'Channel'> }; export type MentionableDefaultElement = { id: string; type: keyof typeof SelectMenuDefaultValueType };
/** /**
* Represents a Select Menu for selecting mentionable entities. * Represents a Select Menu for selecting mentionable entities.

View File

@ -94,7 +94,11 @@ export class LimitedMemoryAdapter<T> implements Adapter {
} }
private __set(key: string, data: any) { private __set(key: string, data: any) {
const __guildId = Array.isArray(data) ? data[0].guild_id : data.guild_id; const isArray = Array.isArray(data);
if (isArray && data.length === 0) {
return;
}
const __guildId = isArray ? data[0].guild_id : data.guild_id;
const namespace = `${key.split('.')[0]}${__guildId ? `.${__guildId}` : ''}`; const namespace = `${key.split('.')[0]}${__guildId ? `.${__guildId}` : ''}`;
const self = this; const self = this;
if (!this.storage.has(namespace)) { if (!this.storage.has(namespace)) {

12
src/cache/index.ts vendored
View File

@ -1,11 +1,5 @@
import { type If, Logger } from '../common';
import type { Adapter } from './adapters';
import { Guilds } from './resources/guilds';
import { Users } from './resources/users';
import type { InternalOptions, UsingClient } from '../commands'; import type { InternalOptions, UsingClient } from '../commands';
import { type If, Logger } from '../common';
import { import {
type APIChannel, type APIChannel,
type APIEmoji, type APIEmoji,
@ -20,9 +14,11 @@ import {
GuildMemberFlags, GuildMemberFlags,
OverwriteType, OverwriteType,
} from '../types'; } from '../types';
import type { Adapter } from './adapters';
import { Bans } from './resources/bans'; import { Bans } from './resources/bans';
import { Channels } from './resources/channels'; import { Channels } from './resources/channels';
import { Emojis } from './resources/emojis'; import { Emojis } from './resources/emojis';
import { Guilds } from './resources/guilds';
import { Members } from './resources/members'; import { Members } from './resources/members';
import { Messages } from './resources/messages'; import { Messages } from './resources/messages';
import { Overwrites } from './resources/overwrites'; import { Overwrites } from './resources/overwrites';
@ -30,7 +26,9 @@ import { Presences } from './resources/presence';
import { Roles } from './resources/roles'; import { Roles } from './resources/roles';
import { StageInstances } from './resources/stage-instances'; import { StageInstances } from './resources/stage-instances';
import { Stickers } from './resources/stickers'; import { Stickers } from './resources/stickers';
import { Users } from './resources/users';
import { VoiceStates } from './resources/voice-states'; import { VoiceStates } from './resources/voice-states';
export { BaseResource } from './resources/default/base'; export { BaseResource } from './resources/default/base';
export { GuildBasedResource } from './resources/default/guild-based'; export { GuildBasedResource } from './resources/default/guild-based';
export { GuildRelatedResource } from './resources/default/guild-related'; export { GuildRelatedResource } from './resources/default/guild-related';

View File

@ -1,7 +1,7 @@
import type { Cache, CacheFrom, ReturnCache } from '..';
import { type GuildStructure, Transformers } from '../../client/transformers'; import { type GuildStructure, Transformers } from '../../client/transformers';
import { fakePromise } from '../../common'; import { fakePromise } from '../../common';
import type { APIGuild, GatewayGuildCreateDispatchData } from '../../types'; import type { APIGuild, GatewayGuildCreateDispatchData } from '../../types';
import type { Cache, CacheFrom, ReturnCache } from '..';
import { BaseResource } from './default/base'; import { BaseResource } from './default/base';
export class Guilds extends BaseResource<any, APIGuild | GatewayGuildCreateDispatchData> { export class Guilds extends BaseResource<any, APIGuild | GatewayGuildCreateDispatchData> {

View File

@ -1,5 +1,7 @@
import { promises } from 'node:fs';
import { join } from 'node:path'; import { join } from 'node:path';
import { ApiHandler } from '../api'; import { ApiHandler } from '../api';
import { isBufferLike } from '../api/utils/utils';
import type { Adapter, DisabledCache } from '../cache'; import type { Adapter, DisabledCache } from '../cache';
import { Cache, MemoryAdapter } from '../cache'; import { Cache, MemoryAdapter } from '../cache';
import type { import type {
@ -16,34 +18,31 @@ import type {
UsingClient, UsingClient,
} from '../commands'; } from '../commands';
import { IgnoreCommand, type InferWithPrefix, type MiddlewareContext } from '../commands/applications/shared'; import { IgnoreCommand, type InferWithPrefix, type MiddlewareContext } from '../commands/applications/shared';
import { HandleCommand } from '../commands/handle';
import { CommandHandler } from '../commands/handler'; import { CommandHandler } from '../commands/handler';
import { import {
ApplicationShorter, ApplicationShorter,
assertString,
ChannelShorter, ChannelShorter,
EmojiShorter, EmojiShorter,
filterSplit,
GuildShorter, GuildShorter,
InteractionShorter, InteractionShorter,
InvitesShorter, InvitesShorter,
LogLevels,
Logger, Logger,
LogLevels,
type MakeRequired, type MakeRequired,
MemberShorter, MemberShorter,
MergeOptions, MergeOptions,
MessageShorter, MessageShorter,
magicImport,
ReactionShorter, ReactionShorter,
RoleShorter, RoleShorter,
TemplateShorter, TemplateShorter,
ThreadShorter, ThreadShorter,
UsersShorter, UsersShorter,
WebhookShorter, WebhookShorter,
assertString,
filterSplit,
magicImport,
} from '../common'; } from '../common';
import { promises } from 'node:fs';
import { isBufferLike } from '../api/utils/utils';
import { HandleCommand } from '../commands/handle';
import { BanShorter } from '../common/shorters/bans'; import { BanShorter } from '../common/shorters/bans';
import { SoundboardShorter } from '../common/shorters/soundboard'; import { SoundboardShorter } from '../common/shorters/soundboard';
import { VoiceStateShorter } from '../common/shorters/voiceStates'; import { VoiceStateShorter } from '../common/shorters/voiceStates';

View File

@ -1,17 +1,17 @@
import type { CommandContext, Message } from '..'; import type { CommandContext, Message } from '..';
import { import {
type Awaitable, type Awaitable,
assertString,
type DeepPartial, type DeepPartial,
type If, type If,
lazyLoadPackage,
type PickPartial, type PickPartial,
type WatcherPayload, type WatcherPayload,
type WatcherSendToShard, type WatcherSendToShard,
assertString,
lazyLoadPackage,
} from '../common'; } from '../common';
import { EventHandler } from '../events'; import { EventHandler } from '../events';
import type { GatewayDispatchPayload, GatewayPresenceUpdateData } from '../types'; import type { GatewayDispatchPayload, GatewayPresenceUpdateData } from '../types';
import { ShardManager, type ShardManagerOptions, properties } from '../websocket'; import { properties, ShardManager, type ShardManagerOptions } from '../websocket';
import { MemberUpdateHandler } from '../websocket/discord/events/memberUpdate'; import { MemberUpdateHandler } from '../websocket/discord/events/memberUpdate';
import { PresenceUpdateHandler } from '../websocket/discord/events/presenceUpdate'; import { PresenceUpdateHandler } from '../websocket/discord/events/presenceUpdate';
import type { BaseClientOptions, InternalRuntimeConfig, ServicesOptions, StartOptions } from './base'; import type { BaseClientOptions, InternalRuntimeConfig, ServicesOptions, StartOptions } from './base';
@ -219,8 +219,8 @@ export interface ClientOptions extends BaseClientOptions {
}; };
commands?: BaseClientOptions['commands'] & { commands?: BaseClientOptions['commands'] & {
prefix?: (message: MessageStructure) => Awaitable<string[]>; prefix?: (message: MessageStructure) => Awaitable<string[]>;
deferReplyResponse?: (ctx: CommandContext) => Parameters<Message['write']>[0]; deferReplyResponse?: (ctx: CommandContext) => Awaitable<Parameters<Message['write']>[0]>;
reply?: (ctx: CommandContext) => boolean; reply?: (ctx: CommandContext) => Awaitable<boolean>;
}; };
handlePayload?: ShardManagerOptions['handlePayload']; handlePayload?: ShardManagerOptions['handlePayload'];
resharding?: PickPartial<NonNullable<ShardManagerOptions['resharding']>, 'getInfo'>; resharding?: PickPartial<NonNullable<ShardManagerOptions['resharding']>, 'getInfo'>;

View File

@ -1,4 +1,4 @@
import { type UUID, randomUUID } from 'node:crypto'; import { randomUUID, type UUID } from 'node:crypto';
import type { UsingClient } from '../commands'; import type { UsingClient } from '../commands';
import type { Awaitable, CamelCase } from '../common'; import type { Awaitable, CamelCase } from '../common';
import type { CallbackEventHandler, CustomEventsKeys, GatewayEvents } from '../events'; import type { CallbackEventHandler, CustomEventsKeys, GatewayEvents } from '../events';

View File

@ -1,5 +1,5 @@
export type { RuntimeConfig, RuntimeConfigHTTP } from './base'; export type { RuntimeConfig, RuntimeConfigHTTP } from './base';
export * from './client'; export * from './client';
export * from './httpclient'; export * from './httpclient';
export * from './workerclient';
export * from './transformers'; export * from './transformers';
export * from './workerclient';

View File

@ -8,8 +8,8 @@ import {
BaseGuildChannel, BaseGuildChannel,
CategoryChannel, CategoryChannel,
ClientUser, ClientUser,
DMChannel,
DirectoryChannel, DirectoryChannel,
DMChannel,
Emoji, Emoji,
Entitlement, Entitlement,
ForumChannel, ForumChannel,

View File

@ -1,11 +1,24 @@
import { type UUID, randomUUID } from 'node:crypto'; import { randomUUID, type UUID } from 'node:crypto';
import { ApiHandler, Logger } from '..'; import { ApiHandler, Logger } from '..';
import { WorkerAdapter } from '../cache'; import { WorkerAdapter } from '../cache';
import { type DeepPartial, LogLevels, type MakeRequired, type When, lazyLoadPackage } from '../common'; import {
type Awaitable,
calculateShardId,
type DeepPartial,
LogLevels,
lazyLoadPackage,
type MakeRequired,
type When,
} from '../common';
import { EventHandler } from '../events'; import { EventHandler } from '../events';
import type { GatewayDispatchPayload, GatewaySendPayload } from '../types'; import type { GatewayDispatchPayload, GatewaySendPayload } from '../types';
import { Shard, type ShardManagerOptions, ShardSocketCloseCodes, type WorkerData, properties } from '../websocket'; import { properties, Shard, type ShardManagerOptions, ShardSocketCloseCodes, type WorkerData } from '../websocket';
import { MemberUpdateHandler } from '../websocket/discord/events/memberUpdate';
import { PresenceUpdateHandler } from '../websocket/discord/events/presenceUpdate';
import type { WorkerHeartbeaterMessages } from '../websocket/discord/heartbeater';
import type { ShardData } from '../websocket/discord/shared';
import type { import type {
ClientHeartbeaterMessages,
WorkerDisconnectedAllShardsResharding, WorkerDisconnectedAllShardsResharding,
WorkerMessages, WorkerMessages,
WorkerReady, WorkerReady,
@ -27,10 +40,6 @@ import type { ManagerMessages, ManagerSpawnShards } from '../websocket/discord/w
import type { BaseClientOptions, ServicesOptions, StartOptions } from './base'; import type { BaseClientOptions, ServicesOptions, StartOptions } from './base';
import { BaseClient } from './base'; import { BaseClient } from './base';
import type { Client, ClientOptions } from './client'; import type { Client, ClientOptions } from './client';
import { MemberUpdateHandler } from '../websocket/discord/events/memberUpdate';
import { PresenceUpdateHandler } from '../websocket/discord/events/presenceUpdate';
import type { ShardData } from '../websocket/discord/shared';
import { Collectors } from './collectors'; import { Collectors } from './collectors';
import { type ClientUserStructure, Transformers } from './transformers'; import { type ClientUserStructure, Transformers } from './transformers';
@ -166,13 +175,19 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
} }
} }
postMessage(body: WorkerMessages): unknown { postMessage(body: WorkerMessages | ClientHeartbeaterMessages): unknown {
if (manager) return manager.postMessage(body); if (manager) return manager.postMessage(body);
return process.send!(body); return process.send!(body);
} }
async handleManagerMessages(data: ManagerMessages) { async handleManagerMessages(data: ManagerMessages | WorkerHeartbeaterMessages) {
switch (data.type) { switch (data.type) {
case 'HEARTBEAT':
this.postMessage({
type: 'ACK_HEARTBEAT',
workerId: workerData.workerId,
});
break;
case 'CACHE_RESULT': case 'CACHE_RESULT':
if (this.cache.adapter instanceof WorkerAdapter && this.cache.adapter.promises.has(data.nonce)) { if (this.cache.adapter instanceof WorkerAdapter && this.cache.adapter.promises.has(data.nonce)) {
const cacheData = this.cache.adapter.promises.get(data.nonce)!; const cacheData = this.cache.adapter.promises.get(data.nonce)!;
@ -377,12 +392,18 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
return this.onPacket(packet, shardId); return this.onPacket(packet, shardId);
}; };
} }
workerData.totalShards = data.totalShards;
workerData.shards = [...this.shards.keys()];
this.resharding.clear(); this.resharding.clear();
} }
break; break;
} }
} }
calculateShardId(guildId: string) {
return calculateShardId(guildId, this.workerData.totalShards);
}
private generateNonce(): UUID { private generateNonce(): UUID {
const uuid = randomUUID(); const uuid = randomUUID();
if (this.promises.has(uuid)) return this.generateNonce(); if (this.promises.has(uuid)) return this.generateNonce();
@ -420,7 +441,7 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
return Promise.all(promises); return Promise.all(promises);
} }
createShard(id: number, data: Pick<ManagerSpawnShards, 'info' | 'compress'>) { createShard(id: number, data: Pick<ManagerSpawnShards, 'info' | 'compress' | 'properties'>) {
const onPacket = this.onPacket.bind(this); const onPacket = this.onPacket.bind(this);
const handlePayload = this.options?.handlePayload?.bind(this); const handlePayload = this.options?.handlePayload?.bind(this);
const self = this; const self = this;
@ -432,6 +453,7 @@ export class WorkerClient<Ready extends boolean = boolean> extends BaseClient {
debugger: this.debugger, debugger: this.debugger,
properties: { properties: {
...properties, ...properties,
...data.properties,
...this.options.gateway?.properties, ...this.options.gateway?.properties,
}, },
async handlePayload(shardId, payload) { async handlePayload(shardId, payload) {
@ -553,12 +575,12 @@ export function generateShardInfo(shard: Shard): WorkerShardInfo {
}; };
} }
interface WorkerClientOptions extends BaseClientOptions { export interface WorkerClientOptions extends BaseClientOptions {
commands?: NonNullable<Client['options']>['commands']; commands?: NonNullable<Client['options']>['commands'];
handlePayload?: ShardManagerOptions['handlePayload']; handlePayload?: ShardManagerOptions['handlePayload'];
gateway?: ClientOptions['gateway']; gateway?: ClientOptions['gateway'];
postMessage?: (body: unknown) => unknown; postMessage?: (body: unknown) => Awaitable<unknown>;
/** can have perfomance issues in big bots if the client sends every event, specially in startup (false by default) */ /** can have perfomance issues in big bots if the client sends every event, specially in startup (false by default) */
sendPayloadToParent?: boolean; sendPayloadToParent?: boolean;
handleManagerMessages?(message: ManagerMessages): any; handleManagerMessages?(message: ManagerMessages | WorkerHeartbeaterMessages): Awaitable<unknown>;
} }

View File

@ -79,7 +79,7 @@ export class CommandContext<
if (this.interaction) return this.interaction.write(body, withResponse); if (this.interaction) return this.interaction.write(body, withResponse);
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 && (await options?.reply?.(this)) ? 'reply' : 'write'
](body)) as never; ](body)) as never;
} }
@ -97,8 +97,8 @@ export class CommandContext<
return this.interaction.deferReply(ephemeral ? MessageFlags.Ephemeral : undefined, withResponse); return this.interaction.deferReply(ephemeral ? MessageFlags.Ephemeral : undefined, withResponse);
this.__deferred = true; this.__deferred = true;
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)[options?.reply?.(this) ? 'reply' : 'write']( return (this.messageResponse = await (this.message! as Message)[(await options?.reply?.(this)) ? 'reply' : 'write'](
options?.deferReplyResponse?.(this) ?? { content: 'Thinking...' }, (await options?.deferReplyResponse?.(this)) ?? { content: 'Thinking...' },
)) as never; )) as never;
} }

View File

@ -1,4 +1,4 @@
import { type PermissionStrings, magicImport } from '../../common'; import { magicImport, type PermissionStrings } from '../../common';
import { import {
ApplicationCommandType, ApplicationCommandType,
type ApplicationIntegrationType, type ApplicationIntegrationType,

View File

@ -12,10 +12,11 @@ import type {
MakeRequired, MakeRequired,
MessageWebhookCreateBodyRequest, MessageWebhookCreateBodyRequest,
ModalCreateBodyRequest, ModalCreateBodyRequest,
ModalCreateOptions,
UnionToTuple, UnionToTuple,
When, When,
} from '../../common'; } from '../../common';
import type { AllChannels, EntryPointInteraction } from '../../structures'; import type { AllChannels, EntryPointInteraction, ModalSubmitInteraction } from '../../structures';
import { MessageFlags, type RESTGetAPIGuildQuery } from '../../types'; import { MessageFlags, type RESTGetAPIGuildQuery } from '../../types';
import { BaseContext } from '../basecontext'; import { BaseContext } from '../basecontext';
import type { RegisteredMiddlewares } from '../decorators'; import type { RegisteredMiddlewares } from '../decorators';
@ -52,8 +53,11 @@ export class EntryPointContext<M extends keyof RegisteredMiddlewares = never> ex
return this.interaction.write<WR>(body, withResponse); return this.interaction.write<WR>(body, withResponse);
} }
modal(body: ModalCreateBodyRequest) { modal(body: ModalCreateBodyRequest, options?: undefined): Promise<undefined>;
return this.interaction.modal(body); modal(body: ModalCreateBodyRequest, options: ModalCreateOptions): Promise<ModalSubmitInteraction | null>;
modal(body: ModalCreateBodyRequest, options?: ModalCreateOptions | undefined) {
if (options === undefined) return this.interaction.modal(body);
return this.interaction.modal(body, options);
} }
deferReply<WR extends boolean = false>( deferReply<WR extends boolean = false>(

View File

@ -1,4 +1,4 @@
import { type PermissionStrings, magicImport } from '../../common'; import { magicImport, type PermissionStrings } from '../../common';
import type { import type {
ApplicationCommandType, ApplicationCommandType,
ApplicationIntegrationType, ApplicationIntegrationType,

View File

@ -8,16 +8,22 @@ import {
type WebhookMessageStructure, type WebhookMessageStructure,
} from '../../client/transformers'; } from '../../client/transformers';
import { import {
type InteractionCreateBodyRequest, InteractionCreateBodyRequest,
type InteractionMessageUpdateBodyRequest, InteractionMessageUpdateBodyRequest,
type MakeRequired, MakeRequired,
type MessageWebhookCreateBodyRequest, MessageWebhookCreateBodyRequest,
type ModalCreateBodyRequest, ModalCreateBodyRequest,
type UnionToTuple, ModalCreateOptions,
type When,
toSnakeCase, toSnakeCase,
UnionToTuple,
When,
} from '../../common'; } from '../../common';
import type { AllChannels, MessageCommandInteraction, UserCommandInteraction } from '../../structures'; import {
AllChannels,
MessageCommandInteraction,
ModalSubmitInteraction,
UserCommandInteraction,
} from '../../structures';
import { type APIMessage, ApplicationCommandType, MessageFlags, type RESTGetAPIGuildQuery } from '../../types'; import { type APIMessage, ApplicationCommandType, MessageFlags, type RESTGetAPIGuildQuery } from '../../types';
import { BaseContext } from '../basecontext'; import { BaseContext } from '../basecontext';
import type { RegisteredMiddlewares } from '../decorators'; import type { RegisteredMiddlewares } from '../decorators';
@ -47,7 +53,6 @@ export class MenuCommandContext<
metadata: CommandMetadata<UnionToTuple<M>> = {} as never; metadata: CommandMetadata<UnionToTuple<M>> = {} as never;
globalMetadata: GlobalMetadata = {}; globalMetadata: GlobalMetadata = {};
// biome-ignore lint/suspicious/useGetterReturn: default don't exist.
get target(): InteractionTarget<T> { get target(): InteractionTarget<T> {
switch (this.interaction.data.type) { switch (this.interaction.data.type) {
case ApplicationCommandType.Message: { case ApplicationCommandType.Message: {
@ -76,8 +81,11 @@ export class MenuCommandContext<
return this.interaction.write<WR>(body, withResponse); return this.interaction.write<WR>(body, withResponse);
} }
modal(body: ModalCreateBodyRequest) { modal(body: ModalCreateBodyRequest, options?: undefined): Promise<undefined>;
return this.interaction.modal(body); modal(body: ModalCreateBodyRequest, options: ModalCreateOptions): Promise<ModalSubmitInteraction | null>;
modal(body: ModalCreateBodyRequest, options?: ModalCreateOptions | undefined) {
if (options === undefined) return this.interaction.modal(body);
return this.interaction.modal(body, options);
} }
deferReply<WR extends boolean = false>( deferReply<WR extends boolean = false>(

View File

@ -1,10 +1,3 @@
import type {
AutocompleteCallback,
EntryPointContext,
MenuCommandContext,
OnAutocompleteErrorCallback,
ReturnOptionsTypes,
} from '..';
import type { Awaitable, FlatObjectKeys } from '../../common'; import type { Awaitable, FlatObjectKeys } from '../../common';
import type { ModalContext } from '../../components'; import type { ModalContext } from '../../components';
import type { ComponentContext } from '../../components/componentcontext'; import type { ComponentContext } from '../../components/componentcontext';
@ -15,6 +8,13 @@ import {
ApplicationCommandOptionType, ApplicationCommandOptionType,
} from '../../types'; } from '../../types';
import type { LocalizationMap } from '../../types/payloads'; import type { LocalizationMap } from '../../types/payloads';
import type {
AutocompleteCallback,
EntryPointContext,
MenuCommandContext,
OnAutocompleteErrorCallback,
ReturnOptionsTypes,
} from '..';
import type { CommandContext } from './chatcontext'; import type { CommandContext } from './chatcontext';
import type { DefaultLocale, MiddlewareContext, OKFunction, SeyfertChannelMap, StopFunction } from './shared'; import type { DefaultLocale, MiddlewareContext, OKFunction, SeyfertChannelMap, StopFunction } from './shared';

View File

@ -1,7 +1,7 @@
import type { import type {
CategoryChannelStructure, CategoryChannelStructure,
DMChannelStructure,
DirectoryChannelStructure, DirectoryChannelStructure,
DMChannelStructure,
ForumChannelStructure, ForumChannelStructure,
MediaChannelStructure, MediaChannelStructure,
NewsChannelStructure, NewsChannelStructure,

View File

@ -1,3 +1,32 @@
import type { Client, WorkerClient } from '../client';
import { type MessageStructure, type OptionResolverStructure, Transformers } from '../client/transformers';
import type { MakeRequired } from '../common';
import { INTEGER_OPTION_VALUE_LIMIT } from '../common/it/constants';
import { ComponentContext, ModalContext } from '../components';
import {
type __InternalReplyFunction,
AutocompleteInteraction,
BaseInteraction,
type ChatInputCommandInteraction,
type ComponentInteraction,
type EntryPointInteraction,
type MessageCommandInteraction,
type ModalSubmitInteraction,
type UserCommandInteraction,
} from '../structures';
import type { PermissionsBitField } from '../structures/extra/Permissions';
import {
type APIApplicationCommandInteraction,
type APIApplicationCommandInteractionDataOption,
type APIInteraction,
type APIInteractionDataResolvedChannel,
ApplicationCommandOptionType,
ApplicationCommandType,
ChannelType,
type GatewayMessageCreateDispatchData,
InteractionContextType,
InteractionType,
} from '../types';
import { import {
BaseCommand, BaseCommand,
Command, Command,
@ -19,35 +48,6 @@ import {
SubCommand, SubCommand,
type UsingClient, type UsingClient,
} from '.'; } from '.';
import type { Client, WorkerClient } from '../client';
import { type MessageStructure, type OptionResolverStructure, Transformers } from '../client/transformers';
import type { MakeRequired } from '../common';
import { INTEGER_OPTION_VALUE_LIMIT } from '../common/it/constants';
import { ComponentContext, ModalContext } from '../components';
import {
AutocompleteInteraction,
BaseInteraction,
type ChatInputCommandInteraction,
type ComponentInteraction,
type EntryPointInteraction,
type MessageCommandInteraction,
type ModalSubmitInteraction,
type UserCommandInteraction,
type __InternalReplyFunction,
} from '../structures';
import type { PermissionsBitField } from '../structures/extra/Permissions';
import {
type APIApplicationCommandInteraction,
type APIApplicationCommandInteractionDataOption,
type APIInteraction,
type APIInteractionDataResolvedChannel,
ApplicationCommandOptionType,
ApplicationCommandType,
ChannelType,
type GatewayMessageCreateDispatchData,
InteractionContextType,
InteractionType,
} from '../types';
export type CommandOptionWithType = CommandOption & { export type CommandOptionWithType = CommandOption & {
type: ApplicationCommandOptionType; type: ApplicationCommandOptionType;

View File

@ -1,6 +1,5 @@
import { promises } from 'node:fs'; import { promises } from 'node:fs';
import { basename, dirname } from 'node:path'; import { basename, dirname } from 'node:path';
import type { EntryPointCommand } from '.';
import type { Logger, NulleableCoalising, OmitInsert } from '../common'; import type { Logger, NulleableCoalising, OmitInsert } from '../common';
import { BaseHandler, isCloudfareWorker } from '../common'; import { BaseHandler, isCloudfareWorker } from '../common';
import { PermissionsBitField } from '../structures/extra/Permissions'; import { PermissionsBitField } from '../structures/extra/Permissions';
@ -17,6 +16,7 @@ import {
type LocaleString, type LocaleString,
type LocalizationMap, type LocalizationMap,
} from '../types'; } from '../types';
import type { EntryPointCommand } from '.';
import { Command, type CommandOption, SubCommand } from './applications/chat'; import { Command, type CommandOption, SubCommand } from './applications/chat';
import { ContextMenuCommand } from './applications/menu'; import { ContextMenuCommand } from './applications/menu';
import { IgnoreCommand, type UsingClient } from './applications/shared'; import { IgnoreCommand, type UsingClient } from './applications/shared';

View File

@ -1,11 +1,11 @@
export * from './applications/shared';
// //
export * from './applications/chat'; export * from './applications/chat';
export * from './applications/chatcontext'; export * from './applications/chatcontext';
export * from './applications/entrycontext';
export * from './applications/entryPoint';
export * from './applications/menu'; export * from './applications/menu';
export * from './applications/menucontext'; export * from './applications/menucontext';
export * from './applications/options'; export * from './applications/options';
export * from './applications/entryPoint'; export * from './applications/shared';
export * from './applications/entrycontext';
export * from './decorators'; export * from './decorators';
export * from './optionresolver'; export * from './optionresolver';

View File

@ -1,28 +1,28 @@
export * from './it/constants'; //
export * from './it/utils'; export * from './bot/watcher';
export * from './it/colors'; export * from './it/colors';
export { CustomizeLoggerCallback, AssignFilenameCallback, LogLevels, Logger, LoggerOptions } from './it/logger'; export * from './it/constants';
export * from './it/formatter'; export * from './it/formatter';
// circular lol export { AssignFilenameCallback, CustomizeLoggerCallback, Logger, LoggerOptions, LogLevels } from './it/logger';
export * from './shorters/invites'; export * from './it/utils';
export * from './shorters/application';
// //
export * from './shorters/channels'; export * from './shorters/channels';
export * from './shorters/emojis'; export * from './shorters/emojis';
export * from './shorters/guilds'; export * from './shorters/guilds';
export * from './shorters/interaction';
// circular lol
export * from './shorters/invites';
export * from './shorters/members'; export * from './shorters/members';
export * from './shorters/messages'; export * from './shorters/messages';
export * from './shorters/reactions'; export * from './shorters/reactions';
export * from './shorters/roles'; export * from './shorters/roles';
export * from './shorters/templates'; export * from './shorters/templates';
export * from './shorters/users';
export * from './shorters/threads'; export * from './shorters/threads';
export * from './shorters/users';
export * from './shorters/webhook'; export * from './shorters/webhook';
export * from './shorters/interaction';
export * from './shorters/application';
// //
export * from './types/options'; export * from './types/options';
export * from './types/resolvables'; export * from './types/resolvables';
export * from './types/util'; export * from './types/util';
export * from './types/write'; export * from './types/write';
//
export * from './bot/watcher';

View File

@ -1,4 +1,4 @@
import { type WriteStream, createWriteStream, existsSync, mkdirSync, promises } from 'node:fs'; import { createWriteStream, existsSync, mkdirSync, promises, type WriteStream } from 'node:fs';
import { join } from 'node:path'; import { join } from 'node:path';
import { bgBrightWhite, black, bold, brightBlack, cyan, gray, italic, red, stripColor, yellow } from './colors'; import { bgBrightWhite, black, bold, brightBlack, cyan, gray, italic, red, stripColor, yellow } from './colors';
import { MergeOptions } from './utils'; import { MergeOptions } from './utils';

View File

@ -1,5 +1,7 @@
import { promises } from 'node:fs'; import { promises } from 'node:fs';
import { basename, join } from 'node:path'; import { basename, join } from 'node:path';
import type { Cache } from '../../cache';
import { type APIPartialEmoji, FormattingPatterns, GatewayIntentBits } from '../../types';
import { import {
type ColorResolvable, type ColorResolvable,
DiscordEpoch, DiscordEpoch,
@ -10,8 +12,6 @@ import {
type ObjectToSnake, type ObjectToSnake,
type TypeArray, type TypeArray,
} from '..'; } from '..';
import type { Cache } from '../../cache';
import { type APIPartialEmoji, FormattingPatterns, GatewayIntentBits } from '../../types';
/** /**
* Calculates the shard ID for a guild based on its ID. * Calculates the shard ID for a guild based on its ID.
@ -304,8 +304,6 @@ export function lazyLoadPackage<T>(mod: string): T | undefined {
try { try {
return require(mod); return require(mod);
} catch (e) { } catch (e) {
// biome-ignore lint/suspicious/noConsoleLog:
// biome-ignore lint/suspicious/noConsole:
console.log(`Cannot import ${mod}`, e); console.log(`Cannot import ${mod}`, e);
return; return;
} }

View File

@ -1,7 +1,7 @@
import { CacheFrom } from '../../cache'; import { CacheFrom } from '../../cache';
import type { Overwrites } from '../../cache/resources/overwrites'; import type { Overwrites } from '../../cache/resources/overwrites';
import { type MessageStructure, type ThreadChannelStructure, Transformers } from '../../client/transformers'; import { type MessageStructure, type ThreadChannelStructure, Transformers } from '../../client/transformers';
import { type AllChannels, BaseChannel, type GuildMember, type GuildRole, channelFrom } from '../../structures'; import { type AllChannels, BaseChannel, channelFrom, type GuildMember, type GuildRole } from '../../structures';
import { PermissionsBitField } from '../../structures/extra/Permissions'; import { PermissionsBitField } from '../../structures/extra/Permissions';
import type { import type {
APIChannel, APIChannel,

View File

@ -14,8 +14,8 @@ import {
type AllChannels, type AllChannels,
BaseChannel, BaseChannel,
type CreateStickerBodyRequest, type CreateStickerBodyRequest,
type GuildChannelTypes,
channelFrom, channelFrom,
type GuildChannelTypes,
} from '../../structures'; } from '../../structures';
import type { import type {
APIChannel, APIChannel,

View File

@ -1,5 +1,6 @@
import { BaseInteraction, Modal, type ReplyInteractionBody, resolveFiles } from '../..'; import { BaseInteraction, Modal, type ReplyInteractionBody, resolveFiles } from '../..';
import { Transformers, type WebhookMessageStructure } from '../../client/transformers'; import { Transformers, type WebhookMessageStructure } from '../../client/transformers';
import type { RESTPostAPIWebhookWithTokenWaitResult } from '../../types';
import type { InteractionMessageUpdateBodyRequest, MessageWebhookCreateBodyRequest } from '../types/write'; import type { InteractionMessageUpdateBodyRequest, MessageWebhookCreateBodyRequest } from '../types/write';
import { BaseShorter } from './base'; import { BaseShorter } from './base';
@ -69,12 +70,12 @@ export class InteractionShorter extends BaseShorter {
async followup(token: string, { files, ...body }: MessageWebhookCreateBodyRequest): Promise<WebhookMessageStructure> { async followup(token: string, { files, ...body }: MessageWebhookCreateBodyRequest): Promise<WebhookMessageStructure> {
const parsedFiles = files ? await resolveFiles(files) : undefined; const parsedFiles = files ? await resolveFiles(files) : undefined;
const apiMessage = await this.client.proxy const apiMessage = (await this.client.proxy
.webhooks(this.client.applicationId)(token) .webhooks(this.client.applicationId)(token)
.post({ .post({
body: BaseInteraction.transformBody(body, parsedFiles, this.client), body: BaseInteraction.transformBody(body, parsedFiles, this.client),
files: parsedFiles, files: parsedFiles,
}); })) as RESTPostAPIWebhookWithTokenWaitResult;
return Transformers.WebhookMessage(this.client, apiMessage, this.client.applicationId, token); return Transformers.WebhookMessage(this.client, apiMessage, this.client.applicationId, token);
} }
} }

View File

@ -1,4 +1,7 @@
import type { ValidAnswerId } from '../../api/Routes/channels';
import { resolveFiles } from '../../builders'; import { resolveFiles } from '../../builders';
import { CacheFrom } from '../../cache';
import { type MessageStructure, type ThreadChannelStructure, Transformers, type UserStructure } from '../../client';
import { MessagesMethods } from '../../structures'; import { MessagesMethods } from '../../structures';
import type { import type {
RESTGetAPIChannelMessagesQuery, RESTGetAPIChannelMessagesQuery,
@ -6,10 +9,6 @@ import type {
RESTPostAPIChannelMessageJSONBody, RESTPostAPIChannelMessageJSONBody,
RESTPostAPIChannelMessagesThreadsJSONBody, RESTPostAPIChannelMessagesThreadsJSONBody,
} from '../../types'; } from '../../types';
import type { ValidAnswerId } from '../../api/Routes/channels';
import { CacheFrom } from '../../cache';
import { type MessageStructure, type ThreadChannelStructure, Transformers, type UserStructure } from '../../client';
import type { MessageCreateBodyRequest, MessageUpdateBodyRequest } from '../types/write'; import type { MessageCreateBodyRequest, MessageUpdateBodyRequest } from '../types/write';
import { BaseShorter } from './base'; import { BaseShorter } from './base';

View File

@ -92,10 +92,9 @@ export class RoleShorter extends BaseShorter {
* @param reason The reason for deleting the role. * @param reason The reason for deleting the role.
* @returns A Promise that resolves when the role is deleted. * @returns A Promise that resolves when the role is deleted.
*/ */
async delete(guildId: string, roleId: string, reason?: string): Promise<GuildRoleStructure> { async delete(guildId: string, roleId: string, reason?: string) {
const res = await this.client.proxy.guilds(guildId).roles(roleId).delete({ reason }); await this.client.proxy.guilds(guildId).roles(roleId).delete({ reason });
this.client.cache.roles?.removeIfNI('Guilds', roleId, guildId); this.client.cache.roles?.removeIfNI('Guilds', roleId, guildId);
return Transformers.GuildRole(this.client, res, guildId);
} }
/** /**

View File

@ -1,15 +1,16 @@
import { CacheFrom } from '../..'; import { CacheFrom } from '../..';
import type { ThreadChannelStructure } from '../../client/transformers'; import type { ThreadChannelStructure } from '../../client/transformers';
import { channelFrom } from '../../structures'; import { channelFrom } from '../../structures';
import type { import {
APIThreadChannel, type APIThreadChannel,
APIThreadMember, type APIThreadMember,
RESTGetAPIChannelThreadMembersQuery, ChannelType,
RESTGetAPIChannelThreadsArchivedQuery, type RESTGetAPIChannelThreadMembersQuery,
RESTPatchAPIChannelJSONBody, type RESTGetAPIChannelThreadsArchivedQuery,
RESTPostAPIChannelMessagesThreadsJSONBody, type RESTPatchAPIChannelJSONBody,
RESTPostAPIChannelThreadsJSONBody, type RESTPostAPIChannelMessagesThreadsJSONBody,
RESTPostAPIGuildForumThreadsJSONBody, type RESTPostAPIChannelThreadsJSONBody,
type RESTPostAPIGuildForumThreadsJSONBody,
} from '../../types'; } from '../../types';
import type { MakeRequired, When } from '../types/util'; import type { MakeRequired, When } from '../types/util';
import { BaseShorter } from './base'; import { BaseShorter } from './base';
@ -44,27 +45,22 @@ export class ThreadShorter extends BaseShorter {
); );
} }
fromMessage( async fromMessage(
channelId: string, channelId: string,
messageId: string, messageId: string,
options: RESTPostAPIChannelMessagesThreadsJSONBody & { reason?: string }, options: RESTPostAPIChannelMessagesThreadsJSONBody & { reason?: string },
): Promise<ThreadChannelStructure> { ): Promise<ThreadChannelStructure> {
const { reason, ...body } = options; const { reason, ...body } = options;
return this.client.proxy const thread = await this.client.proxy.channels(channelId).messages(messageId).threads.post({ body, reason });
.channels(channelId) await this.client.cache.channels?.setIfNI(
.messages(messageId) CacheFrom.Rest,
.threads.post({ body, reason }) 'Guilds',
.then(async thread => { thread.id,
await this.client.cache.channels?.setIfNI( (thread as APIThreadChannel).guild_id!,
CacheFrom.Rest, thread,
'Guilds', );
thread.id, return await (channelFrom(thread, this.client) as ThreadChannelStructure);
(thread as APIThreadChannel).guild_id!,
thread,
);
return channelFrom(thread, this.client) as ThreadChannelStructure;
});
} }
join(threadId: string) { join(threadId: string) {
@ -75,8 +71,9 @@ export class ThreadShorter extends BaseShorter {
return this.client.proxy.channels(threadId)['thread-members']('@me').delete(); return this.client.proxy.channels(threadId)['thread-members']('@me').delete();
} }
lock(threadId: string, locked = true, reason?: string): Promise<ThreadChannelStructure> { async lock(threadId: string, locked = true, reason?: string): Promise<ThreadChannelStructure> {
return this.edit(threadId, { locked }, reason).then(x => channelFrom(x, this.client) as ThreadChannelStructure); const x = await this.edit(threadId, { locked }, reason);
return channelFrom(x, this.client) as ThreadChannelStructure;
} }
async edit(threadId: string, body: RESTPatchAPIChannelJSONBody, reason?: string): Promise<ThreadChannelStructure> { async edit(threadId: string, body: RESTPatchAPIChannelJSONBody, reason?: string): Promise<ThreadChannelStructure> {
@ -110,7 +107,7 @@ export class ThreadShorter extends BaseShorter {
return this.client.proxy.channels(threadId)['thread-members'].get({ query }) as never; return this.client.proxy.channels(threadId)['thread-members'].get({ query }) as never;
} }
async listArchivedThreads( async listArchived(
channelId: string, channelId: string,
type: 'public' | 'private', type: 'public' | 'private',
query?: RESTGetAPIChannelThreadsArchivedQuery, query?: RESTGetAPIChannelThreadsArchivedQuery,
@ -128,6 +125,25 @@ export class ThreadShorter extends BaseShorter {
}; };
} }
async listGuildActive(guildId: string, force = false): Promise<ThreadChannelStructure[]> {
if (!force) {
const cached = await this.client.cache.channels?.valuesRaw(guildId);
if (cached)
return cached
.filter(x =>
[ChannelType.PublicThread, ChannelType.PrivateThread, ChannelType.AnnouncementThread].includes(x.type),
)
.map(x => channelFrom(x, this.client) as ThreadChannelStructure);
}
const data = await this.client.proxy.guilds(guildId).threads.active.get();
return Promise.all(
data.threads.map(async thread => {
await this.client.cache.channels?.setIfNI(CacheFrom.Rest, 'Guilds', thread.id, guildId, thread);
return channelFrom(thread, this.client) as ThreadChannelStructure;
}),
);
}
async listJoinedArchivedPrivate( async listJoinedArchivedPrivate(
channelId: string, channelId: string,
query?: RESTGetAPIChannelThreadsArchivedQuery, query?: RESTGetAPIChannelThreadsArchivedQuery,

View File

@ -1,9 +1,9 @@
import { resolveFiles } from '../../builders'; import { resolveFiles } from '../../builders';
import { Transformers, type WebhookMessageStructure, type WebhookStructure } from '../../client/transformers'; import { Transformers, type WebhookMessageStructure, type WebhookStructure } from '../../client/transformers';
import { import {
MessagesMethods,
type MessageWebhookMethodEditParams, type MessageWebhookMethodEditParams,
type MessageWebhookMethodWriteParams, type MessageWebhookMethodWriteParams,
MessagesMethods,
} from '../../structures'; } from '../../structures';
import type { import type {
APIWebhook, APIWebhook,

View File

@ -1,6 +1,6 @@
import type { Identify } from '..';
import type { CDNUrlOptions } from '../../api'; import type { CDNUrlOptions } from '../../api';
import type { UsingClient } from '../../commands'; import type { UsingClient } from '../../commands';
import type { Identify } from '..';
export type ImageOptions = CDNUrlOptions; export type ImageOptions = CDNUrlOptions;

View File

@ -1,7 +1,7 @@
import type { EmbedColors, OmitInsert } from '..';
import type { Attachment, AttachmentDataType, AttachmentResolvable } from '../../builders'; import type { Attachment, AttachmentDataType, AttachmentResolvable } from '../../builders';
import type { GuildMember } from '../../structures'; import type { GuildMember } from '../../structures';
import type { APIGuildMember, APIPartialEmoji, RESTPostAPIApplicationEmojiJSONBody } from '../../types'; import type { APIGuildMember, APIPartialEmoji, RESTPostAPIApplicationEmojiJSONBody } from '../../types';
import type { EmbedColors, OmitInsert } from '..';
export type EmojiResolvable = string | Partial<APIPartialEmoji> | `<${string | undefined}:${string}:${string}>`; export type EmojiResolvable = string | Partial<APIPartialEmoji> | `<${string | undefined}:${string}:${string}>`;
export type GuildMemberResolvable = string | Partial<GuildMember> | APIGuildMember; export type GuildMemberResolvable = string | Partial<GuildMember> | APIGuildMember;

View File

@ -107,7 +107,7 @@ export type SnakeCase<S extends string> = S extends `${infer A}${infer Rest}`
export type ObjectToLower<T> = T extends unknown[] export type ObjectToLower<T> = T extends unknown[]
? ObjectToLower<T[0]>[] ? ObjectToLower<T[0]>[]
: Identify<{ : Identify<{
[K in keyof T as CamelCase<Exclude<K, symbol | number>>]: T[K] extends unknown[] [K in keyof T as K extends number ? K : CamelCase<Exclude<K, symbol | number>>]: T[K] extends unknown[]
? Identify<ObjectToLower<T[K][0]>[]> ? Identify<ObjectToLower<T[K][0]>[]>
: T[K] extends object : T[K] extends object
? Identify<ObjectToLower<T[K]>> ? Identify<ObjectToLower<T[K]>>
@ -119,7 +119,7 @@ export type ObjectToLower<T> = T extends unknown[]
export type ObjectToLowerUndefined<T> = T extends unknown[] export type ObjectToLowerUndefined<T> = T extends unknown[]
? ObjectToLower<T[0]>[] ? ObjectToLower<T[0]>[]
: Identify<{ : Identify<{
[K in keyof T as CamelCase<Exclude<K, symbol | number>>]: T[K] extends unknown[] [K in keyof T as K extends number ? K : CamelCase<Exclude<K, symbol | number>>]: T[K] extends unknown[]
? ObjectToLower<T[K][0]>[] ? ObjectToLower<T[K][0]>[]
: T[K] extends object : T[K] extends object
? ObjectToLower<T[K]> ? ObjectToLower<T[K]>
@ -127,7 +127,7 @@ export type ObjectToLowerUndefined<T> = T extends unknown[]
}>; }>;
export type ObjectToSnake<T> = Identify<{ export type ObjectToSnake<T> = Identify<{
[K in keyof T as SnakeCase<Exclude<K, symbol | number>>]: T[K] extends unknown[] [K in keyof T as K extends number ? K : SnakeCase<Exclude<K, symbol | number>>]: T[K] extends unknown[]
? Identify<ObjectToSnake<T[K][0]>[]> ? Identify<ObjectToSnake<T[K][0]>[]>
: T[K] extends object : T[K] extends object
? Identify<ObjectToSnake<T[K]>> ? Identify<ObjectToSnake<T[K]>>
@ -139,7 +139,7 @@ export type ObjectToSnake<T> = Identify<{
export type ObjectToSnakeUndefined<T> = T extends unknown[] export type ObjectToSnakeUndefined<T> = T extends unknown[]
? ObjectToSnake<T[0]>[] ? ObjectToSnake<T[0]>[]
: Identify<{ : Identify<{
[K in keyof T as SnakeCase<Exclude<K, symbol | number>>]: T[K] extends unknown[] [K in keyof T as K extends number ? K : SnakeCase<Exclude<K, symbol | number>>]: T[K] extends unknown[]
? ObjectToSnake<T[K][0]>[] ? ObjectToSnake<T[K][0]>[]
: T[K] extends object : T[K] extends object
? ObjectToSnake<T[K]> ? ObjectToSnake<T[K]>
@ -171,14 +171,10 @@ type OptionalizeAux<T extends object> = Identify<
* it is recursive * it is recursive
*/ */
export type Optionalize<T> = T extends object export type Optionalize<T> = T extends object
? // biome-ignore lint/style/useShorthandArrayType: typescript things ? T extends Array<unknown>
// biome-ignore lint/style/useConsistentArrayType: <explanation>
T extends Array<unknown>
? number extends T['length'] ? number extends T['length']
? T[number] extends object ? T[number] extends object
? // biome-ignore lint/style/useShorthandArrayType: <explanation> ? Array<OptionalizeAux<T[number]>>
// biome-ignore lint/style/useConsistentArrayType: <explanation>
Array<OptionalizeAux<T[number]>>
: T : T
: Partial<T> : Partial<T>
: OptionalizeAux<T> : OptionalizeAux<T>

View File

@ -16,6 +16,7 @@ import type {
import type { OmitInsert } from './util'; import type { OmitInsert } from './util';
export interface ResolverProps { export interface ResolverProps {
content?: string | undefined | null;
embeds?: Embed[] | APIEmbed[] | undefined; embeds?: Embed[] | APIEmbed[] | undefined;
components?: TopLevelBuilders[] | ReturnType<TopLevelBuilders['toJSON']>[]; components?: TopLevelBuilders[] | ReturnType<TopLevelBuilders['toJSON']>[];
files?: AttachmentBuilder[] | Attachment[] | RawFile[] | undefined; files?: AttachmentBuilder[] | Attachment[] | RawFile[] | undefined;
@ -27,31 +28,31 @@ export interface SendResolverProps extends ResolverProps {
export type MessageCreateBodyRequest = OmitInsert< export type MessageCreateBodyRequest = OmitInsert<
RESTPostAPIChannelMessageJSONBody, RESTPostAPIChannelMessageJSONBody,
'components' | 'embeds' | 'poll', 'components' | 'embeds' | 'poll' | 'content',
SendResolverProps SendResolverProps
>; >;
export type MessageUpdateBodyRequest = OmitInsert< export type MessageUpdateBodyRequest = OmitInsert<
RESTPatchAPIChannelMessageJSONBody, RESTPatchAPIChannelMessageJSONBody,
'components' | 'embeds', 'components' | 'embeds' | 'content',
ResolverProps ResolverProps
>; >;
export type MessageWebhookCreateBodyRequest = OmitInsert< export type MessageWebhookCreateBodyRequest = OmitInsert<
RESTPostAPIWebhookWithTokenJSONBody, RESTPostAPIWebhookWithTokenJSONBody,
'components' | 'embeds' | 'poll', 'components' | 'embeds' | 'poll' | 'content',
SendResolverProps SendResolverProps
>; >;
export type MessageWebhookUpdateBodyRequest = OmitInsert< export type MessageWebhookUpdateBodyRequest = OmitInsert<
RESTPatchAPIWebhookWithTokenMessageJSONBody, RESTPatchAPIWebhookWithTokenMessageJSONBody,
'components' | 'embeds' | 'poll', 'components' | 'embeds' | 'poll' | 'content',
ResolverProps ResolverProps
>; >;
export type InteractionMessageUpdateBodyRequest = OmitInsert< export type InteractionMessageUpdateBodyRequest = OmitInsert<
RESTPatchAPIWebhookWithTokenMessageJSONBody, RESTPatchAPIWebhookWithTokenMessageJSONBody,
'components' | 'embeds' | 'poll', 'components' | 'embeds' | 'poll' | 'content',
SendResolverProps SendResolverProps
> & { > & {
flags?: MessageFlags; flags?: MessageFlags;
@ -59,14 +60,18 @@ export type InteractionMessageUpdateBodyRequest = OmitInsert<
export type ComponentInteractionMessageUpdate = OmitInsert< export type ComponentInteractionMessageUpdate = OmitInsert<
APIInteractionResponseCallbackData, APIInteractionResponseCallbackData,
'components' | 'embeds', 'components' | 'embeds' | 'content',
ResolverProps ResolverProps
>; >;
export type InteractionCreateBodyRequest = OmitInsert< export type InteractionCreateBodyRequest = OmitInsert<
APIInteractionResponseChannelMessageWithSource['data'], APIInteractionResponseChannelMessageWithSource['data'],
'components' | 'embeds' | 'poll', 'components' | 'embeds' | 'poll' | 'content',
SendResolverProps SendResolverProps
>; >;
export type ModalCreateBodyRequest = APIModalInteractionResponse['data'] | Modal; export type ModalCreateBodyRequest = APIModalInteractionResponse['data'] | Modal;
export interface ModalCreateOptions {
waitFor?: number;
}

View File

@ -4,6 +4,7 @@ import {
type ChannelSelectMenu, type ChannelSelectMenu,
type Container, type Container,
type File, type File,
fromComponent,
type MediaGallery, type MediaGallery,
type MentionableSelectMenu, type MentionableSelectMenu,
type RoleSelectMenu, type RoleSelectMenu,
@ -14,7 +15,6 @@ import {
type TextInput, type TextInput,
type Thumbnail, type Thumbnail,
type UserSelectMenu, type UserSelectMenu,
fromComponent,
} from '../builders'; } from '../builders';
import { import {
type APIActionRowComponent, type APIActionRowComponent,

View File

@ -1,5 +1,5 @@
import { type ContainerComponents, componentFactory } from '.';
import type { APIContainerComponent, ComponentType } from '../types'; import type { APIContainerComponent, ComponentType } from '../types';
import { type ContainerComponents, componentFactory } from '.';
import { BaseComponent } from './BaseComponent'; import { BaseComponent } from './BaseComponent';
export class ContainerComponent extends BaseComponent<ComponentType.Container> { export class ContainerComponent extends BaseComponent<ComponentType.Container> {

View File

@ -1,5 +1,5 @@
import { componentFactory } from '.';
import type { APISectionComponent, ComponentType } from '../types'; import type { APISectionComponent, ComponentType } from '../types';
import { componentFactory } from '.';
import { BaseComponent } from './BaseComponent'; import { BaseComponent } from './BaseComponent';
import type { ButtonComponent } from './ButtonComponent'; import type { ButtonComponent } from './ButtonComponent';
import type { TextDisplayComponent } from './TextDisplay'; import type { TextDisplayComponent } from './TextDisplay';

View File

@ -14,13 +14,17 @@ export interface ComponentCommand {
export abstract class ComponentCommand { export abstract class ComponentCommand {
type = InteractionCommandType.COMPONENT; type = InteractionCommandType.COMPONENT;
abstract componentType: keyof ContextComponentCommandInteractionMap; abstract componentType: keyof ContextComponentCommandInteractionMap;
customId?: string; customId?: string | RegExp;
filter?(context: ComponentContext<typeof this.componentType>): Promise<boolean> | boolean; 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;
/** @internal */ /** @internal */
_filter(context: ComponentContext) { _filter(context: ComponentContext) {
if (this.customId && this.customId !== context.customId) return false; if (this.customId) {
const matches =
typeof this.customId === 'string' ? this.customId === context.customId : context.customId.match(this.customId);
if (!matches) return false;
}
if (this.filter) return this.filter(context); if (this.filter) return this.filter(context);
return true; return true;
} }

View File

@ -18,16 +18,18 @@ import type {
} from '../client/transformers'; } from '../client/transformers';
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 {
ComponentInteractionMessageUpdate, ComponentInteractionMessageUpdate,
InteractionCreateBodyRequest, InteractionCreateBodyRequest,
InteractionMessageUpdateBodyRequest, InteractionMessageUpdateBodyRequest,
MakeRequired, MakeRequired,
MessageWebhookCreateBodyRequest, MessageWebhookCreateBodyRequest,
ModalCreateBodyRequest, ModalCreateBodyRequest,
ModalCreateOptions,
UnionToTuple, UnionToTuple,
When, When,
} from '../common'; } from '../common';
import { ModalSubmitInteraction } from '../structures';
import { ComponentType, MessageFlags, type RESTGetAPIGuildQuery } from '../types'; import { ComponentType, MessageFlags, type RESTGetAPIGuildQuery } from '../types';
export interface ComponentContext< export interface ComponentContext<
@ -150,8 +152,11 @@ export class ComponentContext<
return this.interaction.deleteResponse(); return this.interaction.deleteResponse();
} }
modal(body: ModalCreateBodyRequest) { modal(body: ModalCreateBodyRequest, options?: undefined): Promise<undefined>;
return this.interaction.modal(body); modal(body: ModalCreateBodyRequest, options: ModalCreateOptions): Promise<ModalSubmitInteraction | null>;
modal(body: ModalCreateBodyRequest, options?: ModalCreateOptions | undefined) {
if (options === undefined) return this.interaction.modal(body);
return this.interaction.modal(body, options);
} }
/** /**

View File

@ -9,7 +9,7 @@ import type {
import { LimitedCollection } from '../collection'; import { LimitedCollection } from '../collection';
import { BaseCommand, type RegisteredMiddlewares, type UsingClient } from '../commands'; import { BaseCommand, type RegisteredMiddlewares, type UsingClient } from '../commands';
import type { FileLoaded } from '../commands/handler'; import type { FileLoaded } from '../commands/handler';
import { BaseHandler, type Logger, type OnFailCallback, isCloudfareWorker, magicImport } from '../common'; import { BaseHandler, isCloudfareWorker, type Logger, magicImport, type OnFailCallback } from '../common';
import type { ComponentInteraction, ModalSubmitInteraction, StringSelectMenuInteraction } from '../structures'; import type { ComponentInteraction, ModalSubmitInteraction, StringSelectMenuInteraction } from '../structures';
import { ComponentCommand, InteractionCommandType } from './componentcommand'; import { ComponentCommand, InteractionCommandType } from './componentcommand';
import type { ComponentContext } from './componentcontext'; import type { ComponentContext } from './componentcontext';
@ -38,6 +38,11 @@ export interface CreateComponentCollectorResult {
callback: ComponentCallback<T>, callback: ComponentCallback<T>,
): void; ): void;
stop(reason?: string): void; stop(reason?: string): void;
waitFor<T extends CollectorInteraction = CollectorInteraction>(
customId: UserMatches,
timeout?: number,
): Promise<T | null>;
resetTimeouts(): void;
} }
export class ComponentHandler extends BaseHandler { export class ComponentHandler extends BaseHandler {
@ -115,6 +120,28 @@ export class ComponentHandler extends BaseHandler {
this.createComponentCollector(messageId, channelId, guildId, options, old.components); this.createComponentCollector(messageId, channelId, guildId, options, old.components);
}); });
}, },
waitFor: (customId, timeout) =>
new Promise(resolve => {
const collector = this.values.get(messageId);
if (!collector) return resolve(null);
let nodeTimeout: NodeJS.Timeout | undefined;
this.values.get(messageId)!.__run(customId, interaction => {
clearTimeout(nodeTimeout);
//@ts-expect-error generic
resolve(interaction);
});
if (timeout && timeout > 0)
nodeTimeout = setTimeout(() => {
resolve(null);
// by default 15 seconds in case user don't do anything
}, timeout);
}),
resetTimeouts: () => {
this.resetTimeouts(messageId);
},
}; };
} }

View File

@ -1,4 +1,4 @@
import type { AllChannels, Interaction, ModalCommand, ModalSubmitInteraction, ReturnCache } from '..'; import type { AllChannels, ModalCommand, ModalSubmitInteraction, ReturnCache } from '..';
import type { import type {
GuildMemberStructure, GuildMemberStructure,
GuildStructure, GuildStructure,
@ -8,12 +8,13 @@ import type {
} from '../client/transformers'; } from '../client/transformers';
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 {
InteractionCreateBodyRequest, InteractionCreateBodyRequest,
InteractionMessageUpdateBodyRequest, InteractionMessageUpdateBodyRequest,
MakeRequired, MakeRequired,
MessageWebhookCreateBodyRequest, MessageWebhookCreateBodyRequest,
ModalCreateBodyRequest, ModalCreateBodyRequest,
ModalCreateOptions,
UnionToTuple, UnionToTuple,
When, When,
} from '../common'; } from '../common';
@ -119,9 +120,11 @@ export class ModalContext<M extends keyof RegisteredMiddlewares = never> extends
return this.interaction.deleteResponse(); return this.interaction.deleteResponse();
} }
modal(body: ModalCreateBodyRequest): ReturnType<Interaction['modal']> { modal(body: ModalCreateBodyRequest, options?: undefined): Promise<undefined>;
//@ts-expect-error modal(body: ModalCreateBodyRequest, options: ModalCreateOptions): Promise<ModalSubmitInteraction | null>;
return this.interaction.modal(body); modal(body: ModalCreateBodyRequest, options?: ModalCreateOptions | undefined) {
// @ts-expect-error
return this.interaction.modal(body, options);
} }
/** /**

View File

@ -4,11 +4,11 @@ import type { FileLoaded } from '../commands/handler';
import { import {
BaseHandler, BaseHandler,
type CamelCase, type CamelCase,
isCloudfareWorker,
type MakeRequired, type MakeRequired,
magicImport,
ReplaceRegex, ReplaceRegex,
type SnakeCase, type SnakeCase,
isCloudfareWorker,
magicImport,
} from '../common'; } from '../common';
import type { ClientEvents } from '../events/hooks'; import type { ClientEvents } from '../events/hooks';
import * as RawEvents from '../events/hooks'; import * as RawEvents from '../events/hooks';

View File

@ -1,3 +1,6 @@
import type { UsingClient } from '../../commands';
import { toCamelCase } from '../../common';
import { type AllChannels, channelFrom } from '../../structures';
import type { import type {
GatewayChannelCreateDispatchData, GatewayChannelCreateDispatchData,
GatewayChannelDeleteDispatchData, GatewayChannelDeleteDispatchData,
@ -5,10 +8,6 @@ import type {
GatewayChannelUpdateDispatchData, GatewayChannelUpdateDispatchData,
} from '../../types'; } from '../../types';
import type { UsingClient } from '../../commands';
import { toCamelCase } from '../../common';
import { type AllChannels, channelFrom } from '../../structures';
export const CHANNEL_CREATE = (self: UsingClient, data: GatewayChannelCreateDispatchData): AllChannels => { export const CHANNEL_CREATE = (self: UsingClient, data: GatewayChannelCreateDispatchData): AllChannels => {
return channelFrom(data, self); return channelFrom(data, self);
}; };

View File

@ -20,8 +20,8 @@ import type {
GatewayGuildIntegrationsUpdateDispatchData, GatewayGuildIntegrationsUpdateDispatchData,
GatewayGuildMemberAddDispatchData, GatewayGuildMemberAddDispatchData,
GatewayGuildMemberRemoveDispatchData, GatewayGuildMemberRemoveDispatchData,
GatewayGuildMemberUpdateDispatchData,
GatewayGuildMembersChunkDispatchData, GatewayGuildMembersChunkDispatchData,
GatewayGuildMemberUpdateDispatchData,
GatewayGuildRoleCreateDispatchData, GatewayGuildRoleCreateDispatchData,
GatewayGuildRoleDeleteDispatchData, GatewayGuildRoleDeleteDispatchData,
GatewayGuildRoleUpdateDispatchData, GatewayGuildRoleUpdateDispatchData,

View File

@ -10,13 +10,13 @@ export * from './interactions';
export * from './invite'; export * from './invite';
export * from './message'; export * from './message';
export * from './presence'; export * from './presence';
export * from './soundboard';
export * from './stage'; export * from './stage';
export * from './thread'; export * from './thread';
export * from './typing'; export * from './typing';
export * from './user'; export * from './user';
export * from './voice'; export * from './voice';
export * from './webhook'; export * from './webhook';
export * from './soundboard';
import type { CamelCase } from '../../common'; import type { CamelCase } from '../../common';
import type * as RawEvents from './index'; import type * as RawEvents from './index';

View File

@ -1,6 +1,6 @@
import { type MessageStructure, Transformers } from '../../client/transformers'; import { type MessageStructure, Transformers } from '../../client/transformers';
import type { UsingClient } from '../../commands'; import type { UsingClient } from '../../commands';
import { type ObjectToLower, type OmitInsert, fakePromise, toCamelCase } from '../../common'; import { fakePromise, type ObjectToLower, type OmitInsert, toCamelCase } from '../../common';
import type { import type {
GatewayMessageCreateDispatchData, GatewayMessageCreateDispatchData,
GatewayMessageDeleteBulkDispatchData, GatewayMessageDeleteBulkDispatchData,

View File

@ -5,8 +5,8 @@ import type {
APISoundBoard, APISoundBoard,
GatewayGuildSoundboardSoundCreateDispatchData, GatewayGuildSoundboardSoundCreateDispatchData,
GatewayGuildSoundboardSoundDeleteDispatchData, GatewayGuildSoundboardSoundDeleteDispatchData,
GatewayGuildSoundboardSoundUpdateDispatchData,
GatewayGuildSoundboardSoundsUpdateDispatchData, GatewayGuildSoundboardSoundsUpdateDispatchData,
GatewayGuildSoundboardSoundUpdateDispatchData,
GatewaySoundboardSoundsDispatchData, GatewaySoundboardSoundsDispatchData,
} from '../../types'; } from '../../types';

View File

@ -5,8 +5,8 @@ import type {
GatewayThreadCreateDispatchData, GatewayThreadCreateDispatchData,
GatewayThreadDeleteDispatchData, GatewayThreadDeleteDispatchData,
GatewayThreadListSyncDispatchData, GatewayThreadListSyncDispatchData,
GatewayThreadMemberUpdateDispatchData,
GatewayThreadMembersUpdateDispatchData, GatewayThreadMembersUpdateDispatchData,
GatewayThreadMemberUpdateDispatchData,
GatewayThreadUpdateDispatchData, GatewayThreadUpdateDispatchData,
} from '../../types'; } from '../../types';

View File

@ -1,4 +1,5 @@
export * from './client'; export * from './client';
import { import {
BaseClient, BaseClient,
type BaseClientOptions, type BaseClientOptions,
@ -10,21 +11,22 @@ import {
import { isCloudfareWorker } from './common'; import { isCloudfareWorker } from './common';
import type { ClientNameEvents, CustomEventsKeys, ResolveEventParams } from './events'; import type { ClientNameEvents, CustomEventsKeys, ResolveEventParams } from './events';
import { GatewayIntentBits } from './types'; import { GatewayIntentBits } from './types';
export { Logger, PermissionStrings, Formatter } from './common';
//
export { Collection, LimitedCollection } from './collection';
// //
export * from './api'; export * from './api';
export * from './builders'; export * from './builders';
export * from './cache'; export * from './cache';
//
export { Collection, LimitedCollection } from './collection';
export * from './commands'; export * from './commands';
export { Formatter, Logger, PermissionStrings } from './common';
export * from './components'; export * from './components';
export * from './events'; export * from './events';
export * from './langs'; export * from './langs';
// //
export { ShardManager, WorkerManager } from './websocket/discord';
//
export * from './structures'; export * from './structures';
//
export { ShardManager, WorkerManager } from './websocket/discord';
/** /**
* Creates an event with the specified data and run function. * Creates an event with the specified data and run function.

View File

@ -14,6 +14,7 @@ export const LangRouter = (userLocale: string, defaultLang: string, langs: Parti
function getValue(locale?: string) { function getValue(locale?: string) {
if (typeof locale === 'undefined') throw new Error('Undefined locale'); if (typeof locale === 'undefined') throw new Error('Undefined locale');
let value = langs[locale] as Record<string, any>; let value = langs[locale] as Record<string, any>;
if (typeof value === 'undefined') throw new Error(`Locale "${locale}" not found`);
for (const i of route) value = value[i]; for (const i of route) value = value[i];
return value; return value;
} }
@ -51,4 +52,4 @@ export type __InternalParseLocale<T extends Record<string, any>> = {
}; };
export type ParseLocales<T extends Record<string, any>> = T; export type ParseLocales<T extends Record<string, any>> = T;
/**Idea inspiration from: FreeAoi */ /**Idea inspiration from: FreeAoi | Fixed by: Drylozu */

View File

@ -1,18 +1,18 @@
import type { GuildMemberStructure, GuildStructure } from '../client'; import type { GuildMemberStructure, GuildStructure, ThreadChannelStructure } from '../client';
import type { UsingClient } from '../commands'; import type { UsingClient } from '../commands';
import type { CreateInviteFromChannel } from '../common'; import type { CreateInviteFromChannel } from '../common';
import type { ObjectToLower, StructPropState, StructStates, ToClass } from '../common/types/util'; import type { ObjectToLower, StructPropState, StructStates, ToClass } from '../common/types/util';
import type { APIGuild, APIPartialGuild, GatewayGuildCreateDispatchData, RESTPatchAPIGuildJSONBody } from '../types'; import type { APIGuild, APIPartialGuild, GatewayGuildCreateDispatchData, RESTPatchAPIGuildJSONBody } from '../types';
import { AutoModerationRule } from './AutoModerationRule'; import { AutoModerationRule } from './AutoModerationRule';
import { BaseChannel, WebhookGuildMethods } from './channels';
import { GuildEmoji } from './Emoji'; import { GuildEmoji } from './Emoji';
import { BaseGuild } from './extra/BaseGuild';
import type { DiscordBase } from './extra/DiscordBase';
import { GuildBan } from './GuildBan'; import { GuildBan } from './GuildBan';
import { GuildMember } from './GuildMember'; import { GuildMember } from './GuildMember';
import { GuildRole } from './GuildRole'; import { GuildRole } from './GuildRole';
import { GuildTemplate } from './GuildTemplate'; import { GuildTemplate } from './GuildTemplate';
import { Sticker } from './Sticker'; import { Sticker } from './Sticker';
import { BaseChannel, WebhookGuildMethods } from './channels';
import { BaseGuild } from './extra/BaseGuild';
import type { DiscordBase } from './extra/DiscordBase';
export interface Guild extends ObjectToLower<Omit<APIGuild, 'stickers' | 'emojis' | 'roles'>>, DiscordBase {} export interface Guild extends ObjectToLower<Omit<APIGuild, 'stickers' | 'emojis' | 'roles'>>, DiscordBase {}
export class Guild<State extends StructStates = 'api'> extends (BaseGuild as unknown as ToClass< export class Guild<State extends StructStates = 'api'> extends (BaseGuild as unknown as ToClass<
@ -71,6 +71,10 @@ export class Guild<State extends StructStates = 'api'> extends (BaseGuild as unk
return this.members.fetch(this.ownerId, force); return this.members.fetch(this.ownerId, force);
} }
listActiveThreads(force = false): Promise<ThreadChannelStructure[]> {
return this.client.threads.listGuildActive(this.id, force);
}
templates = GuildTemplate.methods({ client: this.client, guildId: this.id }); templates = GuildTemplate.methods({ client: this.client, guildId: this.id });
stickers = Sticker.methods({ client: this.client, guildId: this.id }); stickers = Sticker.methods({ client: this.client, guildId: this.id });
members = GuildMember.methods({ client: this.client, guildId: this.id }); members = GuildMember.methods({ client: this.client, guildId: this.id });

View File

@ -3,7 +3,7 @@ import type { GuildBanStructure, GuildStructure } from '../client';
import type { UsingClient } from '../commands'; import type { UsingClient } from '../commands';
import { Formatter, type MethodContext, type ObjectToLower } from '../common'; import { Formatter, type MethodContext, type ObjectToLower } from '../common';
import type { BanShorter } from '../common/shorters/bans'; import type { BanShorter } from '../common/shorters/bans';
import type { APIBan, ActuallyBan, RESTGetAPIGuildBansQuery } from '../types'; import type { ActuallyBan, APIBan, RESTGetAPIGuildBansQuery } from '../types';
import { DiscordBase } from './extra/DiscordBase'; import { DiscordBase } from './extra/DiscordBase';
export interface GuildBan extends DiscordBase, ObjectToLower<Omit<APIBan, 'id'>> {} export interface GuildBan extends DiscordBase, ObjectToLower<Omit<APIBan, 'id'>> {}

View File

@ -46,7 +46,7 @@ export class GuildRole extends DiscordBase {
return this.client.roles.edit(this.guildId, this.id, body); return this.client.roles.edit(this.guildId, this.id, body);
} }
delete(reason?: string): Promise<GuildRoleStructure> { delete(reason?: string) {
return this.client.roles.delete(this.guildId, this.id, reason); return this.client.roles.delete(this.guildId, this.id, reason);
} }
@ -61,8 +61,7 @@ export class GuildRole extends DiscordBase {
list: (force = false): Promise<GuildRoleStructure[]> => ctx.client.roles.list(ctx.guildId, force), list: (force = false): Promise<GuildRoleStructure[]> => ctx.client.roles.list(ctx.guildId, force),
edit: (roleId: string, body: RESTPatchAPIGuildRoleJSONBody, reason?: string): Promise<GuildRoleStructure> => edit: (roleId: string, body: RESTPatchAPIGuildRoleJSONBody, reason?: string): Promise<GuildRoleStructure> =>
ctx.client.roles.edit(ctx.guildId, roleId, body, reason), ctx.client.roles.edit(ctx.guildId, roleId, body, reason),
delete: (roleId: string, reason?: string): Promise<GuildRoleStructure> => delete: (roleId: string, reason?: string) => ctx.client.roles.delete(ctx.guildId, roleId, reason),
ctx.client.roles.delete(ctx.guildId, roleId, reason),
editPositions: (body: RESTPatchAPIGuildRolePositionsJSONBody): Promise<GuildRoleStructure[]> => editPositions: (body: RESTPatchAPIGuildRolePositionsJSONBody): Promise<GuildRoleStructure[]> =>
ctx.client.roles.editPositions(ctx.guildId, body), ctx.client.roles.editPositions(ctx.guildId, body),
}; };

View File

@ -1,3 +1,34 @@
import type { RawFile } from '../api';
import { ActionRow, Embed, Modal, PollBuilder, resolveAttachment, resolveFiles } from '../builders';
import type { ReturnCache } from '../cache';
import {
type EntitlementStructure,
type GuildRoleStructure,
type GuildStructure,
type InteractionGuildMemberStructure,
type MessageStructure,
type OptionResolverStructure,
Transformers,
type UserStructure,
type WebhookMessageStructure,
} from '../client/transformers';
import type { ContextOptionsResolved, UsingClient } from '../commands';
import {
type ComponentInteractionMessageUpdate,
type InteractionCreateBodyRequest,
type InteractionMessageUpdateBodyRequest,
type MessageCreateBodyRequest,
type MessageUpdateBodyRequest,
type MessageWebhookCreateBodyRequest,
type ModalCreateBodyRequest,
ModalCreateOptions,
type ObjectToLower,
type OmitInsert,
type ToClass,
toCamelCase,
type When,
} from '../common';
import { mix } from '../deps/mixer';
import { import {
type APIActionRowComponent, type APIActionRowComponent,
type APIApplicationCommandAutocompleteInteraction, type APIApplicationCommandAutocompleteInteraction,
@ -41,37 +72,6 @@ import {
type RESTPostAPIInteractionCallbackJSONBody, type RESTPostAPIInteractionCallbackJSONBody,
type RESTPostAPIInteractionCallbackResult, type RESTPostAPIInteractionCallbackResult,
} from '../types'; } from '../types';
import type { RawFile } from '../api';
import { ActionRow, Embed, Modal, PollBuilder, resolveAttachment, resolveFiles } from '../builders';
import type { ReturnCache } from '../cache';
import {
type EntitlementStructure,
type GuildRoleStructure,
type GuildStructure,
type InteractionGuildMemberStructure,
type MessageStructure,
type OptionResolverStructure,
Transformers,
type UserStructure,
type WebhookMessageStructure,
} from '../client/transformers';
import type { ContextOptionsResolved, UsingClient } from '../commands';
import {
type ComponentInteractionMessageUpdate,
type InteractionCreateBodyRequest,
type InteractionMessageUpdateBodyRequest,
type MessageCreateBodyRequest,
type MessageUpdateBodyRequest,
type MessageWebhookCreateBodyRequest,
type ModalCreateBodyRequest,
type ObjectToLower,
type OmitInsert,
type ToClass,
type When,
toCamelCase,
} from '../common';
import { mix } from '../deps/mixer';
import { type AllChannels, channelFrom } from './'; import { type AllChannels, channelFrom } from './';
import { DiscordBase } from './extra/DiscordBase'; import { DiscordBase } from './extra/DiscordBase';
import { PermissionsBitField } from './extra/Permissions'; import { PermissionsBitField } from './extra/Permissions';
@ -315,7 +315,6 @@ export class BaseInteraction<
switch (gateway.type) { switch (gateway.type) {
case InteractionType.ApplicationCommandAutocomplete: case InteractionType.ApplicationCommandAutocomplete:
return new AutocompleteInteraction(client, gateway, undefined, __reply); return new AutocompleteInteraction(client, gateway, undefined, __reply);
// biome-ignore lint/suspicious/noFallthroughSwitchClause: bad interaction between biome and ts-server
case InteractionType.ApplicationCommand: case InteractionType.ApplicationCommand:
switch (gateway.data.type) { switch (gateway.data.type) {
case ApplicationCommandType.ChatInput: case ApplicationCommandType.ChatInput:
@ -474,11 +473,37 @@ export class Interaction<
) as never; ) as never;
} }
modal(body: ModalCreateBodyRequest) { modal(body: ModalCreateBodyRequest, options?: undefined): Promise<undefined>;
return this.reply({ modal(body: ModalCreateBodyRequest, options: ModalCreateOptions): Promise<ModalSubmitInteraction | null>;
async modal(body: ModalCreateBodyRequest, options?: ModalCreateOptions | undefined) {
if (options !== undefined && !(body instanceof Modal)) {
body = new Modal(body);
}
if (options === undefined)
return this.reply({
type: InteractionResponseType.Modal,
data: body,
});
const promise = new Promise<ModalSubmitInteraction | null>(res => {
let nodeTimeout: NodeJS.Timeout | undefined;
// body is always a modal here, so we can safely cast it
(body as Modal).__exec = (interaction: ModalSubmitInteraction) => {
res(interaction);
clearTimeout(nodeTimeout);
};
if (options?.waitFor && options?.waitFor > 0) {
nodeTimeout = setTimeout(() => {
res(null);
}, options.waitFor);
}
});
await this.reply({
type: InteractionResponseType.Modal, type: InteractionResponseType.Modal,
data: body, data: body,
}); });
return promise;
} }
async editOrReply<FR extends boolean = false>( async editOrReply<FR extends boolean = false>(

View File

@ -1,4 +1,4 @@
import { type AllChannels, Embed, type ReturnCache, componentFactory } from '..'; import { type AllChannels, componentFactory, Embed, type ReturnCache } from '..';
import type { ListenerOptions } from '../builders'; import type { ListenerOptions } from '../builders';
import { import {
type GuildMemberStructure, type GuildMemberStructure,
@ -11,8 +11,7 @@ import {
type WebhookStructure, type WebhookStructure,
} from '../client/transformers'; } from '../client/transformers';
import type { UsingClient } from '../commands'; import type { UsingClient } from '../commands';
import { type ObjectToLower, toCamelCase } from '../common'; import { Formatter, type ObjectToLower, toCamelCase } from '../common';
import { Formatter } from '../common';
import type { EmojiResolvable } from '../common/types/resolvables'; import type { EmojiResolvable } from '../common/types/resolvables';
import type { MessageCreateBodyRequest, MessageUpdateBodyRequest } from '../common/types/write'; import type { MessageCreateBodyRequest, MessageUpdateBodyRequest } from '../common/types/write';
import type { TopLevelComponents } from '../components'; import type { TopLevelComponents } from '../components';
@ -24,8 +23,8 @@ import type {
APIUser, APIUser,
GatewayMessageCreateDispatchData, GatewayMessageCreateDispatchData,
} from '../types'; } from '../types';
import type { MessageWebhookMethodEditParams, MessageWebhookMethodWriteParams } from './Webhook';
import { DiscordBase } from './extra/DiscordBase'; import { DiscordBase } from './extra/DiscordBase';
import type { MessageWebhookMethodEditParams, MessageWebhookMethodWriteParams } from './Webhook';
export type MessageData = APIMessage | GatewayMessageCreateDispatchData; export type MessageData = APIMessage | GatewayMessageCreateDispatchData;

View File

@ -5,8 +5,8 @@ import {
type BaseChannelStructure, type BaseChannelStructure,
type BaseGuildChannelStructure, type BaseGuildChannelStructure,
type CategoryChannelStructure, type CategoryChannelStructure,
type DMChannelStructure,
type DirectoryChannelStructure, type DirectoryChannelStructure,
type DMChannelStructure,
type ForumChannelStructure, type ForumChannelStructure,
type GuildMemberStructure, type GuildMemberStructure,
type GuildStructure, type GuildStructure,
@ -26,13 +26,13 @@ import type { SeyfertChannelMap, UsingClient } from '../commands';
import { import {
type CreateInviteFromChannel, type CreateInviteFromChannel,
type EmojiResolvable, type EmojiResolvable,
fakePromise,
type MessageCreateBodyRequest, type MessageCreateBodyRequest,
type MessageUpdateBodyRequest, type MessageUpdateBodyRequest,
type MethodContext, type MethodContext,
type ObjectToLower, type ObjectToLower,
type StringToNumber, type StringToNumber,
type ToClass, type ToClass,
fakePromise,
} from '../common'; } from '../common';
import { mix } from '../deps/mixer'; import { mix } from '../deps/mixer';
import { import {
@ -63,9 +63,9 @@ import {
type ThreadAutoArchiveDuration, type ThreadAutoArchiveDuration,
VideoQualityMode, VideoQualityMode,
} from '../types'; } from '../types';
import { DiscordBase } from './extra/DiscordBase';
import type { GuildMember } from './GuildMember'; import type { GuildMember } from './GuildMember';
import type { GuildRole } from './GuildRole'; import type { GuildRole } from './GuildRole';
import { DiscordBase } from './extra/DiscordBase';
export class BaseNoEditableChannel<T extends ChannelType> extends DiscordBase<APIChannelBase<ChannelType>> { export class BaseNoEditableChannel<T extends ChannelType> extends DiscordBase<APIChannelBase<ChannelType>> {
declare type: T; declare type: T;
@ -581,13 +581,15 @@ export class ForumChannel extends BaseGuildChannel {
export interface ThreadChannel export interface ThreadChannel
extends ObjectToLower<Omit<APIThreadChannel, 'permission_overwrites' | 'guild_id'>>, extends ObjectToLower<Omit<APIThreadChannel, 'permission_overwrites' | 'guild_id'>>,
Omit<TextBaseGuildChannel, 'edit' | 'parentId'> {} Omit<TextBaseGuildChannel, 'edit' | 'parentId'> {
parentId: string;
}
@mix(TextBaseGuildChannel) @mix(TextBaseGuildChannel)
export class ThreadChannel extends BaseChannel< export class ThreadChannel extends BaseChannel<
ChannelType.PublicThread | ChannelType.AnnouncementThread | ChannelType.PrivateThread ChannelType.PublicThread | ChannelType.AnnouncementThread | ChannelType.PrivateThread
> { > {
parentId!: string;
declare type: ChannelType.PublicThread | ChannelType.AnnouncementThread | ChannelType.PrivateThread; declare type: ChannelType.PublicThread | ChannelType.AnnouncementThread | ChannelType.PrivateThread;
webhooks = WebhookChannelMethods.channel({ webhooks = WebhookChannelMethods.channel({
client: this.client, client: this.client,
channelId: this.parentId, channelId: this.parentId,

View File

@ -1,6 +1,6 @@
import type { ReturnCache, WorkerClient } from '../..'; import type { ReturnCache, WorkerClient } from '../..';
import type { GuildStructure } from '../../client'; import type { GuildStructure } from '../../client';
import { type ObjectToLower, calculateShardId } from '../../common'; import { calculateShardId, type ObjectToLower } from '../../common';
import type { ImageOptions } from '../../common/types/options'; import type { ImageOptions } from '../../common/types/options';
import { type APIPartialGuild, GuildFeature } from '../../types'; import { type APIPartialGuild, GuildFeature } from '../../types';
import type { ShardManager } from '../../websocket'; import type { ShardManager } from '../../websocket';

View File

@ -1,19 +1,19 @@
export * from './AnonymousGuild'; export * from './AnonymousGuild';
export * from './AutoModerationRule'; export * from './AutoModerationRule';
export * from './ClientUser'; export * from './ClientUser';
export * from './Guild'; export * from './channels';
export * from './Emoji'; export * from './Emoji';
export * from './Entitlement';
export * from './Guild';
export * from './GuildBan';
export * from './GuildMember'; export * from './GuildMember';
export * from './GuildPreview'; export * from './GuildPreview';
export * from './GuildRole'; export * from './GuildRole';
export * from './GuildTemplate'; export * from './GuildTemplate';
export * from './Interaction'; export * from './Interaction';
export * from './Message'; export * from './Message';
export * from './Poll';
export * from './Sticker'; export * from './Sticker';
export * from './User'; export * from './User';
export * from './VoiceState'; export * from './VoiceState';
export * from './Webhook'; export * from './Webhook';
export * from './channels';
export * from './Poll';
export * from './GuildBan';
export * from './Entitlement';

View File

@ -23,9 +23,9 @@
*/ */
export * from './gateway'; export * from './gateway';
export * from './rest';
export * from './payloads'; export * from './payloads';
export * from './rest'; export * from './rest';
export * from './rest';
export * from './utils'; export * from './utils';
/** /**

View File

@ -1,7 +1,6 @@
import type { ApplicationIntegrationType, InteractionContextType } from '..';
import type { ChannelType, Permissions, Snowflake } from '../..'; import type { ChannelType, Permissions, Snowflake } from '../..';
import type { LocaleString } from '../../rest'; import type { LocaleString } from '../../rest';
import type { ApplicationIntegrationType, InteractionContextType } from '..';
import type { import type {
APIAttachment, APIAttachment,
APIChannel, APIChannel,

View File

@ -2,9 +2,9 @@
* Types extracted from https://discord.com/developers/docs/resources/application * Types extracted from https://discord.com/developers/docs/resources/application
*/ */
import type { APIEmoji, LocalizationMap } from '.';
import type { Permissions, Snowflake } from '..';
import type { MakeRequired } from '../../common'; import type { MakeRequired } from '../../common';
import type { Permissions, Snowflake } from '..';
import type { APIEmoji, LocalizationMap } from '.';
import type { APIPartialGuild } from './guild'; import type { APIPartialGuild } from './guild';
import type { ApplicationIntegrationType } from './interactions'; import type { ApplicationIntegrationType } from './interactions';
import type { OAuth2Scopes } from './oauth2'; import type { OAuth2Scopes } from './oauth2';

View File

@ -1,5 +1,5 @@
import type { APIAttachment, Snowflake } from '..';
import type { Identify, MakeRequired } from '../../common'; import type { Identify, MakeRequired } from '../../common';
import type { APIAttachment, Snowflake } from '..';
import type { ChannelType } from '../utils'; import type { ChannelType } from '../utils';
/** /**
@ -260,7 +260,7 @@ export type APIRoleSelectComponent = APIBaseAutoPopulatedSelectMenuComponent<
*/ */
export type APIMentionableSelectComponent = APIBaseAutoPopulatedSelectMenuComponent< export type APIMentionableSelectComponent = APIBaseAutoPopulatedSelectMenuComponent<
ComponentType.MentionableSelect, ComponentType.MentionableSelect,
SelectMenuDefaultValueType.Role | SelectMenuDefaultValueType.User SelectMenuDefaultValueType
>; >;
/** /**

View File

@ -1,6 +1,7 @@
import type { Snowflake } from '..'; import type { Snowflake } from '..';
import type { APIGuildMember } from './guild'; import type { APIGuildMember } from './guild';
import type { APIUser } from './user'; import type { APIUser } from './user';
interface APIGuildScheduledEventBase<Type extends GuildScheduledEventEntityType> { interface APIGuildScheduledEventBase<Type extends GuildScheduledEventEntityType> {
/** /**
* The id of the guild event * The id of the guild event

View File

@ -2,15 +2,18 @@ export * from './application';
export * from './auditLog'; export * from './auditLog';
export * from './autoModeration'; export * from './autoModeration';
export * from './channel'; export * from './channel';
export * from './components';
export * from './emoji'; export * from './emoji';
export * from './gateway'; export * from './gateway';
export * from './guild'; export * from './guild';
export * from './guildScheduledEvent'; export * from './guildScheduledEvent';
export * from './interactions'; export * from './interactions';
export * from './invite'; export * from './invite';
export * from './monetization';
export * from './oauth2'; export * from './oauth2';
export * from './poll';
export * from './permissions'; export * from './permissions';
export * from './poll';
export * from './soundboard';
export * from './stageInstance'; export * from './stageInstance';
export * from './sticker'; export * from './sticker';
export * from './teams'; export * from './teams';
@ -18,9 +21,6 @@ export * from './template';
export * from './user'; export * from './user';
export * from './voice'; export * from './voice';
export * from './webhook'; export * from './webhook';
export * from './monetization';
export * from './soundboard';
export * from './components';
import type { LocaleString } from '../rest'; import type { LocaleString } from '../rest';

View File

@ -85,7 +85,7 @@ export type RESTPatchAPIApplicationEmojiResult = APIApplicationEmoji;
/** /**
* https://discord.com/developers/docs/resources/emoji#delete-application-emoji * https://discord.com/developers/docs/resources/emoji#delete-application-emoji
*/ */
export type RESTDeleteAPIApplicationEmojiResult = never; export type RESTDeleteAPIApplicationEmojiResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/application#get-application-activity-instance * https://discord.com/developers/docs/resources/application#get-application-activity-instance

View File

@ -80,4 +80,4 @@ export type RESTPatchAPIAutoModerationRuleResult = APIAutoModerationRule;
/** /**
* https://discord.com/developers/docs/resources/auto-moderation#delete-auto-moderation-rule * https://discord.com/developers/docs/resources/auto-moderation#delete-auto-moderation-rule
*/ */
export type RESTDeleteAPIAutoModerationRuleResult = never; export type RESTDeleteAPIAutoModerationRuleResult = undefined;

View File

@ -344,17 +344,17 @@ export type RESTPostAPIChannelMessageCrosspostResult = APIMessage;
/** /**
* https://discord.com/developers/docs/resources/channel#create-reaction * https://discord.com/developers/docs/resources/channel#create-reaction
*/ */
export type RESTPutAPIChannelMessageReactionResult = never; export type RESTPutAPIChannelMessageReactionResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/channel#delete-own-reaction * https://discord.com/developers/docs/resources/channel#delete-own-reaction
*/ */
export type RESTDeleteAPIChannelMessageOwnReaction = never; export type RESTDeleteAPIChannelMessageOwnReaction = undefined;
/** /**
* https://discord.com/developers/docs/resources/channel#delete-user-reaction * https://discord.com/developers/docs/resources/channel#delete-user-reaction
*/ */
export type RESTDeleteAPIChannelMessageUserReactionResult = never; export type RESTDeleteAPIChannelMessageUserReactionResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/channel#get-reactions * https://discord.com/developers/docs/resources/channel#get-reactions
@ -392,12 +392,12 @@ export type RESTGetAPIChannelMessageReactionUsersResult = APIUser[];
/** /**
* https://discord.com/developers/docs/resources/channel#delete-all-reactions * https://discord.com/developers/docs/resources/channel#delete-all-reactions
*/ */
export type RESTDeleteAPIChannelAllMessageReactionsResult = never; export type RESTDeleteAPIChannelAllMessageReactionsResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji * https://discord.com/developers/docs/resources/channel#delete-all-reactions-for-emoji
*/ */
export type RESTDeleteAPIChannelMessageReactionResult = never; export type RESTDeleteAPIChannelMessageReactionResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/channel#edit-message * https://discord.com/developers/docs/resources/channel#edit-message
@ -464,7 +464,7 @@ export type RESTPatchAPIChannelMessageResult = APIMessage;
/** /**
* https://discord.com/developers/docs/resources/channel#delete-message * https://discord.com/developers/docs/resources/channel#delete-message
*/ */
export type RESTDeleteAPIChannelMessageResult = never; export type RESTDeleteAPIChannelMessageResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/channel#bulk-delete-messages * https://discord.com/developers/docs/resources/channel#bulk-delete-messages
@ -479,7 +479,7 @@ export interface RESTPostAPIChannelMessagesBulkDeleteJSONBody {
/** /**
* https://discord.com/developers/docs/resources/channel#bulk-delete-messages * https://discord.com/developers/docs/resources/channel#bulk-delete-messages
*/ */
export type RESTPostAPIChannelMessagesBulkDeleteResult = never; export type RESTPostAPIChannelMessagesBulkDeleteResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/channel#edit-channel-permissions * https://discord.com/developers/docs/resources/channel#edit-channel-permissions
@ -510,7 +510,7 @@ export interface RESTPutAPIChannelPermissionJSONBody {
/** /**
* https://discord.com/developers/docs/resources/channel#edit-channel-permissions * https://discord.com/developers/docs/resources/channel#edit-channel-permissions
*/ */
export type RESTPutAPIChannelPermissionResult = never; export type RESTPutAPIChannelPermissionResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/channel#get-channel-invites * https://discord.com/developers/docs/resources/channel#get-channel-invites
@ -574,7 +574,7 @@ export type RESTPostAPIChannelInviteResult = APIExtendedInvite;
/** /**
* https://discord.com/developers/docs/resources/channel#delete-channel-permission * https://discord.com/developers/docs/resources/channel#delete-channel-permission
*/ */
export type RESTDeleteAPIChannelPermissionResult = never; export type RESTDeleteAPIChannelPermissionResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/channel#follow-news-channel * https://discord.com/developers/docs/resources/channel#follow-news-channel
@ -594,7 +594,7 @@ export type RESTPostAPIChannelFollowersResult = APIFollowedChannel;
/** /**
* https://discord.com/developers/docs/resources/channel#trigger-typing-indicator * https://discord.com/developers/docs/resources/channel#trigger-typing-indicator
*/ */
export type RESTPostAPIChannelTypingResult = never; export type RESTPostAPIChannelTypingResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/channel#get-pinned-messages * https://discord.com/developers/docs/resources/channel#get-pinned-messages
@ -604,12 +604,12 @@ export type RESTGetAPIChannelPinsResult = APIMessage[];
/** /**
* https://discord.com/developers/docs/resources/channel#pin-message * https://discord.com/developers/docs/resources/channel#pin-message
*/ */
export type RESTPutAPIChannelPinResult = never; export type RESTPutAPIChannelPinResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/channel#unpin-message * https://discord.com/developers/docs/resources/channel#unpin-message
*/ */
export type RESTDeleteAPIChannelPinResult = never; export type RESTDeleteAPIChannelPinResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/channel#group-dm-add-recipient * https://discord.com/developers/docs/resources/channel#group-dm-add-recipient
@ -711,12 +711,12 @@ export type RESTPostAPIChannelThreadsResult = APIChannel;
/** /**
* https://discord.com/developers/docs/resources/channel#join-thread * https://discord.com/developers/docs/resources/channel#join-thread
*/ */
export type RESTPutAPIChannelThreadMembersResult = never; export type RESTPutAPIChannelThreadMembersResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/channel#leave-thread * https://discord.com/developers/docs/resources/channel#leave-thread
*/ */
export type RESTDeleteAPIChannelThreadMembersResult = never; export type RESTDeleteAPIChannelThreadMembersResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/channel#get-thread-member * https://discord.com/developers/docs/resources/channel#get-thread-member

View File

@ -58,4 +58,4 @@ export type RESTPatchAPIGuildEmojiResult = APIEmoji;
/** /**
* https://discord.com/developers/docs/resources/emoji#delete-guild-emoji * https://discord.com/developers/docs/resources/emoji#delete-guild-emoji
*/ */
export type RESTDeleteAPIGuildEmojiResult = never; export type RESTDeleteAPIGuildEmojiResult = undefined;

View File

@ -322,7 +322,7 @@ export type RESTPatchAPIGuildResult = APIGuild;
/** /**
* https://discord.com/developers/docs/resources/guild#delete-guild * https://discord.com/developers/docs/resources/guild#delete-guild
*/ */
export type RESTDeleteAPIGuildResult = never; export type RESTDeleteAPIGuildResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/guild#get-guild-channels * https://discord.com/developers/docs/resources/guild#get-guild-channels
@ -364,7 +364,7 @@ export type RESTPatchAPIGuildChannelPositionsJSONBody = {
/** /**
* https://discord.com/developers/docs/resources/guild#modify-guild-channel-positions * https://discord.com/developers/docs/resources/guild#modify-guild-channel-positions
*/ */
export type RESTPatchAPIGuildChannelPositionsResult = never; export type RESTPatchAPIGuildChannelPositionsResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/guild#list-active-guild-threads * https://discord.com/developers/docs/resources/guild#list-active-guild-threads
@ -513,17 +513,17 @@ export interface RESTPatchAPICurrentGuildMemberJSONBody {
/** /**
* https://discord.com/developers/docs/resources/guild#add-guild-member-role * https://discord.com/developers/docs/resources/guild#add-guild-member-role
*/ */
export type RESTPutAPIGuildMemberRoleResult = never; export type RESTPutAPIGuildMemberRoleResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/guild#remove-guild-member-role * https://discord.com/developers/docs/resources/guild#remove-guild-member-role
*/ */
export type RESTDeleteAPIGuildMemberRoleResult = never; export type RESTDeleteAPIGuildMemberRoleResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/guild#remove-guild-member * https://discord.com/developers/docs/resources/guild#remove-guild-member
*/ */
export type RESTDeleteAPIGuildMemberResult = never; export type RESTDeleteAPIGuildMemberResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/guild#get-guild-bans * https://discord.com/developers/docs/resources/guild#get-guild-bans
@ -568,12 +568,12 @@ export interface RESTPutAPIGuildBanJSONBody {
/** /**
* https://discord.com/developers/docs/resources/guild#create-guild-ban * https://discord.com/developers/docs/resources/guild#create-guild-ban
*/ */
export type RESTPutAPIGuildBanResult = never; export type RESTPutAPIGuildBanResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/guild#remove-guild-ban * https://discord.com/developers/docs/resources/guild#remove-guild-ban
*/ */
export type RESTDeleteAPIGuildBanResult = never; export type RESTDeleteAPIGuildBanResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/guild#bulk-guild-ban * https://discord.com/developers/docs/resources/guild#bulk-guild-ban
@ -723,7 +723,7 @@ export type RESTPatchAPIGuildRoleResult = APIRole;
/** /**
* https://discord.com/developers/docs/resources/guild#delete-guild-role * https://discord.com/developers/docs/resources/guild#delete-guild-role
*/ */
export type RESTDeleteAPIGuildRoleResult = never; export type RESTDeleteAPIGuildRoleResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/guild#get-guild-prune-count * https://discord.com/developers/docs/resources/guild#get-guild-prune-count
@ -800,7 +800,7 @@ export type RESTGetAPIGuildIntegrationsResult = APIGuildIntegration[];
/** /**
* https://discord.com/developers/docs/resources/guild#delete-guild-integration * https://discord.com/developers/docs/resources/guild#delete-guild-integration
*/ */
export type RESTDeleteAPIGuildIntegrationResult = never; export type RESTDeleteAPIGuildIntegrationResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/guild#get-guild-widget-settings * https://discord.com/developers/docs/resources/guild#get-guild-widget-settings
@ -888,7 +888,7 @@ export interface RESTPatchAPIGuildVoiceStateCurrentMemberJSONBody {
/** /**
* https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state * https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state
*/ */
export type RESTPatchAPIGuildVoiceStateCurrentMemberResult = never; export type RESTPatchAPIGuildVoiceStateCurrentMemberResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/guild#modify-user-voice-state * https://discord.com/developers/docs/resources/guild#modify-user-voice-state
@ -907,7 +907,7 @@ export interface RESTPatchAPIGuildVoiceStateUserJSONBody {
/** /**
* https://discord.com/developers/docs/resources/guild#modify-user-voice-state * https://discord.com/developers/docs/resources/guild#modify-user-voice-state
*/ */
export type RESTPatchAPIGuildVoiceStateUserResult = never; export type RESTPatchAPIGuildVoiceStateUserResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/guild#get-guild-welcome-screen * https://discord.com/developers/docs/resources/guild#get-guild-welcome-screen

View File

@ -106,7 +106,7 @@ export type RESTPatchAPIGuildScheduledEventResult = APIGuildScheduledEvent;
/** /**
* https://discord.com/developers/docs/resources/guild-scheduled-event#delete-guild-scheduled-event * https://discord.com/developers/docs/resources/guild-scheduled-event#delete-guild-scheduled-event
*/ */
export type RESTDeleteAPIGuildScheduledEventResult = never; export type RESTDeleteAPIGuildScheduledEventResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event-users * https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event-users

View File

@ -10,16 +10,16 @@ export * from './guild';
export * from './guildScheduledEvent'; export * from './guildScheduledEvent';
export * from './interactions'; export * from './interactions';
export * from './invite'; export * from './invite';
export * from './monetization';
export * from './oauth2'; export * from './oauth2';
export * from './poll'; export * from './poll';
export * from './soundboard';
export * from './stageInstance'; export * from './stageInstance';
export * from './sticker'; export * from './sticker';
export * from './template'; export * from './template';
export * from './user'; export * from './user';
export * from './voice'; export * from './voice';
export * from './webhook'; export * from './webhook';
export * from './monetization';
export * from './soundboard';
export type DefaultUserAvatarAssets = 0 | 1 | 2 | 3 | 4 | 5; export type DefaultUserAvatarAssets = 0 | 1 | 2 | 3 | 4 | 5;

View File

@ -86,7 +86,7 @@ export enum EntitlementOwnerType {
/** /**
* https://discord.com/developers/docs/monetization/entitlements#delete-test-entitlement * https://discord.com/developers/docs/monetization/entitlements#delete-test-entitlement
*/ */
export type RESTDeleteAPIEntitlementResult = never; export type RESTDeleteAPIEntitlementResult = undefined;
/** /**
* https://discord.com/developers/docs/monetization/skus#list-skus * https://discord.com/developers/docs/monetization/skus#list-skus
@ -96,7 +96,7 @@ export type RESTGetAPISKUsResult = APISKU[];
/** /**
* https://discord.com/developers/docs/monetization/entitlements#consume-an-entitlement * https://discord.com/developers/docs/monetization/entitlements#consume-an-entitlement
*/ */
export type RESTPostAPIEntitlementConsumeResult = never; export type RESTPostAPIEntitlementConsumeResult = undefined;
/** /**
* https://canary.discord.com/developers/docs/resources/subscription#query-string-params * https://canary.discord.com/developers/docs/resources/subscription#query-string-params

View File

@ -67,4 +67,4 @@ export type RESTPatchAPIGuildSoundboardSoundResult = APISoundBoard;
* This endpoint supports the X-Audit-Log-Reason header. * This endpoint supports the X-Audit-Log-Reason header.
* @fires GuildSoundboardSoundDelete * @fires GuildSoundboardSoundDelete
*/ */
export type RESTDeleteAPIGuildSoundboardSoundResult = never; export type RESTDeleteAPIGuildSoundboardSoundResult = undefined;

View File

@ -61,4 +61,4 @@ export type RESTPatchAPIStageInstanceResult = APIStageInstance;
/** /**
* https://discord.com/developers/docs/resources/stage-instance#delete-stage-instance * https://discord.com/developers/docs/resources/stage-instance#delete-stage-instance
*/ */
export type RESTDeleteAPIStageInstanceResult = never; export type RESTDeleteAPIStageInstanceResult = undefined;

View File

@ -82,4 +82,4 @@ export type RESTPatchAPIGuildStickerResult = APISticker;
/** /**
* https://discord.com/developers/docs/resources/sticker#delete-guild-sticker * https://discord.com/developers/docs/resources/sticker#delete-guild-sticker
*/ */
export type RESTDeleteAPIGuildStickerResult = never; export type RESTDeleteAPIGuildStickerResult = undefined;

View File

@ -91,7 +91,7 @@ export type RESTGetAPICurrentUserGuildsResult = RESTAPIPartialCurrentUserGuild[]
/** /**
* https://discord.com/developers/docs/resources/user#leave-guild * https://discord.com/developers/docs/resources/user#leave-guild
*/ */
export type RESTDeleteAPICurrentUserGuildResult = never; export type RESTDeleteAPICurrentUserGuildResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/user#create-dm * https://discord.com/developers/docs/resources/user#create-dm

View File

@ -89,12 +89,12 @@ export type RESTPatchAPIWebhookWithTokenResult = RESTGetAPIWebhookWithTokenResul
/** /**
* https://discord.com/developers/docs/resources/webhook#delete-webhook * https://discord.com/developers/docs/resources/webhook#delete-webhook
*/ */
export type RESTDeleteAPIWebhookResult = never; export type RESTDeleteAPIWebhookResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/webhook#delete-webhook-with-token * https://discord.com/developers/docs/resources/webhook#delete-webhook-with-token
*/ */
export type RESTDeleteAPIWebhookWithTokenResult = never; export type RESTDeleteAPIWebhookWithTokenResult = undefined;
/** /**
* https://discord.com/developers/docs/resources/webhook#execute-webhook * https://discord.com/developers/docs/resources/webhook#execute-webhook
@ -202,7 +202,7 @@ export interface RESTPostAPIWebhookWithTokenQuery {
/** /**
* https://discord.com/developers/docs/resources/webhook#execute-webhook * https://discord.com/developers/docs/resources/webhook#execute-webhook
*/ */
export type RESTPostAPIWebhookWithTokenResult = never; export type RESTPostAPIWebhookWithTokenResult = undefined;
/** /**
* Received when a call to https://discord.com/developers/docs/resources/webhook#execute-webhook receives * Received when a call to https://discord.com/developers/docs/resources/webhook#execute-webhook receives
@ -220,7 +220,7 @@ export type RESTPostAPIWebhookWithTokenSlackQuery = Omit<RESTPostAPIWebhookWithT
/** /**
* https://discord.com/developers/docs/resources/webhook#execute-slackcompatible-webhook * https://discord.com/developers/docs/resources/webhook#execute-slackcompatible-webhook
*/ */
export type RESTPostAPIWebhookWithTokenSlackResult = never; export type RESTPostAPIWebhookWithTokenSlackResult = undefined;
/** /**
* Received when a call to https://discord.com/developers/docs/resources/webhook#execute-webhook receives * Received when a call to https://discord.com/developers/docs/resources/webhook#execute-webhook receives
@ -238,7 +238,7 @@ export type RESTPostAPIWebhookWithTokenGitHubQuery = Omit<RESTPostAPIWebhookWith
/** /**
* https://discord.com/developers/docs/resources/webhook#execute-githubcompatible-webhook * https://discord.com/developers/docs/resources/webhook#execute-githubcompatible-webhook
*/ */
export type RESTPostAPIWebhookWithTokenGitHubResult = never; export type RESTPostAPIWebhookWithTokenGitHubResult = undefined;
/** /**
* Received when a call to https://discord.com/developers/docs/resources/webhook#execute-webhook receives * Received when a call to https://discord.com/developers/docs/resources/webhook#execute-webhook receives
@ -297,4 +297,4 @@ export type RESTPatchAPIWebhookWithTokenMessageResult = APIMessage;
/** /**
* https://discord.com/developers/docs/resources/webhook#delete-webhook-message * https://discord.com/developers/docs/resources/webhook#delete-webhook-message
*/ */
export type RESTDeleteAPIWebhookWithTokenMessageResult = never; export type RESTDeleteAPIWebhookWithTokenMessageResult = undefined;

View File

@ -23,8 +23,8 @@ import type {
GatewayGuildIntegrationsUpdateDispatchData, GatewayGuildIntegrationsUpdateDispatchData,
GatewayGuildMemberAddDispatchData, GatewayGuildMemberAddDispatchData,
GatewayGuildMemberRemoveDispatchData, GatewayGuildMemberRemoveDispatchData,
GatewayGuildMemberUpdateDispatchData,
GatewayGuildMembersChunkDispatchData, GatewayGuildMembersChunkDispatchData,
GatewayGuildMemberUpdateDispatchData,
GatewayGuildRoleCreateDispatchData, GatewayGuildRoleCreateDispatchData,
GatewayGuildRoleDeleteDispatchData, GatewayGuildRoleDeleteDispatchData,
GatewayGuildRoleUpdateDispatchData, GatewayGuildRoleUpdateDispatchData,
@ -55,8 +55,8 @@ import type {
GatewayThreadCreateDispatchData, GatewayThreadCreateDispatchData,
GatewayThreadDeleteDispatchData, GatewayThreadDeleteDispatchData,
GatewayThreadListSyncDispatchData, GatewayThreadListSyncDispatchData,
GatewayThreadMemberUpdateDispatchData,
GatewayThreadMembersUpdateDispatchData, GatewayThreadMembersUpdateDispatchData,
GatewayThreadMemberUpdateDispatchData,
GatewayTypingStartDispatchData, GatewayTypingStartDispatchData,
GatewayUserUpdateDispatchData, GatewayUserUpdateDispatchData,
GatewayVoiceChannelEffectSendDispachData, GatewayVoiceChannelEffectSendDispachData,

View File

@ -25,14 +25,14 @@ export class BaseSocket {
return new Promise<number>(res => { return new Promise<number>(res => {
const nonce = randomUUID(); const nonce = randomUUID();
const start = performance.now(); const start = performance.now();
const listener = (data: Buffer) => { const listener = ({ data }: MessageEvent) => {
if (data.toString() !== nonce) return; if (data.toString() !== nonce) return;
//@ts-expect-error //@ts-expect-error
ws.removeListener('pong', listener); ws.removeEventListener('pong', listener);
res(performance.now() - start); res(performance.now() - start);
}; };
//@ts-expect-error //@ts-expect-error
ws.on('pong', listener); ws.addEventListener('pong', listener);
//@ts-expect-error //@ts-expect-error
ws.ping(nonce); ws.ping(nonce);
}); });

View File

@ -0,0 +1,43 @@
import type { Awaitable } from '../../common';
export type WorkerHeartbeaterMessages = SendHeartbeat;
export type CreateHeartbeaterMessage<T extends string, D extends object = object> = { type: T } & D;
export type SendHeartbeat = CreateHeartbeaterMessage<'HEARTBEAT'>;
export class Heartbeater {
store = new Map<
number,
{
ack: boolean;
interval: NodeJS.Timeout;
}
>();
constructor(
public sendMethod: (workerId: number, data: WorkerHeartbeaterMessages) => Awaitable<void>,
public interval: number,
) {}
register(workerId: number, recreate: (workerId: number) => Awaitable<void>) {
if (this.interval <= 0) return;
this.store.set(workerId, {
ack: true,
interval: setInterval(() => {
const heartbeat = this.store.get(workerId)!;
if (!heartbeat.ack) {
heartbeat.ack = true;
return recreate(workerId);
}
heartbeat.ack = false;
this.sendMethod(workerId, { type: 'HEARTBEAT' });
}, this.interval),
});
}
acknowledge(workerId: number) {
const heartbeat = this.store.get(workerId);
if (!heartbeat) return;
heartbeat.ack = true;
}
}

View File

@ -1,5 +1,5 @@
import { inflateSync } from 'node:zlib'; import { inflateSync } from 'node:zlib';
import { LogLevels, Logger, type MakeRequired, MergeOptions, delay, hasIntent } from '../../common'; import { delay, hasIntent, Logger, LogLevels, type MakeRequired, MergeOptions } from '../../common';
import { import {
type APIGuildMember, type APIGuildMember,
GatewayCloseCodes, GatewayCloseCodes,
@ -45,7 +45,7 @@ export class Shard {
bucket: DynamicBucket; bucket: DynamicBucket;
offlineSendQueue: ((_?: unknown) => void)[] = []; offlineSendQueue: ((_?: unknown) => void)[] = [];
pendingGuilds = new Set<string>(); pendingGuilds?: Set<string>;
options: MakeRequired<ShardOptions, 'properties' | 'ratelimitOptions' | 'reconnectTimeout' | 'connectionTimeout'>; options: MakeRequired<ShardOptions, 'properties' | 'ratelimitOptions' | 'reconnectTimeout' | 'connectionTimeout'>;
isReady = false; isReady = false;
@ -56,10 +56,7 @@ export class Shard {
{ {
members: APIGuildMember[]; members: APIGuildMember[];
presences: GatewayGuildMembersChunkPresence[]; presences: GatewayGuildMembersChunkPresence[];
resolve: (value: { resolve: (value: { members: APIGuildMember[]; presences: GatewayGuildMembersChunkPresence[] }) => void;
members: APIGuildMember[];
presences: GatewayGuildMembersChunkPresence[];
}) => void;
reject: (reason?: any) => void; reject: (reason?: any) => void;
} }
>(); >();
@ -138,7 +135,6 @@ export class Shard {
); );
// @ts-expect-error Use native websocket when using Bun // @ts-expect-error Use native websocket when using Bun
// biome-ignore lint/correctness/noUndeclaredVariables: /\
this.websocket = new BaseSocket(typeof Bun === 'undefined' ? 'ws' : 'bun', this.currentGatewayURL); this.websocket = new BaseSocket(typeof Bun === 'undefined' ? 'ws' : 'bun', this.currentGatewayURL);
this.websocket.onmessage = ({ data }: { data: string | Buffer }) => { this.websocket.onmessage = ({ data }: { data: string | Buffer }) => {
@ -301,16 +297,14 @@ export class Shard {
clearTimeout(this.connectionTimeout); clearTimeout(this.connectionTimeout);
this.connectionTimeout = undefined; this.connectionTimeout = undefined;
if (hasIntent(this.options.intents, 'Guilds')) { if (hasIntent(this.options.intents, 'Guilds')) {
for (let i = 0; i < packet.d.guilds.length; i++) { this.pendingGuilds = new Set(packet.d.guilds.map(guild => guild.id));
this.pendingGuilds.add(packet.d.guilds.at(i)!.id);
}
} }
this.data.resume_gateway_url = packet.d.resume_gateway_url; this.data.resume_gateway_url = packet.d.resume_gateway_url;
this.data.session_id = packet.d.session_id; this.data.session_id = packet.d.session_id;
this.offlineSendQueue.map(resolve => resolve()); this.offlineSendQueue.map(resolve => resolve());
this.options.handlePayload(this.id, packet); this.options.handlePayload(this.id, packet);
if (this.pendingGuilds.size === 0) { if (this.pendingGuilds?.size === 0) {
this.isReady = true; this.isReady = true;
this.options.handlePayload(this.id, { this.options.handlePayload(this.id, {
t: GatewayDispatchEvents.GuildsReady, t: GatewayDispatchEvents.GuildsReady,
@ -322,7 +316,7 @@ export class Shard {
} }
case GatewayDispatchEvents.GuildCreate: case GatewayDispatchEvents.GuildCreate:
case GatewayDispatchEvents.GuildDelete: case GatewayDispatchEvents.GuildDelete:
if (this.pendingGuilds.delete(packet.d.id)) { if (this.pendingGuilds?.delete(packet.d.id)) {
(packet as any).t = `RAW_${packet.t}`; (packet as any).t = `RAW_${packet.t}`;
this.options.handlePayload(this.id, packet); this.options.handlePayload(this.id, packet);
if (this.pendingGuilds.size === 0) { if (this.pendingGuilds.size === 0) {
@ -376,10 +370,7 @@ export class Shard {
) { ) {
const nonce = Date.now().toString() + Math.random().toString(36); const nonce = Date.now().toString() + Math.random().toString(36);
let resolve: (value: { let resolve: (value: { members: APIGuildMember[]; presences: GatewayGuildMembersChunkPresence[] }) => void = () => {
members: APIGuildMember[];
presences: GatewayGuildMembersChunkPresence[];
}) => void = () => {
// //
}; };
let reject: (reason?: any) => void = () => { let reject: (reason?: any) => void = () => {

View File

@ -1,11 +1,11 @@
import { import {
LogLevels, calculateShardId,
Logger, Logger,
LogLevels,
lazyLoadPackage,
type MakeRequired, type MakeRequired,
MergeOptions, MergeOptions,
type WatcherSendToShard, type WatcherSendToShard,
calculateShardId,
lazyLoadPackage,
} from '../../common'; } from '../../common';
import type { DeepPartial, MakeDeepPartial } from '../../common/types/util'; import type { DeepPartial, MakeDeepPartial } from '../../common/types/util';
import { import {

View File

@ -69,6 +69,9 @@ export interface WorkerManagerOptions extends Omit<ShardManagerOptions, 'handleP
workerProxy?: boolean; workerProxy?: boolean;
/** @default 15000 */
heartbeaterInterval?: number;
path: string; path: string;
handlePayload?(shardId: number, workerId: number, packet: GatewayDispatchPayload): any; handlePayload?(shardId: number, workerId: number, packet: GatewayDispatchPayload): any;

View File

@ -1,4 +1,4 @@
import { type UUID, createHash, randomBytes, randomUUID } from 'node:crypto'; import { createHash, randomBytes, randomUUID, type UUID } from 'node:crypto';
import { request } from 'node:https'; import { request } from 'node:https';
import type { Socket } from 'node:net'; import type { Socket } from 'node:net';

View File

@ -114,7 +114,12 @@ export type CustomWorkerClientMessages = {
>; >;
}; };
export type ClientHeartbeaterMessages = ACKHeartbeat;
export type ACKHeartbeat = CreateWorkerMessage<'ACK_HEARTBEAT'>;
export type WorkerMessages = export type WorkerMessages =
| ClientHeartbeaterMessages
| { | {
[K in BaseWorkerMessage['type']]: Identify<Extract<BaseWorkerMessage, { type: K }>>; [K in BaseWorkerMessage['type']]: Identify<Extract<BaseWorkerMessage, { type: K }>>;
}[BaseWorkerMessage['type']] }[BaseWorkerMessage['type']]

View File

@ -1,14 +1,15 @@
import cluster, { type Worker as ClusterWorker } from 'node:cluster'; import cluster, { type Worker as ClusterWorker } from 'node:cluster';
import { type UUID, randomUUID } from 'node:crypto'; import { randomUUID, type UUID } from 'node:crypto';
import type { Worker as WorkerThreadsWorker } from 'node:worker_threads'; import type { Worker as WorkerThreadsWorker } from 'node:worker_threads';
import { ApiHandler, type CustomWorkerManagerEvents, Logger, type UsingClient, type WorkerClient } from '../..'; import { ApiHandler, type CustomWorkerManagerEvents, Logger, type UsingClient, type WorkerClient } from '../..';
import { type Adapter, MemoryAdapter } from '../../cache'; import { type Adapter, MemoryAdapter } from '../../cache';
import { BaseClient, type InternalRuntimeConfig } from '../../client/base'; import { BaseClient, type InternalRuntimeConfig } from '../../client/base';
import { BASE_HOST, type Identify, MergeOptions, type PickPartial, lazyLoadPackage } from '../../common'; import { BASE_HOST, type Identify, lazyLoadPackage, MergeOptions, type PickPartial } from '../../common';
import type { GatewayPresenceUpdateData, GatewaySendPayload, RESTGetAPIGatewayBotResult } from '../../types'; import type { GatewayPresenceUpdateData, GatewaySendPayload, RESTGetAPIGatewayBotResult } from '../../types';
import { WorkerManagerDefaults, properties } from '../constants'; import { properties, WorkerManagerDefaults } from '../constants';
import { DynamicBucket } from '../structures'; import { DynamicBucket } from '../structures';
import { ConnectQueue } from '../structures/timeout'; import { ConnectQueue } from '../structures/timeout';
import { Heartbeater, type WorkerHeartbeaterMessages } from './heartbeater';
import type { ShardOptions, WorkerData, WorkerManagerOptions } from './shared'; import type { ShardOptions, WorkerData, WorkerManagerOptions } from './shared';
import type { WorkerInfo, WorkerMessages, WorkerShardInfo } from './worker'; import type { WorkerInfo, WorkerMessages, WorkerShardInfo } from './worker';
@ -55,6 +56,7 @@ export class WorkerManager extends Map<
rest!: ApiHandler; rest!: ApiHandler;
reshardingWorkerQueue: (() => void)[] = []; reshardingWorkerQueue: (() => void)[] = [];
private _info?: RESTGetAPIGatewayBotResult; private _info?: RESTGetAPIGatewayBotResult;
heartbeater: Heartbeater;
constructor( constructor(
options: Omit< options: Omit<
@ -75,6 +77,8 @@ export class WorkerManager extends Map<
return oldFn(message); return oldFn(message);
}; };
} }
this.heartbeater = new Heartbeater(this.postMessage.bind(this), options.heartbeaterInterval ?? 15e3);
} }
setCache(adapter: Adapter) { setCache(adapter: Adapter) {
@ -144,12 +148,12 @@ export class WorkerManager extends Map<
return workerId; return workerId;
} }
postMessage(id: number, body: ManagerMessages) { postMessage(id: number, body: ManagerMessages | WorkerHeartbeaterMessages) {
const worker = this.get(id); const worker = this.get(id);
if (!worker) return this.debugger?.error(`Worker ${id} does not exists.`); if (!worker) return this.debugger?.error(`Worker ${id} does not exists.`);
switch (this.options.mode) { switch (this.options.mode) {
case 'clusters': case 'clusters':
(worker as ClusterWorker).send(body); if ((worker as ClusterWorker).isConnected()) (worker as ClusterWorker).send(body);
break; break;
case 'threads': case 'threads':
(worker as import('worker_threads').Worker).postMessage(body); (worker as import('worker_threads').Worker).postMessage(body);
@ -160,33 +164,40 @@ export class WorkerManager extends Map<
} }
} }
prepareWorkers(shards: number[][], resharding = false) { prepareWorkers(shards: number[][], rawResharding = false) {
const worker_threads = lazyLoadPackage<typeof import('node:worker_threads')>('node:worker_threads'); const worker_threads = lazyLoadPackage<typeof import('node:worker_threads')>('node:worker_threads');
if (!worker_threads) throw new Error('Cannot prepare workers without worker_threads.'); if (!worker_threads) throw new Error('Cannot prepare workers without worker_threads.');
for (let i = 0; i < shards.length; i++) { for (let i = 0; i < shards.length; i++) {
const registerWorker = (resharding: boolean) => {
const worker = this.createWorker({
path: this.options.path,
debug: this.options.debug,
token: this.options.token,
shards: shards[i],
intents: this.options.intents,
workerId: i,
workerProxy: this.options.workerProxy,
totalShards: resharding ? this._info!.shards : this.totalShards,
mode: this.options.mode,
resharding,
totalWorkers: shards.length,
info: {
...this.options.info,
shards: this.totalShards,
},
compress: this.options.compress,
});
this.set(i, worker);
};
const workerExists = this.has(i); const workerExists = this.has(i);
if (resharding || !workerExists) { if (rawResharding || !workerExists) {
this[resharding ? 'reshardingWorkerQueue' : 'workerQueue'].push(() => { this[rawResharding ? 'reshardingWorkerQueue' : 'workerQueue'].push(() => {
const worker = this.createWorker({ registerWorker(rawResharding);
path: this.options.path, this.heartbeater.register(i, () => {
debug: this.options.debug, this.delete(i);
token: this.options.token, registerWorker(false);
shards: shards[i],
intents: this.options.intents,
workerId: i,
workerProxy: this.options.workerProxy,
totalShards: resharding ? this._info!.shards : this.totalShards,
mode: this.options.mode,
resharding,
totalWorkers: shards.length,
info: {
...this.options.info,
shards: this.totalShards,
},
compress: this.options.compress,
}); });
this.set(i, worker);
}); });
} }
} }
@ -218,6 +229,9 @@ export class WorkerManager extends Map<
env, env,
}); });
worker.on('message', data => this.handleWorkerMessage(data)); worker.on('message', data => this.handleWorkerMessage(data));
worker.on('error', err => {
this.debugger?.error(`[Worker #${workerData.workerId}]`, err);
});
return worker; return worker;
} }
case 'clusters': { case 'clusters': {
@ -254,6 +268,9 @@ export class WorkerManager extends Map<
async handleWorkerMessage(message: WorkerMessages) { async handleWorkerMessage(message: WorkerMessages) {
switch (message.type) { switch (message.type) {
case 'ACK_HEARTBEAT':
this.heartbeater.acknowledge(message.workerId);
break;
case 'WORKER_READY_RESHARDING': case 'WORKER_READY_RESHARDING':
{ {
this.get(message.workerId)!.resharded = true; this.get(message.workerId)!.resharded = true;
@ -288,6 +305,7 @@ export class WorkerManager extends Map<
for (const [id] of this.entries()) { for (const [id] of this.entries()) {
this.postMessage(id, { this.postMessage(id, {
type: 'CONNECT_ALL_SHARDS_RESHARDING', type: 'CONNECT_ALL_SHARDS_RESHARDING',
totalShards: this.options.totalShards,
} satisfies ConnnectAllShardsResharding); } satisfies ConnnectAllShardsResharding);
} }
this.forEach(w => { this.forEach(w => {
@ -656,7 +674,12 @@ export type ManagerSpawnShardsResharding = CreateManagerMessage<
Pick<ShardOptions, 'info' | 'properties' | 'compress'> Pick<ShardOptions, 'info' | 'properties' | 'compress'>
>; >;
export type DisconnectAllShardsResharding = CreateManagerMessage<'DISCONNECT_ALL_SHARDS_RESHARDING'>; export type DisconnectAllShardsResharding = CreateManagerMessage<'DISCONNECT_ALL_SHARDS_RESHARDING'>;
export type ConnnectAllShardsResharding = CreateManagerMessage<'CONNECT_ALL_SHARDS_RESHARDING'>; export type ConnnectAllShardsResharding = CreateManagerMessage<
'CONNECT_ALL_SHARDS_RESHARDING',
{
totalShards: number;
}
>;
export type ManagerSendPayload = CreateManagerMessage< export type ManagerSendPayload = CreateManagerMessage<
'SEND_PAYLOAD', 'SEND_PAYLOAD',
GatewaySendPayload & { shardId: number; nonce: string } GatewaySendPayload & { shardId: number; nonce: string }

View File

@ -1,3 +1,3 @@
export * from './SharedTypes';
export * from './constants'; export * from './constants';
export * from './discord'; export * from './discord';
export * from './SharedTypes';

Some files were not shown because too many files have changed in this diff Show More