From 67970faf7b768aac132db60b5f0f4e082ba31c34 Mon Sep 17 00:00:00 2001 From: Yuzu Date: Tue, 21 Jun 2022 19:59:41 -0500 Subject: [PATCH] fix: events --- README.md | 1 + deno.json | 8 + mod.ts | 2 +- session/Events.ts | 13 +- session/Session.ts | 189 +- session/mod.ts | 2 +- tests/deps.ts | 2 +- tests/mod.ts | 16 +- util/EventEmmiter.ts | 101 +- util/Routes.ts | 4 +- util/Snowflake.ts | 6 +- util/mod.ts | 2 +- vendor/gateway/README.md | 2 +- vendor/gateway/calculateShardId.ts | 4 +- .../gateway/manager/calculateTotalShards.ts | 20 +- vendor/gateway/manager/calculateWorkerId.ts | 16 +- vendor/gateway/manager/gatewayManager.ts | 474 +-- vendor/gateway/manager/prepareBuckets.ts | 76 +- vendor/gateway/manager/resharder.ts | 292 +- vendor/gateway/manager/shardManager.ts | 178 +- vendor/gateway/manager/spawnShards.ts | 38 +- vendor/gateway/manager/stop.ts | 4 +- .../gateway/manager/tellWorkerToIdentify.ts | 10 +- vendor/gateway/shard/calculateSafeRequests.ts | 8 +- vendor/gateway/shard/close.ts | 4 +- vendor/gateway/shard/connect.ts | 48 +- vendor/gateway/shard/createShard.ts | 528 +-- vendor/gateway/shard/handleClose.ts | 108 +- vendor/gateway/shard/handleMessage.ts | 264 +- vendor/gateway/shard/identify.ts | 78 +- vendor/gateway/shard/isOpen.ts | 2 +- vendor/gateway/shard/resume.ts | 73 +- vendor/gateway/shard/send.ts | 34 +- vendor/gateway/shard/shutdown.ts | 4 +- vendor/gateway/shard/startHeartbeating.ts | 94 +- vendor/gateway/shard/stopHeartbeating.ts | 10 +- vendor/gateway/shard/types.ts | 226 +- vendor/rest/checkRateLimits.ts | 20 +- vendor/rest/cleanupQueues.ts | 16 +- vendor/rest/convertRestError.ts | 4 +- vendor/rest/createRequestBody.ts | 96 +- vendor/rest/processGlobalQueue.ts | 142 +- vendor/rest/processQueue.ts | 88 +- vendor/rest/processRateLimitedPaths.ts | 42 +- vendor/rest/processRequest.ts | 54 +- vendor/rest/processRequestHeaders.ts | 96 +- vendor/rest/rest.ts | 30 +- vendor/rest/restManager.ts | 152 +- vendor/rest/runMethod.ts | 132 +- vendor/rest/runProxyMethod.ts | 72 +- vendor/rest/sendRequest.ts | 274 +- vendor/rest/simplifyUrl.ts | 28 +- vendor/types/discord.ts | 3754 ++++++++--------- vendor/types/shared.ts | 2095 ++++----- vendor/util/bucket.ts | 244 +- vendor/util/collection.ts | 168 +- vendor/util/constants.ts | 4 +- vendor/util/delay.ts | 10 +- vendor/util/token.ts | 14 +- 59 files changed, 5248 insertions(+), 5228 deletions(-) create mode 100644 deno.json diff --git a/README.md b/README.md index 157d546..d6ab012 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # biscuit + A brand new bleeding edge non bloated Discord library diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..d010978 --- /dev/null +++ b/deno.json @@ -0,0 +1,8 @@ +{ + "fmt": { + "options": { + "indentWidth": 4, + "lineWidth": 120 + } + } +} diff --git a/mod.ts b/mod.ts index 6f38a80..d0e61e4 100644 --- a/mod.ts +++ b/mod.ts @@ -2,4 +2,4 @@ export * from "./session/mod.ts"; export * from "./util/mod.ts"; export * from "./structures/mod.ts"; export * from "./vendor/external.ts"; -export * from "./handlers/mod.ts"; \ No newline at end of file +export * from "./handlers/mod.ts"; diff --git a/session/Events.ts b/session/Events.ts index 44d65f3..e0a7ff9 100644 --- a/session/Events.ts +++ b/session/Events.ts @@ -1,6 +1,9 @@ -import type { - DiscordGatewayPayload, - Shard, -} from "../vendor/external.ts"; +import type { DiscordGatewayPayload, DiscordMessage, DiscordReady, Shard } from "../vendor/external.ts"; -export type DiscordRawEventHandler = (shard: Shard, data: DiscordGatewayPayload) => unknown; \ No newline at end of file +export type DiscordRawEventHandler = (shard: Shard, data: DiscordGatewayPayload) => unknown; + +export interface Events { + ready(payload: DiscordReady, shardId: number): unknown; + messageCreate(message: DiscordMessage): unknown; + raw(data: DiscordGatewayPayload, shardId: number): unknown; +} diff --git a/session/Session.ts b/session/Session.ts index eb0561e..932d430 100644 --- a/session/Session.ts +++ b/session/Session.ts @@ -1,129 +1,128 @@ import type { - GatewayIntents, - DiscordGatewayPayload, - DiscordGetGatewayBot, - DiscordReady, - DiscordMessage, - GatewayDispatchEventNames, - GatewayBot, - Shard + DiscordGatewayPayload, + DiscordGetGatewayBot, + DiscordMessage, + DiscordReady, + GatewayBot, + GatewayDispatchEventNames, + GatewayIntents, + Shard, } from "../vendor/external.ts"; -import { - EventEmitter, - Snowflake, - Routes -} from "../util/mod.ts"; +import { EventEmitter, Routes, Snowflake } from "../util/mod.ts"; -import type { - DiscordRawEventHandler, -} from "./Events.ts"; +import type { DiscordRawEventHandler, Events } from "./Events.ts"; -import { - createRestManager, - createGatewayManager -} from "../vendor/external.ts"; +import { createGatewayManager, createRestManager } from "../vendor/external.ts"; export interface RestOptions { - secretKey?: string; - applicationId?: Snowflake; + secretKey?: string; + applicationId?: Snowflake; } export interface GatewayOptions { - botId?: Snowflake; - data?: GatewayBot; + botId?: Snowflake; + data?: GatewayBot; } export interface SessionOptions { - token: string; - rawHandler?: DiscordRawEventHandler; - intents?: GatewayIntents; - rest?: RestOptions; - gateway?: GatewayOptions; + token: string; + rawHandler?: DiscordRawEventHandler; + intents?: GatewayIntents; + rest?: RestOptions; + gateway?: GatewayOptions; } /** * Receives a Token, connects - * */ + */ export class Session extends EventEmitter { - options: SessionOptions; + options: SessionOptions; - // TODO: improve this with CreateShardManager etc - rest: ReturnType; - gateway: ReturnType; + // TODO: improve this with CreateShardManager etc + rest?: ReturnType; + gateway?: ReturnType; - constructor(options: SessionOptions) { - super(); - this.options = options; + constructor(options: SessionOptions) { + super(); + this.options = options; + // TODO: set botId in Session.botId or something + } - const defHandler: DiscordRawEventHandler = (shard, data) => { - this.emit("raw", data, shard.id); + /** TODO: move this */ + static #toSnakeCase(str: string) { + // probably not a fast implementation + return str.replace(/[A-Z]/g, (char) => "_" + char.toLowerCase()); + } - if (!data.t) return; + override on(event: "ready", func: Events["ready"]): this; + override on(event: "messageCreate", func: Events["messageCreate"]): this; + override on(event: "raw", func: Events["raw"]): this; + override on(event: keyof Events, func: Events[keyof Events]): this { + return super.on(event, func); + } - this.emit(data.t as GatewayDispatchEventNames, data, shard.id); - }; + override off(event: "ready", func: Events["ready"]): this; + override off(event: "messageCreate", func: Events["messageCreate"]): this; + override off(event: "raw", func: Events["raw"]): this; + override off(event: keyof Events, func: Events[keyof Events]): this { + return super.off(event, func); + } - this.rest = createRestManager({ - token: this.options.token, - debug: (text) => { - // TODO: set this using the event emitter - super.rawListeners("debug")?.forEach((fn) => fn(text)); - }, - secretKey: this.options.rest?.secretKey ?? undefined - }); + override once(event: "ready", func: Events["ready"]): this; + override once(event: "messageCreate", func: Events["messageCreate"]): this; + override once(event: "raw", func: Events["raw"]): this; + override once(event: keyof Events, func: Events[keyof Events]): this { + return super.once(event, func); + } - this.gateway = createGatewayManager({ - gatewayBot: options.gateway?.data ?? {} as GatewayBot, // TODO - gatewayConfig: { - token: options.token, - intents: options.intents - }, - handleDiscordPayload: options.rawHandler ?? defHandler - }); + async start() { + const defHandler: DiscordRawEventHandler = (shard, data) => { + this.emit("raw", data, shard.id); - // TODO: set botId in Session.botId or something - } + if (!data.t) return; - override on(event: "ready", func: (payload: DiscordReady) => unknown): this; - override on(event: "raw", func: (shard: Shard, data: DiscordGatewayPayload) => unknown): this; - override on(event: "message", func: (message: DiscordMessage) => unknown): this; - override on(event: "debug", func: (text: string) => unknown): this; - override on(event: string, func: Function): this { - return super.on(event, func); - } + this.emit(data.t as GatewayDispatchEventNames, data, shard.id); + }; - override off(event: string, func: Function): this { - return super.off(event, func); - } + this.rest = createRestManager({ + token: this.options.token, + debug: (text) => { + // TODO: set this using the event emitter + super.rawListeners("debug")?.forEach((fn) => fn(text)); + }, + secretKey: this.options.rest?.secretKey ?? undefined, + }); - override once(event: string, func: Function): this { - return super.once(event, func); - } + this.gateway = createGatewayManager({ + gatewayBot: this.options.gateway?.data ?? {} as GatewayBot, // TODO + gatewayConfig: { + token: this.options.token, + intents: this.options.intents, + }, + handleDiscordPayload: this.options.rawHandler ?? defHandler, + }); - async start() { - const getGatewayBot = () => this.rest.runMethod(this.rest, "GET", Routes.GATEWAY_BOT()); + const getGatewayBot = () => this.rest!.runMethod(this.rest!, "GET", Routes.GATEWAY_BOT()); - // check if is empty - if (!Object.keys(this.options.gateway?.data ?? {}).length) { - const nonParsed = await getGatewayBot(); + // check if is empty + if (!Object.keys(this.options.gateway?.data ?? {}).length) { + const nonParsed = await getGatewayBot(); - this.gateway.gatewayBot = { - url: nonParsed.url, - shards: nonParsed.shards, - sessionStartLimit: { - total: nonParsed.session_start_limit.total, - remaining: nonParsed.session_start_limit.remaining, - resetAfter: nonParsed.session_start_limit.reset_after, - maxConcurrency: nonParsed.session_start_limit.max_concurrency, - }, - }; - this.gateway.lastShardId = this.gateway.gatewayBot.shards - 1; - this.gateway.manager.totalShards = this.gateway.gatewayBot.shards; - } + this.gateway.gatewayBot = { + url: nonParsed.url, + shards: nonParsed.shards, + sessionStartLimit: { + total: nonParsed.session_start_limit.total, + remaining: nonParsed.session_start_limit.remaining, + resetAfter: nonParsed.session_start_limit.reset_after, + maxConcurrency: nonParsed.session_start_limit.max_concurrency, + }, + }; + this.gateway.lastShardId = this.gateway.gatewayBot.shards - 1; + this.gateway.manager.totalShards = this.gateway.gatewayBot.shards; + } - this.gateway.spawnShards(); - } + this.gateway.spawnShards(); + } } - - diff --git a/session/mod.ts b/session/mod.ts index 5162dca..13b35eb 100644 --- a/session/mod.ts +++ b/session/mod.ts @@ -1,2 +1,2 @@ export * from "./Session.ts"; -export * from "./Events.ts"; \ No newline at end of file +export * from "./Events.ts"; diff --git a/tests/deps.ts b/tests/deps.ts index 77680a3..4dda9d8 100644 --- a/tests/deps.ts +++ b/tests/deps.ts @@ -1 +1 @@ -export * from "../mod.ts"; \ No newline at end of file +export * from "../mod.ts"; diff --git a/tests/mod.ts b/tests/mod.ts index 5845f44..bdfb4a9 100644 --- a/tests/mod.ts +++ b/tests/mod.ts @@ -1,11 +1,19 @@ import * as Discord from "./deps.ts"; +if (!Deno.args[0]) { + throw new Error("Please provide a token"); +} + const session = new Discord.Session({ - token: Deno.args[0], + token: Deno.args[0], + intents: Discord.GatewayIntents.MessageContent | Discord.GatewayIntents.Guilds | + Discord.GatewayIntents.GuildMessages, }); session.on("ready", (payload) => console.log(payload)); -session.on("raw", (shard, data) => console.log(shard, data)); -session.on("debug", (text) => console.log(text)); +session.on("message", (payload) => console.log(payload)); +// session.on("raw", (data, shardId) => console.log(shardId, data)); -session.start(); \ No newline at end of file +console.log("hello"); + +session.start(); diff --git a/util/EventEmmiter.ts b/util/EventEmmiter.ts index 350b60a..e586164 100644 --- a/util/EventEmmiter.ts +++ b/util/EventEmmiter.ts @@ -1,77 +1,74 @@ // deno-lint-ignore-file ban-types - /** * An event emitter (observer pattern) - * */ + */ export class EventEmitter { - listeners = new Map; + listeners = new Map(); - #addListener(event: string, func: Function) { - this.listeners.set(event, this.listeners.get(event) || []); - this.listeners.get(event)?.push(func); - return this; - } + #addListener(event: string, func: Function) { + this.listeners.set(event, this.listeners.get(event) || []); + this.listeners.get(event)?.push(func); + return this; + } - on(event: string, func: Function) { - return this.#addListener(event, func); - } + on(event: string, func: Function) { + return this.#addListener(event, func); + } - #removeListener(event: string, func: Function) { - if (this.listeners.has(event)) { - const listener = this.listeners.get(event); + #removeListener(event: string, func: Function) { + if (this.listeners.has(event)) { + const listener = this.listeners.get(event); - if (listener?.includes(func)) { - listener.splice(listener.indexOf(func), 1); + if (listener?.includes(func)) { + listener.splice(listener.indexOf(func), 1); - if (listener.length === 0) { - this.listeners.delete(event); + if (listener.length === 0) { + this.listeners.delete(event); + } } - } - } + } - return this; - } + return this; + } - off(event: string, func: Function) { - return this.#removeListener(event, func); - } + off(event: string, func: Function) { + return this.#removeListener(event, func); + } - once(event: string, func: Function) { - // it is important for this to be an arrow function - const closure = () => { - func(); - this.off(event, func); - } + once(event: string, func: Function) { + // it is important for this to be an arrow function + const closure = () => { + func(); + this.off(event, func); + }; - const listener = this.listeners.get(event) ?? []; + const listener = this.listeners.get(event) ?? []; - listener.push(closure); + listener.push(closure); - return this; - } + return this; + } - emit(event: string, ...args: unknown[]) { - const listener = this.listeners.get(event); + emit(event: string, ...args: unknown[]) { + const listener = this.listeners.get(event); - if (!listener) { - return false; - } + if (!listener) { + return false; + } - listener.forEach((f) => f(...args)); + listener.forEach((f) => f(...args)); - return true; - } + return true; + } - listenerCount(eventName: string) { - return this.listeners.get(eventName)?.length ?? 0; - } - - rawListeners(eventName: string): Function[] | undefined { - return this.listeners.get(eventName); - } + listenerCount(eventName: string) { + return this.listeners.get(eventName)?.length ?? 0; + } + rawListeners(eventName: string): Function[] | undefined { + return this.listeners.get(eventName); + } } - -export default EventEmitter; \ No newline at end of file +export default EventEmitter; diff --git a/util/Routes.ts b/util/Routes.ts index 29506c4..0a13468 100644 --- a/util/Routes.ts +++ b/util/Routes.ts @@ -1,3 +1,3 @@ export function GATEWAY_BOT() { - return "/gateway/bot"; -} \ No newline at end of file + return "/gateway/bot"; +} diff --git a/util/Snowflake.ts b/util/Snowflake.ts index 0b0ee61..6509f3f 100644 --- a/util/Snowflake.ts +++ b/util/Snowflake.ts @@ -5,7 +5,7 @@ export const DiscordEpoch = 14200704e5; // utilities for Snowflakes export const Snowflake = { - snowflakeToTimestamp(id: Snowflake) { + snowflakeToTimestamp(id: Snowflake) { return (Number(id) >> 22) + DiscordEpoch; - } -} \ No newline at end of file + }, +}; diff --git a/util/mod.ts b/util/mod.ts index 9300663..1cfabbb 100644 --- a/util/mod.ts +++ b/util/mod.ts @@ -1,3 +1,3 @@ export * from "./EventEmmiter.ts"; export * from "./Snowflake.ts"; -export * as Routes from "./Routes.ts"; \ No newline at end of file +export * as Routes from "./Routes.ts"; diff --git a/vendor/gateway/README.md b/vendor/gateway/README.md index d608547..80ed44a 100644 --- a/vendor/gateway/README.md +++ b/vendor/gateway/README.md @@ -52,7 +52,7 @@ This WS service is meant for ADVANCED DEVELOPERS ONLY! ```ts createGatewayManager({ - // TODO: (docs) Fill this out + // TODO: (docs) Fill this out }); ``` diff --git a/vendor/gateway/calculateShardId.ts b/vendor/gateway/calculateShardId.ts index d07c1a4..a3db346 100644 --- a/vendor/gateway/calculateShardId.ts +++ b/vendor/gateway/calculateShardId.ts @@ -1,7 +1,7 @@ import { GatewayManager } from "./manager/gatewayManager.ts"; export function calculateShardId(gateway: GatewayManager, guildId: bigint) { - if (gateway.manager.totalShards === 1) return 0; + if (gateway.manager.totalShards === 1) return 0; - return Number((guildId >> 22n) % BigInt(gateway.manager.totalShards - 1)); + return Number((guildId >> 22n) % BigInt(gateway.manager.totalShards - 1)); } diff --git a/vendor/gateway/manager/calculateTotalShards.ts b/vendor/gateway/manager/calculateTotalShards.ts index 44817f5..afac127 100644 --- a/vendor/gateway/manager/calculateTotalShards.ts +++ b/vendor/gateway/manager/calculateTotalShards.ts @@ -2,15 +2,15 @@ import { GatewayManager } from "./gatewayManager.ts"; /** Handler used to determine max number of shards to use based upon the max concurrency. */ export function calculateTotalShards(gateway: GatewayManager): number { - // Bots under 100k servers do not have access to total shards. - if (gateway.manager.totalShards < 100) return gateway.manager.totalShards; + // Bots under 100k servers do not have access to total shards. + if (gateway.manager.totalShards < 100) return gateway.manager.totalShards; - // Calculate a multiple of `maxConcurrency` which can be used to connect to the gateway. - return Math.ceil( - gateway.manager.totalShards / - // If `maxConcurrency` is 1 we can safely use 16. - (gateway.gatewayBot.sessionStartLimit.maxConcurrency === 1 - ? 16 - : gateway.gatewayBot.sessionStartLimit.maxConcurrency), - ) * gateway.gatewayBot.sessionStartLimit.maxConcurrency; + // Calculate a multiple of `maxConcurrency` which can be used to connect to the gateway. + return Math.ceil( + gateway.manager.totalShards / + // If `maxConcurrency` is 1 we can safely use 16. + (gateway.gatewayBot.sessionStartLimit.maxConcurrency === 1 + ? 16 + : gateway.gatewayBot.sessionStartLimit.maxConcurrency), + ) * gateway.gatewayBot.sessionStartLimit.maxConcurrency; } diff --git a/vendor/gateway/manager/calculateWorkerId.ts b/vendor/gateway/manager/calculateWorkerId.ts index cd2ac2e..0e76fc3 100644 --- a/vendor/gateway/manager/calculateWorkerId.ts +++ b/vendor/gateway/manager/calculateWorkerId.ts @@ -1,13 +1,13 @@ import { GatewayManager } from "./gatewayManager.ts"; export function calculateWorkerId(manager: GatewayManager, shardId: number) { - // Ignore decimal numbers. - let workerId = Math.floor((shardId) / manager.shardsPerWorker); - // If the workerId overflows the maximal allowed workers we by default just use to last worker. - if (workerId >= manager.totalWorkers) { - // The Id of the last available worker is total -1 - workerId = manager.totalWorkers - 1; - } + // Ignore decimal numbers. + let workerId = Math.floor((shardId) / manager.shardsPerWorker); + // If the workerId overflows the maximal allowed workers we by default just use to last worker. + if (workerId >= manager.totalWorkers) { + // The Id of the last available worker is total -1 + workerId = manager.totalWorkers - 1; + } - return workerId; + return workerId; } diff --git a/vendor/gateway/manager/gatewayManager.ts b/vendor/gateway/manager/gatewayManager.ts index bcaa8a3..98a1eb7 100644 --- a/vendor/gateway/manager/gatewayManager.ts +++ b/vendor/gateway/manager/gatewayManager.ts @@ -27,268 +27,268 @@ export type GatewayManager = ReturnType; * bots. */ export function createGatewayManager( - options: PickPartial, + options: PickPartial, ) { - const prepareBucketsOverwritten = options.prepareBuckets ?? prepareBuckets; - const spawnShardsOverwritten = options.spawnShards ?? spawnShards; - const stopOverwritten = options.stop ?? stop; - const tellWorkerToIdentifyOverwritten = options.tellWorkerToIdentify ?? tellWorkerToIdentify; - const calculateTotalShardsOverwritten = options.calculateTotalShards ?? calculateTotalShards; - const calculateWorkerIdOverwritten = options.calculateWorkerId ?? calculateWorkerId; + const prepareBucketsOverwritten = options.prepareBuckets ?? prepareBuckets; + const spawnShardsOverwritten = options.spawnShards ?? spawnShards; + const stopOverwritten = options.stop ?? stop; + const tellWorkerToIdentifyOverwritten = options.tellWorkerToIdentify ?? tellWorkerToIdentify; + const calculateTotalShardsOverwritten = options.calculateTotalShards ?? calculateTotalShards; + const calculateWorkerIdOverwritten = options.calculateWorkerId ?? calculateWorkerId; - const totalShards = options.totalShards ?? options.gatewayBot.shards ?? 1; + const totalShards = options.totalShards ?? options.gatewayBot.shards ?? 1; - const gatewayManager = { - // ---------- - // PROPERTIES - // ---------- + const gatewayManager = { + // ---------- + // PROPERTIES + // ---------- - /** The max concurrency buckets. - * Those will be created when the `spawnShards` (which calls `prepareBuckets` under the hood) function gets called. - */ - buckets: new Map< - number, - { - workers: { id: number; queue: number[] }[]; - leak: LeakyBucket; - } - >(), + /** The max concurrency buckets. + * Those will be created when the `spawnShards` (which calls `prepareBuckets` under the hood) function gets called. + */ + buckets: new Map< + number, + { + workers: { id: number; queue: number[] }[]; + leak: LeakyBucket; + } + >(), + /** Id of the first Shard which should get controlled by this manager. + * + * NOTE: This is intended for testing purposes + * if big bots want to test the gateway on smaller scale. + * This is not recommended to be used in production. + */ + firstShardId: options.firstShardId ?? 0, + /** Important data which is used by the manager to connect shards to the gateway. */ + gatewayBot: options.gatewayBot, + /** Id of the last Shard which should get controlled by this manager. + * + * NOTE: This is intended for testing purposes + * if big bots want to test the gateway on smaller scale. + * This is not recommended to be used in production. + */ + lastShardId: options.lastShardId ?? totalShards - 1 ?? 1, + /** This is where the Shards get stored. + * This will not be used when having a custom workers solution. + */ + manager: {} as ShardManager, + /** Delay in milliseconds to wait before spawning next shard. + * OPTIMAL IS ABOVE 5100. YOU DON'T WANT TO HIT THE RATE LIMIT!!! + */ + spawnShardDelay: options.spawnShardDelay ?? 5300, + /** How many Shards should get assigned to a Worker. + * + * IMPORTANT: Discordeno will NOT spawn Workers for you. + * Instead you have to overwrite the `tellWorkerToIdentify` function to make that for you. + * Look at the [BigBot template gateway solution](https://github.com/discordeno/discordeno/tree/main/template/bigbot/src/gateway) for reference. + * + * NOTE: The last Worker will IGNORE this value, + * which means that the last worker can get assigned an unlimited amount of shards. + * This is not a bug but intended behavior and means you have to assign more workers to this manager. + */ + shardsPerWorker: options.shardsPerWorker ?? 25, + /** The total amount of Workers which get controlled by this manager. + * + * IMPORTANT: Discordeno will NOT spawn Workers for you. + * Instead you have to overwrite the `tellWorkerToIdentify` function to make that for you. + * Look at the [BigBot template gateway solution](https://github.com/discordeno/discordeno/tree/main/template/bigbot/src/gateway) for reference. + */ + totalWorkers: options.totalWorkers ?? 4, + + // ---------- + // PROPERTIES + // ---------- + /** Prepares the buckets for identifying. + * + * NOTE: Most of the time this function does not need to be called, + * since it gets called by the `spawnShards` function indirectly. + */ + prepareBuckets: function () { + return prepareBucketsOverwritten(this); + }, + /** This function starts to spawn the Shards assigned to this manager. + * + * The managers `buckets` will be created and + * + * if `resharding.useOptimalLargeBotSharding` is set to true, + * `totalShards` gets double checked and adjusted accordingly if wrong. + */ + spawnShards: function () { + return spawnShardsOverwritten(this); + }, + /** Stop the gateway. This closes all shards. */ + stop: function (code: number, reason: string) { + return stopOverwritten(this, code, reason); + }, + /** Tell the Worker with this Id to identify this Shard. + * + * Useful if a custom Worker solution should be used. + * + * IMPORTANT: Discordeno will NOT spawn Workers for you. + * Instead you have to overwrite the `tellWorkerToIdentify` function to make that for you. + * Look at the [BigBot template gateway solution](https://github.com/discordeno/discordeno/tree/main/template/bigbot/src/gateway) for reference. + */ + tellWorkerToIdentify: function (workerId: number, shardId: number, bucketId: number) { + return tellWorkerToIdentifyOverwritten(this, workerId, shardId, bucketId); + }, + // TODO: fix debug + /** Handle the different logs. Used for debugging. */ + debug: options.debug || function () {}, + + // /** The methods related to resharding. */ + // resharding: { + // /** Whether the resharder should automatically switch to LARGE BOT SHARDING when the bot is above 100K servers. */ + // useOptimalLargeBotSharding: options.resharding?.useOptimalLargeBotSharding ?? true, + // /** Whether or not to automatically reshard. + // * + // * @default true + // */ + // reshard: options.resharding?.reshard ?? true, + // /** The percentage at which resharding should occur. + // * + // * @default 80 + // */ + // reshardPercentage: options.resharding?.reshardPercentage ?? 80, + // /** Handles resharding the bot when necessary. */ + // resharder: options.resharding?.resharder ?? resharder, + // /** Handles checking if all new shards are online in the new gateway. */ + // isPending: options.resharding?.isPending ?? resharderIsPending, + // /** Handles closing all shards in the old gateway. */ + // closeOldShards: options.resharding?.closeOldShards ?? resharderCloseOldShards, + // /** Handles checking if it is time to reshard and triggers the resharder. */ + // check: options.resharding?.check ?? startReshardingChecks, + // /** Handler to mark a guild id with its new shard id in cache. */ + // markNewGuildShardId: options.resharding?.markNewGuildShardId ?? markNewGuildShardId, + // /** Handler to update all guilds in cache with the new shard id. */ + // editGuildShardIds: options.resharding?.editGuildShardIds ?? reshardingEditGuildShardIds, + // }, + + /** Calculate the amount of Shards which should be used based on the bot's max concurrency. */ + calculateTotalShards: function () { + return calculateTotalShardsOverwritten(this); + }, + + /** Calculate the Id of the Worker related to this Shard. */ + calculateWorkerId: function (shardId: number) { + return calculateWorkerIdOverwritten(this, shardId); + }, + }; + + gatewayManager.manager = createShardManager({ + createShardOptions: options.createShardOptions, + gatewayConfig: options.gatewayConfig, + shardIds: [], + totalShards, + + handleMessage: function (shard, message) { + return options.handleDiscordPayload(shard, message); + }, + + requestIdentify: async (shardId) => { + // TODO: improve + await gatewayManager.buckets.get(shardId % gatewayManager.gatewayBot.sessionStartLimit.maxConcurrency)!.leak + .acquire(1); + }, + }); + + return gatewayManager; +} + +export interface CreateGatewayManager { + /** Delay in milliseconds to wait before spawning next shard. OPTIMAL IS ABOVE 5100. YOU DON'T WANT TO HIT THE RATE LIMIT!!! */ + spawnShardDelay: number; + /** Total amount of shards your bot uses. Useful for zero-downtime updates or resharding. */ + totalShards: number; + /** The amount of shards to load per worker. */ + shardsPerWorker: number; + /** The total amount of workers to use for your bot. */ + totalWorkers: number; /** Id of the first Shard which should get controlled by this manager. * * NOTE: This is intended for testing purposes * if big bots want to test the gateway on smaller scale. * This is not recommended to be used in production. */ - firstShardId: options.firstShardId ?? 0, - /** Important data which is used by the manager to connect shards to the gateway. */ - gatewayBot: options.gatewayBot, + firstShardId: number; /** Id of the last Shard which should get controlled by this manager. * * NOTE: This is intended for testing purposes * if big bots want to test the gateway on smaller scale. * This is not recommended to be used in production. */ - lastShardId: options.lastShardId ?? totalShards - 1 ?? 1, - /** This is where the Shards get stored. - * This will not be used when having a custom workers solution. - */ - manager: {} as ShardManager, - /** Delay in milliseconds to wait before spawning next shard. - * OPTIMAL IS ABOVE 5100. YOU DON'T WANT TO HIT THE RATE LIMIT!!! - */ - spawnShardDelay: options.spawnShardDelay ?? 5300, - /** How many Shards should get assigned to a Worker. - * - * IMPORTANT: Discordeno will NOT spawn Workers for you. - * Instead you have to overwrite the `tellWorkerToIdentify` function to make that for you. - * Look at the [BigBot template gateway solution](https://github.com/discordeno/discordeno/tree/main/template/bigbot/src/gateway) for reference. - * - * NOTE: The last Worker will IGNORE this value, - * which means that the last worker can get assigned an unlimited amount of shards. - * This is not a bug but intended behavior and means you have to assign more workers to this manager. - */ - shardsPerWorker: options.shardsPerWorker ?? 25, - /** The total amount of Workers which get controlled by this manager. - * - * IMPORTANT: Discordeno will NOT spawn Workers for you. - * Instead you have to overwrite the `tellWorkerToIdentify` function to make that for you. - * Look at the [BigBot template gateway solution](https://github.com/discordeno/discordeno/tree/main/template/bigbot/src/gateway) for reference. - */ - totalWorkers: options.totalWorkers ?? 4, + lastShardId: number; - // ---------- - // PROPERTIES - // ---------- - /** Prepares the buckets for identifying. - * - * NOTE: Most of the time this function does not need to be called, - * since it gets called by the `spawnShards` function indirectly. - */ - prepareBuckets: function () { - return prepareBucketsOverwritten(this); - }, - /** This function starts to spawn the Shards assigned to this manager. - * - * The managers `buckets` will be created and - * - * if `resharding.useOptimalLargeBotSharding` is set to true, - * `totalShards` gets double checked and adjusted accordingly if wrong. - */ - spawnShards: function () { - return spawnShardsOverwritten(this); - }, - /** Stop the gateway. This closes all shards. */ - stop: function (code: number, reason: string) { - return stopOverwritten(this, code, reason); - }, - /** Tell the Worker with this Id to identify this Shard. - * - * Useful if a custom Worker solution should be used. - * - * IMPORTANT: Discordeno will NOT spawn Workers for you. - * Instead you have to overwrite the `tellWorkerToIdentify` function to make that for you. - * Look at the [BigBot template gateway solution](https://github.com/discordeno/discordeno/tree/main/template/bigbot/src/gateway) for reference. - */ - tellWorkerToIdentify: function (workerId: number, shardId: number, bucketId: number) { - return tellWorkerToIdentifyOverwritten(this, workerId, shardId, bucketId); - }, - // TODO: fix debug + /** Important data which is used by the manager to connect shards to the gateway. */ + gatewayBot: GatewayBot; + + gatewayConfig: PickPartial; + + /** Options which are used to create a new shard. */ + createShardOptions?: Omit; + + /** Stored as bucketId: { workers: [workerId, [ShardIds]], createNextShard: boolean } */ + buckets: Map< + number, + { + workers: { id: number; queue: number[] }[]; + leak: LeakyBucket; + } + >; + // METHODS + + /** Prepares the buckets for identifying */ + prepareBuckets: typeof prepareBuckets; + /** The handler for spawning ALL the shards. */ + spawnShards: typeof spawnShards; + /** The handler to close all shards. */ + stop: typeof stop; + /** Sends the discord payload to another server. */ + handleDiscordPayload: (shard: Shard, data: DiscordGatewayPayload) => any; + /** Tell the worker to begin identifying this shard */ + tellWorkerToIdentify: typeof tellWorkerToIdentify; /** Handle the different logs. Used for debugging. */ - debug: options.debug || function () {}, - - // /** The methods related to resharding. */ + debug: (text: GatewayDebugEvents, ...args: any[]) => unknown; + /** The methods related to resharding. */ // resharding: { - // /** Whether the resharder should automatically switch to LARGE BOT SHARDING when the bot is above 100K servers. */ - // useOptimalLargeBotSharding: options.resharding?.useOptimalLargeBotSharding ?? true, - // /** Whether or not to automatically reshard. - // * - // * @default true - // */ - // reshard: options.resharding?.reshard ?? true, - // /** The percentage at which resharding should occur. - // * - // * @default 80 - // */ - // reshardPercentage: options.resharding?.reshardPercentage ?? 80, + // /** Whether the resharder should automatically switch to LARGE BOT SHARDING when you are above 100K servers. */ + // useOptimalLargeBotSharding: boolean; + // /** Whether or not to automatically reshard. */ + // reshard: boolean; + // /** The percentage at which resharding should occur. */ + // reshardPercentage: number; // /** Handles resharding the bot when necessary. */ - // resharder: options.resharding?.resharder ?? resharder, + // resharder: typeof resharder; // /** Handles checking if all new shards are online in the new gateway. */ - // isPending: options.resharding?.isPending ?? resharderIsPending, + // isPending: typeof resharderIsPending; // /** Handles closing all shards in the old gateway. */ - // closeOldShards: options.resharding?.closeOldShards ?? resharderCloseOldShards, - // /** Handles checking if it is time to reshard and triggers the resharder. */ - // check: options.resharding?.check ?? startReshardingChecks, + // closeOldShards: typeof resharderCloseOldShards; // /** Handler to mark a guild id with its new shard id in cache. */ - // markNewGuildShardId: options.resharding?.markNewGuildShardId ?? markNewGuildShardId, + // markNewGuildShardId: typeof markNewGuildShardId; // /** Handler to update all guilds in cache with the new shard id. */ - // editGuildShardIds: options.resharding?.editGuildShardIds ?? reshardingEditGuildShardIds, - // }, + // editGuildShardIds: typeof reshardingEditGuildShardIds; + // }; + /** Calculates the number of shards to use based on the max concurrency */ + calculateTotalShards: typeof calculateTotalShards; - /** Calculate the amount of Shards which should be used based on the bot's max concurrency. */ - calculateTotalShards: function () { - return calculateTotalShardsOverwritten(this); - }, - - /** Calculate the Id of the Worker related to this Shard. */ - calculateWorkerId: function (shardId: number) { - return calculateWorkerIdOverwritten(this, shardId); - }, - }; - - gatewayManager.manager = createShardManager({ - createShardOptions: options.createShardOptions, - gatewayConfig: options.gatewayConfig, - shardIds: [], - totalShards, - - handleMessage: function (shard, message) { - return options.handleDiscordPayload(shard, message); - }, - - requestIdentify: async (shardId) => { - // TODO: improve - await gatewayManager.buckets.get(shardId % gatewayManager.gatewayBot.sessionStartLimit.maxConcurrency)!.leak - .acquire(1); - }, - }); - - return gatewayManager; -} - -export interface CreateGatewayManager { - /** Delay in milliseconds to wait before spawning next shard. OPTIMAL IS ABOVE 5100. YOU DON'T WANT TO HIT THE RATE LIMIT!!! */ - spawnShardDelay: number; - /** Total amount of shards your bot uses. Useful for zero-downtime updates or resharding. */ - totalShards: number; - /** The amount of shards to load per worker. */ - shardsPerWorker: number; - /** The total amount of workers to use for your bot. */ - totalWorkers: number; - /** Id of the first Shard which should get controlled by this manager. - * - * NOTE: This is intended for testing purposes - * if big bots want to test the gateway on smaller scale. - * This is not recommended to be used in production. - */ - firstShardId: number; - /** Id of the last Shard which should get controlled by this manager. - * - * NOTE: This is intended for testing purposes - * if big bots want to test the gateway on smaller scale. - * This is not recommended to be used in production. - */ - lastShardId: number; - - /** Important data which is used by the manager to connect shards to the gateway. */ - gatewayBot: GatewayBot; - - gatewayConfig: PickPartial; - - /** Options which are used to create a new shard. */ - createShardOptions?: Omit; - - /** Stored as bucketId: { workers: [workerId, [ShardIds]], createNextShard: boolean } */ - buckets: Map< - number, - { - workers: { id: number; queue: number[] }[]; - leak: LeakyBucket; - } - >; - // METHODS - - /** Prepares the buckets for identifying */ - prepareBuckets: typeof prepareBuckets; - /** The handler for spawning ALL the shards. */ - spawnShards: typeof spawnShards; - /** The handler to close all shards. */ - stop: typeof stop; - /** Sends the discord payload to another server. */ - handleDiscordPayload: (shard: Shard, data: DiscordGatewayPayload) => any; - /** Tell the worker to begin identifying this shard */ - tellWorkerToIdentify: typeof tellWorkerToIdentify; - /** Handle the different logs. Used for debugging. */ - debug: (text: GatewayDebugEvents, ...args: any[]) => unknown; - /** The methods related to resharding. */ - // resharding: { - // /** Whether the resharder should automatically switch to LARGE BOT SHARDING when you are above 100K servers. */ - // useOptimalLargeBotSharding: boolean; - // /** Whether or not to automatically reshard. */ - // reshard: boolean; - // /** The percentage at which resharding should occur. */ - // reshardPercentage: number; - // /** Handles resharding the bot when necessary. */ - // resharder: typeof resharder; - // /** Handles checking if all new shards are online in the new gateway. */ - // isPending: typeof resharderIsPending; - // /** Handles closing all shards in the old gateway. */ - // closeOldShards: typeof resharderCloseOldShards; - // /** Handler to mark a guild id with its new shard id in cache. */ - // markNewGuildShardId: typeof markNewGuildShardId; - // /** Handler to update all guilds in cache with the new shard id. */ - // editGuildShardIds: typeof reshardingEditGuildShardIds; - // }; - /** Calculates the number of shards to use based on the max concurrency */ - calculateTotalShards: typeof calculateTotalShards; - - /** Calculate the id of the worker related ot this Shard. */ - calculateWorkerId: typeof calculateWorkerId; + /** Calculate the id of the worker related ot this Shard. */ + calculateWorkerId: typeof calculateWorkerId; } export type GatewayDebugEvents = - | "GW ERROR" - | "GW CLOSED" - | "GW CLOSED_RECONNECT" - | "GW RAW" - | "GW RECONNECT" - | "GW INVALID_SESSION" - | "GW RESUMED" - | "GW RESUMING" - | "GW IDENTIFYING" - | "GW RAW_SEND" - | "GW MAX REQUESTS" - | "GW DEBUG" - | "GW HEARTBEATING" - | "GW HEARTBEATING_STARTED" - | "GW HEARTBEATING_DETAILS" - | "GW HEARTBEATING_CLOSED"; + | "GW ERROR" + | "GW CLOSED" + | "GW CLOSED_RECONNECT" + | "GW RAW" + | "GW RECONNECT" + | "GW INVALID_SESSION" + | "GW RESUMED" + | "GW RESUMING" + | "GW IDENTIFYING" + | "GW RAW_SEND" + | "GW MAX REQUESTS" + | "GW DEBUG" + | "GW HEARTBEATING" + | "GW HEARTBEATING_STARTED" + | "GW HEARTBEATING_DETAILS" + | "GW HEARTBEATING_CLOSED"; diff --git a/vendor/gateway/manager/prepareBuckets.ts b/vendor/gateway/manager/prepareBuckets.ts index 308ea3a..d813fa9 100644 --- a/vendor/gateway/manager/prepareBuckets.ts +++ b/vendor/gateway/manager/prepareBuckets.ts @@ -2,46 +2,46 @@ import { createLeakyBucket } from "../../util/bucket.ts"; import { GatewayManager } from "./gatewayManager.ts"; export function prepareBuckets(gateway: GatewayManager) { - for (let i = 0; i < gateway.gatewayBot.sessionStartLimit.maxConcurrency; ++i) { - gateway.buckets.set(i, { - workers: [], - leak: createLeakyBucket({ - max: 1, - refillAmount: 1, - // special number which is proven to be working dont change - refillInterval: gateway.spawnShardDelay, - }), - }); - } - - // ORGANIZE ALL SHARDS INTO THEIR OWN BUCKETS - for (let shardId = gateway.firstShardId; shardId <= gateway.lastShardId; ++shardId) { - // gateway.debug("GW DEBUG", `1. Running for loop in spawnShards function for shardId ${i}.`); - if (shardId >= gateway.manager.totalShards) { - throw new Error( - `Shard (id: ${shardId}) is bigger or equal to the used amount of used shards which is ${gateway.manager.totalShards}`, - ); + for (let i = 0; i < gateway.gatewayBot.sessionStartLimit.maxConcurrency; ++i) { + gateway.buckets.set(i, { + workers: [], + leak: createLeakyBucket({ + max: 1, + refillAmount: 1, + // special number which is proven to be working dont change + refillInterval: gateway.spawnShardDelay, + }), + }); } - const bucketId = shardId % gateway.gatewayBot.sessionStartLimit.maxConcurrency; - const bucket = gateway.buckets.get(bucketId); - if (!bucket) { - throw new Error( - `Shard (id: ${shardId}) got assigned to an illegal bucket id: ${bucketId}, expected a bucket id between 0 and ${ - gateway.gatewayBot.sessionStartLimit.maxConcurrency - 1 - }`, - ); - } + // ORGANIZE ALL SHARDS INTO THEIR OWN BUCKETS + for (let shardId = gateway.firstShardId; shardId <= gateway.lastShardId; ++shardId) { + // gateway.debug("GW DEBUG", `1. Running for loop in spawnShards function for shardId ${i}.`); + if (shardId >= gateway.manager.totalShards) { + throw new Error( + `Shard (id: ${shardId}) is bigger or equal to the used amount of used shards which is ${gateway.manager.totalShards}`, + ); + } - // FIND A QUEUE IN THIS BUCKET THAT HAS SPACE - // const worker = bucket.workers.find((w) => w.queue.length < gateway.shardsPerWorker); - const workerId = gateway.calculateWorkerId(shardId); - const worker = bucket.workers.find((w) => w.id === workerId); - if (worker) { - // IF THE QUEUE HAS SPACE JUST ADD IT TO THIS QUEUE - worker.queue.push(shardId); - } else { - bucket.workers.push({ id: workerId, queue: [shardId] }); + const bucketId = shardId % gateway.gatewayBot.sessionStartLimit.maxConcurrency; + const bucket = gateway.buckets.get(bucketId); + if (!bucket) { + throw new Error( + `Shard (id: ${shardId}) got assigned to an illegal bucket id: ${bucketId}, expected a bucket id between 0 and ${ + gateway.gatewayBot.sessionStartLimit.maxConcurrency - 1 + }`, + ); + } + + // FIND A QUEUE IN THIS BUCKET THAT HAS SPACE + // const worker = bucket.workers.find((w) => w.queue.length < gateway.shardsPerWorker); + const workerId = gateway.calculateWorkerId(shardId); + const worker = bucket.workers.find((w) => w.id === workerId); + if (worker) { + // IF THE QUEUE HAS SPACE JUST ADD IT TO THIS QUEUE + worker.queue.push(shardId); + } else { + bucket.workers.push({ id: workerId, queue: [shardId] }); + } } - } } diff --git a/vendor/gateway/manager/resharder.ts b/vendor/gateway/manager/resharder.ts index 2408f6f..d0e4d7f 100644 --- a/vendor/gateway/manager/resharder.ts +++ b/vendor/gateway/manager/resharder.ts @@ -4,56 +4,56 @@ import { createGatewayManager, GatewayManager } from "./gatewayManager.ts"; export type Resharder = ReturnType; export function activateResharder(options: ActivateResharderOptions) { - const resharder = { - // ---------- - // PROPERTIES - // ---------- + const resharder = { + // ---------- + // PROPERTIES + // ---------- - /** Interval in milliseconds of when to check whether it's time to reshard. - * - * @default 28800000 (8 hours) - */ - checkInterval: options.checkInterval || 28800000, + /** Interval in milliseconds of when to check whether it's time to reshard. + * + * @default 28800000 (8 hours) + */ + checkInterval: options.checkInterval || 28800000, - /** Gateway manager which is currently processing all shards and events. */ - gateway: options.gatewayManager, + /** Gateway manager which is currently processing all shards and events. */ + gateway: options.gatewayManager, - /** Timeout of the reshard checker. */ - intervalId: undefined as number | undefined, + /** Timeout of the reshard checker. */ + intervalId: undefined as number | undefined, - /** Percentage at which resharding should occur. - * @default 80 - */ - percentage: options.percentage ?? 80, + /** Percentage at which resharding should occur. + * @default 80 + */ + percentage: options.percentage ?? 80, - /** Whether the resharder should automatically switch to LARGE BOT SHARDING when the bot is above 100K servers. */ - useOptimalLargeBotSharding: options.useOptimalLargeBotSharding ?? true, + /** Whether the resharder should automatically switch to LARGE BOT SHARDING when the bot is above 100K servers. */ + useOptimalLargeBotSharding: options.useOptimalLargeBotSharding ?? true, - // ---------- - // METHODS - // ---------- + // ---------- + // METHODS + // ---------- - /** Activate the resharder and delay the next reshard check. */ - activate: function () { - return activate(this); - }, + /** Activate the resharder and delay the next reshard check. */ + activate: function () { + return activate(this); + }, - /** Function which is used to fetch the current gateway information of the bot. - * This function is mainly used by the reshard checker. - */ - getGatewayBot: options.getGatewayBot, + /** Function which is used to fetch the current gateway information of the bot. + * This function is mainly used by the reshard checker. + */ + getGatewayBot: options.getGatewayBot, - /** Reshard the bots gateway. */ - reshard: function (gatewayBot: GatewayBot) { - return reshard(this, gatewayBot); - }, + /** Reshard the bots gateway. */ + reshard: function (gatewayBot: GatewayBot) { + return reshard(this, gatewayBot); + }, - tellWorkerToPrepare: options.tellWorkerToPrepare, - }; + tellWorkerToPrepare: options.tellWorkerToPrepare, + }; - resharder.activate(); + resharder.activate(); - return resharder; + return resharder; } // /** The methods related to resharding. */ @@ -85,106 +85,106 @@ export function activateResharder(options: ActivateResharderOptions) { // }, export interface ActivateResharderOptions { - /** Interval in milliseconds of when to check whether it's time to reshard. - * - * @default 28800000 (8 hours) - */ - checkInterval?: number; - /** Gateway manager which the resharder should be bound to. */ - gatewayManager: GatewayManager; - /** Percentage at which resharding should occur. - * @default 80 - */ - percentage?: number; - /** Whether the resharder should automatically switch to LARGE BOT SHARDING when the bot is above 100K servers. */ - useOptimalLargeBotSharding?: boolean; + /** Interval in milliseconds of when to check whether it's time to reshard. + * + * @default 28800000 (8 hours) + */ + checkInterval?: number; + /** Gateway manager which the resharder should be bound to. */ + gatewayManager: GatewayManager; + /** Percentage at which resharding should occur. + * @default 80 + */ + percentage?: number; + /** Whether the resharder should automatically switch to LARGE BOT SHARDING when the bot is above 100K servers. */ + useOptimalLargeBotSharding?: boolean; - /** Function which can be used to fetch the current gateway information of the bot. - * This function is mainly used by the reshard checker. - */ - getGatewayBot(): Promise; + /** Function which can be used to fetch the current gateway information of the bot. + * This function is mainly used by the reshard checker. + */ + getGatewayBot(): Promise; - /** Function which is used to tell a Worker that it should identify a resharder Shard to the gateway and wait for further instructions. - * The worker should **NOT** process any events coming from this Shard. - */ - tellWorkerToPrepare( - gatewayManager: GatewayManager, - workerId: number, - shardId: number, - bucketId: number, - ): Promise; + /** Function which is used to tell a Worker that it should identify a resharder Shard to the gateway and wait for further instructions. + * The worker should **NOT** process any events coming from this Shard. + */ + tellWorkerToPrepare( + gatewayManager: GatewayManager, + workerId: number, + shardId: number, + bucketId: number, + ): Promise; } /** Handler that by default will check to see if resharding should occur. Can be overridden if you have multiple servers and you want to communicate through redis pubsub or whatever you prefer. */ export function activate(resharder: Resharder): void { - if (resharder.intervalId !== undefined) { - throw new Error("[RESHARDER] Cannot activate the resharder more than one time."); - } + if (resharder.intervalId !== undefined) { + throw new Error("[RESHARDER] Cannot activate the resharder more than one time."); + } - resharder.intervalId = setInterval(async () => { - // gateway.debug("GW DEBUG", "[Resharding] Checking if resharding is needed."); + resharder.intervalId = setInterval(async () => { + // gateway.debug("GW DEBUG", "[Resharding] Checking if resharding is needed."); - // TODO: is it possible to route this to REST? - const result = await resharder.getGatewayBot(); + // TODO: is it possible to route this to REST? + const result = await resharder.getGatewayBot(); - const percentage = - ((result.shards - resharder.gateway.manager.totalShards) / resharder.gateway.manager.totalShards) * 100; - // Less than necessary% being used so do nothing - if (percentage < resharder.percentage) return; + const percentage = + ((result.shards - resharder.gateway.manager.totalShards) / resharder.gateway.manager.totalShards) * 100; + // Less than necessary% being used so do nothing + if (percentage < resharder.percentage) return; - // Don't have enough identify rate limits to reshard - if (result.sessionStartLimit.remaining < result.shards) return; + // Don't have enough identify rate limits to reshard + if (result.sessionStartLimit.remaining < result.shards) return; - // MULTI-SERVER BOTS OVERRIDE THIS IF YOU NEED TO RESHARD SERVER BY SERVER - return resharder.reshard(result); - }, resharder.checkInterval); + // MULTI-SERVER BOTS OVERRIDE THIS IF YOU NEED TO RESHARD SERVER BY SERVER + return resharder.reshard(result); + }, resharder.checkInterval); } export async function reshard(resharder: Resharder, gatewayBot: GatewayBot) { - // oldGateway.debug("GW DEBUG", "[Resharding] Starting the reshard process."); + // oldGateway.debug("GW DEBUG", "[Resharding] Starting the reshard process."); - // Create a temporary gateway manager for easier handling. - const tmpManager = createGatewayManager({ - gatewayBot: gatewayBot, - gatewayConfig: resharder.gateway.manager.gatewayConfig, - handleDiscordPayload: () => {}, - tellWorkerToIdentify: resharder.tellWorkerToPrepare, - }); + // Create a temporary gateway manager for easier handling. + const tmpManager = createGatewayManager({ + gatewayBot: gatewayBot, + gatewayConfig: resharder.gateway.manager.gatewayConfig, + handleDiscordPayload: () => {}, + tellWorkerToIdentify: resharder.tellWorkerToPrepare, + }); - // Begin resharding + // Begin resharding - // If more than 100K servers, begin switching to 16x sharding - if (resharder.useOptimalLargeBotSharding) { - // gateway.debug("GW DEBUG", "[Resharding] Using optimal large bot sharding solution."); - tmpManager.manager.totalShards = resharder.gateway.calculateTotalShards(resharder.gateway); - } + // If more than 100K servers, begin switching to 16x sharding + if (resharder.useOptimalLargeBotSharding) { + // gateway.debug("GW DEBUG", "[Resharding] Using optimal large bot sharding solution."); + tmpManager.manager.totalShards = resharder.gateway.calculateTotalShards(resharder.gateway); + } - tmpManager.spawnShards(tmpManager); + tmpManager.spawnShards(tmpManager); - return new Promise((resolve) => { - // TIMER TO KEEP CHECKING WHEN ALL SHARDS HAVE RESHARDED - const timer = setInterval(async () => { - const pending = await gateway.resharding.isPending(gateway, oldGateway); - // STILL PENDING ON SOME SHARDS TO BE CREATED - if (pending) return; + return new Promise((resolve) => { + // TIMER TO KEEP CHECKING WHEN ALL SHARDS HAVE RESHARDED + const timer = setInterval(async () => { + const pending = await gateway.resharding.isPending(gateway, oldGateway); + // STILL PENDING ON SOME SHARDS TO BE CREATED + if (pending) return; - // ENABLE EVENTS ON NEW SHARDS AND IGNORE EVENTS ON OLD - const oldHandler = oldGateway.handleDiscordPayload; - gateway.handleDiscordPayload = oldHandler; - oldGateway.handleDiscordPayload = function (og, data, shardId) { - // ALLOW EXCEPTION FOR CHUNKING TO PREVENT REQUESTS FREEZING - if (data.t !== "GUILD_MEMBERS_CHUNK") return; - oldHandler(og, data, shardId); - }; + // ENABLE EVENTS ON NEW SHARDS AND IGNORE EVENTS ON OLD + const oldHandler = oldGateway.handleDiscordPayload; + gateway.handleDiscordPayload = oldHandler; + oldGateway.handleDiscordPayload = function (og, data, shardId) { + // ALLOW EXCEPTION FOR CHUNKING TO PREVENT REQUESTS FREEZING + if (data.t !== "GUILD_MEMBERS_CHUNK") return; + oldHandler(og, data, shardId); + }; - // STOP TIMER - clearInterval(timer); - await gateway.resharding.editGuildShardIds(); - await gateway.resharding.closeOldShards(oldGateway); - gateway.debug("GW DEBUG", "[Resharding] Complete."); - resolve(gateway); - }, 30000); - }) as Promise; + // STOP TIMER + clearInterval(timer); + await gateway.resharding.editGuildShardIds(); + await gateway.resharding.closeOldShards(oldGateway); + gateway.debug("GW DEBUG", "[Resharding] Complete."); + resolve(gateway); + }, 30000); + }) as Promise; } // /** The handler to automatically reshard when necessary. */ @@ -269,41 +269,41 @@ export async function reshard(resharder: Resharder, gatewayBot: GatewayBot) { /** Handler that by default will check all new shards are online in the new gateway. The handler can be overridden if you have multiple servers to communicate through redis pubsub or whatever you prefer. */ export async function resharderIsPending( - gateway: GatewayManager, - oldGateway: GatewayManager, + gateway: GatewayManager, + oldGateway: GatewayManager, ) { - for (let i = gateway.firstShardId; i < gateway.lastShardId; i++) { - const shard = gateway.shards.get(i); - if (!shard?.ready) { - return true; + for (let i = gateway.firstShardId; i < gateway.lastShardId; i++) { + const shard = gateway.shards.get(i); + if (!shard?.ready) { + return true; + } } - } - return false; + return false; } /** Handler that by default closes all shards in the old gateway. Can be overridden if you have multiple servers and you want to communicate through redis pubsub or whatever you prefer. */ export async function resharderCloseOldShards(oldGateway: GatewayManager) { - // SHUT DOWN ALL SHARDS IF NOTHING IN QUEUE - oldGateway.shards.forEach((shard) => { - // CLOSE THIS SHARD IT HAS NO QUEUE - if (!shard.processingQueue && !shard.queue.length) { - return oldGateway.closeWS( - shard.ws, - 3066, - "Shard has been resharded. Closing shard since it has no queue.", - ); - } + // SHUT DOWN ALL SHARDS IF NOTHING IN QUEUE + oldGateway.shards.forEach((shard) => { + // CLOSE THIS SHARD IT HAS NO QUEUE + if (!shard.processingQueue && !shard.queue.length) { + return oldGateway.closeWS( + shard.ws, + 3066, + "Shard has been resharded. Closing shard since it has no queue.", + ); + } - // IF QUEUE EXISTS GIVE IT 5 MINUTES TO COMPLETE - setTimeout(() => { - oldGateway.closeWS( - shard.ws, - 3066, - "Shard has been resharded. Delayed closing shard since it had a queue.", - ); - }, 300000); - }); + // IF QUEUE EXISTS GIVE IT 5 MINUTES TO COMPLETE + setTimeout(() => { + oldGateway.closeWS( + shard.ws, + 3066, + "Shard has been resharded. Delayed closing shard since it had a queue.", + ); + }, 300000); + }); } // /** Handler that by default will check to see if resharding should occur. Can be overridden if you have multiple servers and you want to communicate through redis pubsub or whatever you prefer. */ @@ -330,10 +330,10 @@ export async function resharderCloseOldShards(oldGateway: GatewayManager) { /** Handler that by default will save the new shard id for each guild this becomes ready in new gateway. This can be overridden to save the shard ids in a redis cache layer or whatever you prefer. These ids will be used later to update all guilds. */ export async function markNewGuildShardId(guildIds: bigint[], shardId: number) { - // PLACEHOLDER TO LET YOU MARK A GUILD ID AND SHARD ID FOR LATER USE ONCE RESHARDED + // PLACEHOLDER TO LET YOU MARK A GUILD ID AND SHARD ID FOR LATER USE ONCE RESHARDED } /** Handler that by default does not do anything since by default the library will not cache. */ export async function reshardingEditGuildShardIds() { - // PLACEHOLDER TO LET YOU UPDATE CACHED GUILDS + // PLACEHOLDER TO LET YOU UPDATE CACHED GUILDS } diff --git a/vendor/gateway/manager/shardManager.ts b/vendor/gateway/manager/shardManager.ts index 38af3f2..bfd48df 100644 --- a/vendor/gateway/manager/shardManager.ts +++ b/vendor/gateway/manager/shardManager.ts @@ -17,106 +17,106 @@ export type ShardManager = ReturnType; * The aim of this is to provide an easy to use manager which can be used by workers or any other kind of separate process. */ export function createShardManager(options: CreateShardManager) { - return { + return { + // ---------- + // PROPERTIES + // ---------- + + /** Options which are used to create a new Shard. */ + createShardOptions: { + ...options.createShardOptions, + events: { + ...options.createShardOptions?.events, + message: options.createShardOptions?.events?.message ?? options.handleMessage, + }, + }, + /** Gateway configuration which is used when creating a Shard. */ + gatewayConfig: options.gatewayConfig, + /** Managed Shards. */ + shards: new Collection( + options.shardIds.map((shardId) => { + const shard = createShard({ + ...options.createShardOptions, + id: shardId, + totalShards: options.totalShards, + gatewayConfig: options.gatewayConfig, + requestIdentify: async function () { + return await options.requestIdentify(shardId); + }, + }); + + return [shardId, shard] as const; + }), + ), + /** Total amount of Shards used by the bot. */ + totalShards: options.totalShards, + + // ---------- + // METHODS + // ---------- + + /** Tell the manager to identify a Shard. + * If this Shard is not already managed this will also add the Shard to the manager. + */ + identify: async function (shardId: number) { + let shard = this.shards.get(shardId); + if (!shard) { + shard = createShard({ + ...this.createShardOptions, + id: shardId, + totalShards: this.totalShards, + gatewayConfig: this.gatewayConfig, + requestIdentify: async function () { + return await options.requestIdentify(shardId); + }, + }); + + this.shards.set(shardId, shard); + } + + return await shard.identify(); + }, + + /** Kill a shard. + * Close a shards connection to Discord's gateway (if any) and remove it from the manager. + */ + kill: async function (shardId: number) { + const shard = this.shards.get(shardId); + if (!shard) return; + + this.shards.delete(shardId); + return await shard.shutdown(); + }, + + /** This function communicates with the parent manager, + * in order to know whether this manager is allowed to identify a new shard. + */ + requestIdentify: options.requestIdentify, + }; +} + +export interface CreateShardManager { // ---------- // PROPERTIES // ---------- - /** Options which are used to create a new Shard. */ - createShardOptions: { - ...options.createShardOptions, - events: { - ...options.createShardOptions?.events, - message: options.createShardOptions?.events?.message ?? options.handleMessage, - }, - }, + createShardOptions?: Omit; /** Gateway configuration which is used when creating a Shard. */ - gatewayConfig: options.gatewayConfig, - /** Managed Shards. */ - shards: new Collection( - options.shardIds.map((shardId) => { - const shard = createShard({ - ...options.createShardOptions, - id: shardId, - totalShards: options.totalShards, - gatewayConfig: options.gatewayConfig, - requestIdentify: async function () { - return await options.requestIdentify(shardId); - }, - }); - - return [shardId, shard] as const; - }), - ), - /** Total amount of Shards used by the bot. */ - totalShards: options.totalShards, + gatewayConfig: PickPartial; + /** Ids of the Shards which should be managed. */ + shardIds: number[]; + /** Total amount of Shard used by the bot. */ + totalShards: number; // ---------- // METHODS // ---------- - /** Tell the manager to identify a Shard. - * If this Shard is not already managed this will also add the Shard to the manager. - */ - identify: async function (shardId: number) { - let shard = this.shards.get(shardId); - if (!shard) { - shard = createShard({ - ...this.createShardOptions, - id: shardId, - totalShards: this.totalShards, - gatewayConfig: this.gatewayConfig, - requestIdentify: async function () { - return await options.requestIdentify(shardId); - }, - }); - - this.shards.set(shardId, shard); - } - - return await shard.identify(); - }, - - /** Kill a shard. - * Close a shards connection to Discord's gateway (if any) and remove it from the manager. - */ - kill: async function (shardId: number) { - const shard = this.shards.get(shardId); - if (!shard) return; - - this.shards.delete(shardId); - return await shard.shutdown(); - }, + /** This function is used when a shard receives any message from Discord. */ + handleMessage(shard: Shard, message: DiscordGatewayPayload): unknown; /** This function communicates with the parent manager, - * in order to know whether this manager is allowed to identify a new shard. + * in order to know whether this manager is allowed to identify a new shard. # */ - requestIdentify: options.requestIdentify, - }; -} - -export interface CreateShardManager { - // ---------- - // PROPERTIES - // ---------- - /** Options which are used to create a new Shard. */ - createShardOptions?: Omit; - /** Gateway configuration which is used when creating a Shard. */ - gatewayConfig: PickPartial; - /** Ids of the Shards which should be managed. */ - shardIds: number[]; - /** Total amount of Shard used by the bot. */ - totalShards: number; - - // ---------- - // METHODS - // ---------- - - /** This function is used when a shard receives any message from Discord. */ - handleMessage(shard: Shard, message: DiscordGatewayPayload): unknown; - - /** This function communicates with the parent manager, - * in order to know whether this manager is allowed to identify a new shard. # - */ - requestIdentify(shardId: number): Promise; + requestIdentify(shardId: number): Promise; } diff --git a/vendor/gateway/manager/spawnShards.ts b/vendor/gateway/manager/spawnShards.ts index 62df1ed..21ce7d6 100644 --- a/vendor/gateway/manager/spawnShards.ts +++ b/vendor/gateway/manager/spawnShards.ts @@ -6,27 +6,27 @@ import { createGatewayManager, GatewayManager } from "./gatewayManager.ts"; /** Begin spawning shards. */ export function spawnShards(gateway: GatewayManager) { - // PREPARES THE MAX SHARD COUNT BY CONCURRENCY - // if (manager.resharding.useOptimalLargeBotSharding) { - // // gateway.debug("GW DEBUG", "[Spawning] Using optimal large bot sharding solution."); - // manager.manager.totalShards = manager.calculateTotalShards( - // manager, - // ); - // } + // PREPARES THE MAX SHARD COUNT BY CONCURRENCY + // if (manager.resharding.useOptimalLargeBotSharding) { + // // gateway.debug("GW DEBUG", "[Spawning] Using optimal large bot sharding solution."); + // manager.manager.totalShards = manager.calculateTotalShards( + // manager, + // ); + // } - // PREPARES ALL SHARDS IN SPECIFIC BUCKETS - gateway.prepareBuckets(); + // PREPARES ALL SHARDS IN SPECIFIC BUCKETS + gateway.prepareBuckets(); - // SPREAD THIS OUT TO DIFFERENT WORKERS TO BEGIN STARTING UP - gateway.buckets.forEach(async (bucket, bucketId) => { - // gateway.debug("GW DEBUG", `2. Running forEach loop in spawnShards function.`); + // SPREAD THIS OUT TO DIFFERENT WORKERS TO BEGIN STARTING UP + gateway.buckets.forEach(async (bucket, bucketId) => { + // gateway.debug("GW DEBUG", `2. Running forEach loop in spawnShards function.`); - for (const worker of bucket.workers) { - // gateway.debug("GW DEBUG", `3. Running for of loop in spawnShards function.`); + for (const worker of bucket.workers) { + // gateway.debug("GW DEBUG", `3. Running for of loop in spawnShards function.`); - for (const shardId of worker.queue) { - await gateway.tellWorkerToIdentify(worker.id, shardId, bucketId); - } - } - }); + for (const shardId of worker.queue) { + await gateway.tellWorkerToIdentify(worker.id, shardId, bucketId); + } + } + }); } diff --git a/vendor/gateway/manager/stop.ts b/vendor/gateway/manager/stop.ts index e852de2..753fb6c 100644 --- a/vendor/gateway/manager/stop.ts +++ b/vendor/gateway/manager/stop.ts @@ -2,7 +2,7 @@ import { delay } from "../../util/delay.ts"; import { GatewayManager } from "./gatewayManager.ts"; export async function stop(gateway: GatewayManager, code: number, reason: string) { - gateway.manager.shards.forEach((shard) => shard.close(code, reason)); + gateway.manager.shards.forEach((shard) => shard.close(code, reason)); - await delay(5000); + await delay(5000); } diff --git a/vendor/gateway/manager/tellWorkerToIdentify.ts b/vendor/gateway/manager/tellWorkerToIdentify.ts index 69054b6..5913752 100644 --- a/vendor/gateway/manager/tellWorkerToIdentify.ts +++ b/vendor/gateway/manager/tellWorkerToIdentify.ts @@ -4,10 +4,10 @@ import { GatewayManager } from "./gatewayManager.ts"; /** Allows users to hook in and change to communicate to different workers across different servers or anything they like. For example using redis pubsub to talk to other servers. */ export async function tellWorkerToIdentify( - gateway: GatewayManager, - _workerId: number, - shardId: number, - _bucketId: number, + gateway: GatewayManager, + _workerId: number, + shardId: number, + _bucketId: number, ): Promise { - return await gateway.manager.identify(shardId); + return await gateway.manager.identify(shardId); } diff --git a/vendor/gateway/shard/calculateSafeRequests.ts b/vendor/gateway/shard/calculateSafeRequests.ts index ea0ca6a..12695b5 100644 --- a/vendor/gateway/shard/calculateSafeRequests.ts +++ b/vendor/gateway/shard/calculateSafeRequests.ts @@ -1,9 +1,9 @@ import { Shard } from "./types.ts"; export function calculateSafeRequests(shard: Shard) { - // * 2 adds extra safety layer for discords OP 1 requests that we need to respond to - const safeRequests = shard.maxRequestsPerRateLimitTick - - Math.ceil(shard.rateLimitResetInterval / shard.heart.interval) * 2; + // * 2 adds extra safety layer for discords OP 1 requests that we need to respond to + const safeRequests = shard.maxRequestsPerRateLimitTick - + Math.ceil(shard.rateLimitResetInterval / shard.heart.interval) * 2; - return safeRequests < 0 ? 0 : safeRequests; + return safeRequests < 0 ? 0 : safeRequests; } diff --git a/vendor/gateway/shard/close.ts b/vendor/gateway/shard/close.ts index fc8d0b2..9dfefa6 100644 --- a/vendor/gateway/shard/close.ts +++ b/vendor/gateway/shard/close.ts @@ -1,7 +1,7 @@ import { Shard } from "./types.ts"; export function close(shard: Shard, code: number, reason: string): void { - if (shard.socket?.readyState !== WebSocket.OPEN) return; + if (shard.socket?.readyState !== WebSocket.OPEN) return; - return shard.socket?.close(code, reason); + return shard.socket?.close(code, reason); } diff --git a/vendor/gateway/shard/connect.ts b/vendor/gateway/shard/connect.ts index 980466b..2671f82 100644 --- a/vendor/gateway/shard/connect.ts +++ b/vendor/gateway/shard/connect.ts @@ -1,34 +1,34 @@ import { Shard, ShardState } from "./types.ts"; export async function connect(shard: Shard): Promise { - // Only set the shard to `Connecting` state, - // if the connection request does not come from an identify or resume action. - if (![ShardState.Identifying, ShardState.Resuming].includes(shard.state)) { - shard.state = ShardState.Connecting; - } - shard.events.connecting?.(shard); + // Only set the shard to `Connecting` state, + // if the connection request does not come from an identify or resume action. + if (![ShardState.Identifying, ShardState.Resuming].includes(shard.state)) { + shard.state = ShardState.Connecting; + } + shard.events.connecting?.(shard); - // Explicitly setting the encoding to json, since we do not support ETF. - const socket = new WebSocket(`${shard.gatewayConfig.url}/?v=${shard.gatewayConfig.version}&encoding=json`); - shard.socket = socket; + // Explicitly setting the encoding to json, since we do not support ETF. + const socket = new WebSocket(`${shard.gatewayConfig.url}/?v=${shard.gatewayConfig.version}&encoding=json`); + shard.socket = socket; - // TODO: proper event handling - socket.onerror = (event) => console.log({ error: event }); + // TODO: proper event handling + socket.onerror = (event) => console.log({ error: event }); - socket.onclose = (event) => shard.handleClose(event); + socket.onclose = (event) => shard.handleClose(event); - socket.onmessage = (message) => shard.handleMessage(message); + socket.onmessage = (message) => shard.handleMessage(message); - return new Promise((resolve) => { - socket.onopen = () => { - // Only set the shard to `Unidentified` state, - // if the connection request does not come from an identify or resume action. - if (![ShardState.Identifying, ShardState.Resuming].includes(shard.state)) { - shard.state = ShardState.Unidentified; - } - shard.events.connected?.(shard); + return new Promise((resolve) => { + socket.onopen = () => { + // Only set the shard to `Unidentified` state, + // if the connection request does not come from an identify or resume action. + if (![ShardState.Identifying, ShardState.Resuming].includes(shard.state)) { + shard.state = ShardState.Unidentified; + } + shard.events.connected?.(shard); - resolve(); - }; - }); + resolve(); + }; + }); } diff --git a/vendor/gateway/shard/createShard.ts b/vendor/gateway/shard/createShard.ts index 3595174..1695cf9 100644 --- a/vendor/gateway/shard/createShard.ts +++ b/vendor/gateway/shard/createShard.ts @@ -1,16 +1,16 @@ import { identify } from "./identify.ts"; import { handleMessage } from "./handleMessage.ts"; import { - DEFAULT_HEARTBEAT_INTERVAL, - GATEWAY_RATE_LIMIT_RESET_INTERVAL, - MAX_GATEWAY_REQUESTS_PER_INTERVAL, - Shard, - ShardEvents, - ShardGatewayConfig, - ShardHeart, - ShardSocketCloseCodes, - ShardSocketRequest, - ShardState, + DEFAULT_HEARTBEAT_INTERVAL, + GATEWAY_RATE_LIMIT_RESET_INTERVAL, + MAX_GATEWAY_REQUESTS_PER_INTERVAL, + Shard, + ShardEvents, + ShardGatewayConfig, + ShardHeart, + ShardSocketCloseCodes, + ShardSocketRequest, + ShardState, } from "./types.ts"; import { startHeartbeating } from "./startHeartbeating.ts"; import { stopHeartbeating } from "./stopHeartbeating.ts"; @@ -33,301 +33,301 @@ import { API_VERSION } from "../../util/constants.ts"; /** */ export function createShard( - options: CreateShard, + options: CreateShard, ) { - // This is done for performance reasons - const calculateSafeRequestsOverwritten = options.calculateSafeRequests ?? calculateSafeRequests; - const closeOverwritten = options.close ?? close; - const connectOverwritten = options.connect ?? connect; - const identifyOverwritten = options.identify ?? identify; - const sendOverwritten = options.send ?? send; - const shutdownOverwritten = options.shutdown ?? shutdown; - const resumeOverwritten = options.resume ?? resume; - const handleCloseOverwritten = options.handleClose ?? handleClose; - const handleMessageOverwritten = options.handleMessage ?? handleMessage; - const isOpenOverwritten = options.isOpen ?? isOpen; - const startHeartbeatingOverwritten = options.startHeartbeating ?? startHeartbeating; - const stopHeartbeatingOverwritten = options.stopHeartbeating ?? stopHeartbeating; + // This is done for performance reasons + const calculateSafeRequestsOverwritten = options.calculateSafeRequests ?? calculateSafeRequests; + const closeOverwritten = options.close ?? close; + const connectOverwritten = options.connect ?? connect; + const identifyOverwritten = options.identify ?? identify; + const sendOverwritten = options.send ?? send; + const shutdownOverwritten = options.shutdown ?? shutdown; + const resumeOverwritten = options.resume ?? resume; + const handleCloseOverwritten = options.handleClose ?? handleClose; + const handleMessageOverwritten = options.handleMessage ?? handleMessage; + const isOpenOverwritten = options.isOpen ?? isOpen; + const startHeartbeatingOverwritten = options.startHeartbeating ?? startHeartbeating; + const stopHeartbeatingOverwritten = options.stopHeartbeating ?? stopHeartbeating; - return { - // ---------- - // PROPERTIES - // ---------- + return { + // ---------- + // PROPERTIES + // ---------- + + /** The gateway configuration which is used to connect to Discord. */ + gatewayConfig: { + compress: options.gatewayConfig.compress ?? false, + intents: options.gatewayConfig.intents ?? 0, + properties: { + os: options.gatewayConfig?.properties?.os ?? Deno.build.os, + browser: options.gatewayConfig?.properties?.browser ?? "Discordeno", + device: options.gatewayConfig?.properties?.device ?? "Discordeno", + }, + token: options.gatewayConfig.token, + url: options.gatewayConfig.url ?? "wss://gateway.discord.gg", + version: options.gatewayConfig.version ?? API_VERSION, + } as ShardGatewayConfig, + /** This contains all the heartbeat information */ + heart: { + acknowledged: false, + interval: DEFAULT_HEARTBEAT_INTERVAL, + } as ShardHeart, + /** Id of the shard. */ + id: options.id, + /** The maximum of requests which can be send to discord per rate limit tick. + * Typically this value should not be changed. + */ + maxRequestsPerRateLimitTick: MAX_GATEWAY_REQUESTS_PER_INTERVAL, + /** The previous payload sequence number. */ + previousSequenceNumber: options.previousSequenceNumber || null, + /** In which interval (in milliseconds) the gateway resets it's rate limit. */ + rateLimitResetInterval: GATEWAY_RATE_LIMIT_RESET_INTERVAL, + /** Current session id of the shard if present. */ + sessionId: undefined as string | undefined, + /** This contains the WebSocket connection to Discord, if currently connected. */ + socket: undefined as WebSocket | undefined, + /** Current internal state of the shard. */ + state: ShardState.Offline, + /** The total amount of shards which are used to communicate with Discord. */ + totalShards: options.totalShards, + + // ---------- + // METHODS + // ---------- + + /** The shard related event handlers. */ + events: options.events ?? {} as ShardEvents, + + /** Calculate the amount of requests which can safely be made per rate limit interval, + * before the gateway gets disconnected due to an exceeded rate limit. + */ + calculateSafeRequests: function () { + return calculateSafeRequestsOverwritten(this); + }, + + /** Close the socket connection to discord if present. */ + close: function (code: number, reason: string) { + return closeOverwritten(this, code, reason); + }, + + /** Connect the shard with the gateway and start heartbeating. + * This will not identify the shard to the gateway. + */ + connect: async function () { + return await connectOverwritten(this); + }, + + /** Identify the shard to the gateway. + * If not connected, this will also connect the shard to the gateway. + */ + identify: async function () { + return await identifyOverwritten(this); + }, + + /** Check whether the connection to Discord is currently open. */ + isOpen: function () { + return isOpenOverwritten(this); + }, + + /** Function which can be overwritten in order to get the shards presence. */ + // This function allows to be async, in case the devs create the presence based on eg. database values. + // Passing the shard's id there to make it easier for the dev to use this function. + makePresence: options.makePresence, + + /** Attempt to resume the previous shards session with the gateway. */ + resume: async function () { + return await resumeOverwritten(this); + }, + + /** Send a message to Discord. + * @param {boolean} [highPriority=false] - Whether this message should be send asap. + */ + send: async function (message: ShardSocketRequest, highPriority: boolean = false) { + return await sendOverwritten(this, message, highPriority); + }, + + /** Shutdown the shard. + * Forcefully disconnect the shard from Discord. + * The shard may not attempt to reconnect with Discord. + */ + shutdown: async function () { + return await shutdownOverwritten(this); + }, + + /** @private Internal shard bucket. + * Only access this if you know what you are doing. + * + * Bucket for handling shard request rate limits. + */ + bucket: createLeakyBucket({ + max: MAX_GATEWAY_REQUESTS_PER_INTERVAL, + refillInterval: GATEWAY_RATE_LIMIT_RESET_INTERVAL, + refillAmount: MAX_GATEWAY_REQUESTS_PER_INTERVAL, + }), + + /** @private Internal shard function. + * Only use this function if you know what you are doing. + * + * Handle a gateway connection close. + */ + handleClose: async function (close: CloseEvent) { + return await handleCloseOverwritten(this, close); + }, + + /** @private Internal shard function. + * Only use this function if you know what you are doing. + * + * Handle an incoming gateway message. + */ + handleMessage: async function (message: MessageEvent) { + return await handleMessageOverwritten(this, message); + }, + + /** This function communicates with the management process, in order to know whether its free to identify. */ + requestIdentify: async function () { + return await options.requestIdentify(this.id); + }, + + /** @private Internal state. + * Only use this if you know what you are doing. + * + * Cache for pending gateway requests which should have been send while the gateway went offline. + */ + offlineSendQueue: [] as ((_?: unknown) => void)[], + + /** @private Internal shard map. + * Only use this map if you know what you are doing. + * + * This is used to resolve internal waiting states. + * Mapped by SelectedEvents => ResolveFunction + */ + resolves: new Map<"READY" | "RESUMED" | "INVALID_SESSION", (payload: DiscordGatewayPayload) => void>(), + + /** @private Internal shard function. + * Only use this function if you know what you are doing. + * + * Start sending heartbeat payloads to Discord in the provided interval. + */ + startHeartbeating: function (interval: number) { + return startHeartbeatingOverwritten(this, interval); + }, + + /** @private Internal shard function. + * Only use this function if you know what you are doing. + * + * Stop the heartbeating process with discord. + */ + stopHeartbeating: function () { + return stopHeartbeatingOverwritten(this); + }, + }; +} + +export interface CreateShard { + /** Id of the shard which should be created. */ + id: number; + + /** Gateway configuration for the shard. */ + gatewayConfig: PickPartial; - /** The gateway configuration which is used to connect to Discord. */ - gatewayConfig: { - compress: options.gatewayConfig.compress ?? false, - intents: options.gatewayConfig.intents ?? 0, - properties: { - os: options.gatewayConfig?.properties?.os ?? Deno.build.os, - browser: options.gatewayConfig?.properties?.browser ?? "Discordeno", - device: options.gatewayConfig?.properties?.device ?? "Discordeno", - }, - token: options.gatewayConfig.token, - url: options.gatewayConfig.url ?? "wss://gateway.discord.gg", - version: options.gatewayConfig.version ?? API_VERSION, - } as ShardGatewayConfig, - /** This contains all the heartbeat information */ - heart: { - acknowledged: false, - interval: DEFAULT_HEARTBEAT_INTERVAL, - } as ShardHeart, - /** Id of the shard. */ - id: options.id, - /** The maximum of requests which can be send to discord per rate limit tick. - * Typically this value should not be changed. - */ - maxRequestsPerRateLimitTick: MAX_GATEWAY_REQUESTS_PER_INTERVAL, - /** The previous payload sequence number. */ - previousSequenceNumber: options.previousSequenceNumber || null, - /** In which interval (in milliseconds) the gateway resets it's rate limit. */ - rateLimitResetInterval: GATEWAY_RATE_LIMIT_RESET_INTERVAL, - /** Current session id of the shard if present. */ - sessionId: undefined as string | undefined, - /** This contains the WebSocket connection to Discord, if currently connected. */ - socket: undefined as WebSocket | undefined, - /** Current internal state of the shard. */ - state: ShardState.Offline, /** The total amount of shards which are used to communicate with Discord. */ - totalShards: options.totalShards, + totalShards: number; - // ---------- - // METHODS - // ---------- - - /** The shard related event handlers. */ - events: options.events ?? {} as ShardEvents, + /** This function communicates with the management process, in order to know whether its free to identify. + * When this function resolves, this means that the shard is allowed to send an identify payload to discord. + */ + requestIdentify: (shardId: number) => Promise; /** Calculate the amount of requests which can safely be made per rate limit interval, * before the gateway gets disconnected due to an exceeded rate limit. */ - calculateSafeRequests: function () { - return calculateSafeRequestsOverwritten(this); - }, + calculateSafeRequests?: typeof calculateSafeRequests; /** Close the socket connection to discord if present. */ - close: function (code: number, reason: string) { - return closeOverwritten(this, code, reason); - }, + close?: typeof close; /** Connect the shard with the gateway and start heartbeating. * This will not identify the shard to the gateway. */ - connect: async function () { - return await connectOverwritten(this); - }, - - /** Identify the shard to the gateway. - * If not connected, this will also connect the shard to the gateway. - */ - identify: async function () { - return await identifyOverwritten(this); - }, - - /** Check whether the connection to Discord is currently open. */ - isOpen: function () { - return isOpenOverwritten(this); - }, - - /** Function which can be overwritten in order to get the shards presence. */ - // This function allows to be async, in case the devs create the presence based on eg. database values. - // Passing the shard's id there to make it easier for the dev to use this function. - makePresence: options.makePresence, - - /** Attempt to resume the previous shards session with the gateway. */ - resume: async function () { - return await resumeOverwritten(this); - }, - - /** Send a message to Discord. - * @param {boolean} [highPriority=false] - Whether this message should be send asap. - */ - send: async function (message: ShardSocketRequest, highPriority: boolean = false) { - return await sendOverwritten(this, message, highPriority); - }, - - /** Shutdown the shard. - * Forcefully disconnect the shard from Discord. - * The shard may not attempt to reconnect with Discord. - */ - shutdown: async function () { - return await shutdownOverwritten(this); - }, - - /** @private Internal shard bucket. - * Only access this if you know what you are doing. - * - * Bucket for handling shard request rate limits. - */ - bucket: createLeakyBucket({ - max: MAX_GATEWAY_REQUESTS_PER_INTERVAL, - refillInterval: GATEWAY_RATE_LIMIT_RESET_INTERVAL, - refillAmount: MAX_GATEWAY_REQUESTS_PER_INTERVAL, - }), + connect?: typeof connect; /** @private Internal shard function. * Only use this function if you know what you are doing. * * Handle a gateway connection close. */ - handleClose: async function (close: CloseEvent) { - return await handleCloseOverwritten(this, close); - }, + handleClose?: typeof handleClose; /** @private Internal shard function. * Only use this function if you know what you are doing. * * Handle an incoming gateway message. */ - handleMessage: async function (message: MessageEvent) { - return await handleMessageOverwritten(this, message); - }, + handleMessage?: typeof handleMessage; - /** This function communicates with the management process, in order to know whether its free to identify. */ - requestIdentify: async function () { - return await options.requestIdentify(this.id); - }, - - /** @private Internal state. - * Only use this if you know what you are doing. - * - * Cache for pending gateway requests which should have been send while the gateway went offline. + /** Identify the shard to the gateway. + * If not connected, this will also connect the shard to the gateway. */ - offlineSendQueue: [] as ((_?: unknown) => void)[], + identify?: typeof identify; - /** @private Internal shard map. - * Only use this map if you know what you are doing. - * - * This is used to resolve internal waiting states. - * Mapped by SelectedEvents => ResolveFunction + /** Check whether the connection to Discord is currently open. */ + isOpen?: typeof isOpen; + + /** Function which can be overwritten in order to get the shards presence. */ + makePresence?(shardId: number): Promise | DiscordStatusUpdate; + + /** The maximum of requests which can be send to discord per rate limit tick. + * Typically this value should not be changed. */ - resolves: new Map<"READY" | "RESUMED" | "INVALID_SESSION", (payload: DiscordGatewayPayload) => void>(), + maxRequestsPerRateLimitTick?: number; + + /** The previous payload sequence number. */ + previousSequenceNumber?: number; + + /** In which interval (in milliseconds) the gateway resets it's rate limit. */ + rateLimitResetInterval?: number; + + /** Attempt to resume the previous shards session with the gateway. */ + resume?: typeof resume; + + /** Send a message to Discord. + * @param {boolean} [highPriority=false] - Whether this message should be send asap. + */ + send?: typeof send; + + /** Shutdown the shard. + * Forcefully disconnect the shard from Discord. + * The shard may not attempt to reconnect with Discord. + */ + shutdown?: typeof shutdown; /** @private Internal shard function. * Only use this function if you know what you are doing. * * Start sending heartbeat payloads to Discord in the provided interval. */ - startHeartbeating: function (interval: number) { - return startHeartbeatingOverwritten(this, interval); - }, + startHeartbeating?: typeof startHeartbeating; + + /** Current internal state of the shard. */ + state?: ShardState; /** @private Internal shard function. * Only use this function if you know what you are doing. * * Stop the heartbeating process with discord. */ - stopHeartbeating: function () { - return stopHeartbeatingOverwritten(this); - }, - }; -} - -export interface CreateShard { - /** Id of the shard which should be created. */ - id: number; - - /** Gateway configuration for the shard. */ - gatewayConfig: PickPartial; - - /** The total amount of shards which are used to communicate with Discord. */ - totalShards: number; - - /** This function communicates with the management process, in order to know whether its free to identify. - * When this function resolves, this means that the shard is allowed to send an identify payload to discord. - */ - requestIdentify: (shardId: number) => Promise; - - /** Calculate the amount of requests which can safely be made per rate limit interval, - * before the gateway gets disconnected due to an exceeded rate limit. - */ - calculateSafeRequests?: typeof calculateSafeRequests; - - /** Close the socket connection to discord if present. */ - close?: typeof close; - - /** Connect the shard with the gateway and start heartbeating. - * This will not identify the shard to the gateway. - */ - connect?: typeof connect; - - /** @private Internal shard function. - * Only use this function if you know what you are doing. - * - * Handle a gateway connection close. - */ - handleClose?: typeof handleClose; - - /** @private Internal shard function. - * Only use this function if you know what you are doing. - * - * Handle an incoming gateway message. - */ - handleMessage?: typeof handleMessage; - - /** Identify the shard to the gateway. - * If not connected, this will also connect the shard to the gateway. - */ - identify?: typeof identify; - - /** Check whether the connection to Discord is currently open. */ - isOpen?: typeof isOpen; - - /** Function which can be overwritten in order to get the shards presence. */ - makePresence?(shardId: number): Promise | DiscordStatusUpdate; - - /** The maximum of requests which can be send to discord per rate limit tick. - * Typically this value should not be changed. - */ - maxRequestsPerRateLimitTick?: number; - - /** The previous payload sequence number. */ - previousSequenceNumber?: number; - - /** In which interval (in milliseconds) the gateway resets it's rate limit. */ - rateLimitResetInterval?: number; - - /** Attempt to resume the previous shards session with the gateway. */ - resume?: typeof resume; - - /** Send a message to Discord. - * @param {boolean} [highPriority=false] - Whether this message should be send asap. - */ - send?: typeof send; - - /** Shutdown the shard. - * Forcefully disconnect the shard from Discord. - * The shard may not attempt to reconnect with Discord. - */ - shutdown?: typeof shutdown; - - /** @private Internal shard function. - * Only use this function if you know what you are doing. - * - * Start sending heartbeat payloads to Discord in the provided interval. - */ - startHeartbeating?: typeof startHeartbeating; - - /** Current internal state of the shard. */ - state?: ShardState; - - /** @private Internal shard function. - * Only use this function if you know what you are doing. - * - * Stop the heartbeating process with discord. - */ - stopHeartbeating?: typeof stopHeartbeating; - - /** The shard related event handlers. */ - events?: ShardEvents; - /** This contains all the heartbeat information */ - heart?: ShardHeart; - /** Bucket for handling shard request rate limits. */ - bucket?: LeakyBucket; - /** Cache for pending gateway requests which should have been send while the gateway went offline. */ - offlineSendQueue?: ShardSocketRequest[]; - /** This is used to resolve internal waiting states. - * Mapped by SelectedEvents => ResolveFunction - */ - resolves?: Shard["resolves"]; + stopHeartbeating?: typeof stopHeartbeating; + + /** The shard related event handlers. */ + events?: ShardEvents; + /** This contains all the heartbeat information */ + heart?: ShardHeart; + /** Bucket for handling shard request rate limits. */ + bucket?: LeakyBucket; + /** Cache for pending gateway requests which should have been send while the gateway went offline. */ + offlineSendQueue?: ShardSocketRequest[]; + /** This is used to resolve internal waiting states. + * Mapped by SelectedEvents => ResolveFunction + */ + resolves?: Shard["resolves"]; } diff --git a/vendor/gateway/shard/handleClose.ts b/vendor/gateway/shard/handleClose.ts index 59f8df4..02683e9 100644 --- a/vendor/gateway/shard/handleClose.ts +++ b/vendor/gateway/shard/handleClose.ts @@ -2,62 +2,62 @@ import { GatewayCloseEventCodes } from "../../types/shared.ts"; import { Shard, ShardSocketCloseCodes, ShardState } from "./types.ts"; export async function handleClose(shard: Shard, close: CloseEvent): Promise { - // gateway.debug("GW CLOSED", { shardId, payload: event }); + // gateway.debug("GW CLOSED", { shardId, payload: event }); - shard.stopHeartbeating(); + shard.stopHeartbeating(); - switch (close.code) { - case ShardSocketCloseCodes.TestingFinished: { - shard.state = ShardState.Offline; - shard.events.disconnected?.(shard); + switch (close.code) { + case ShardSocketCloseCodes.TestingFinished: { + shard.state = ShardState.Offline; + shard.events.disconnected?.(shard); - return; + return; + } + // On these codes a manual start will be done. + case ShardSocketCloseCodes.Shutdown: + case ShardSocketCloseCodes.ReIdentifying: + case ShardSocketCloseCodes.Resharded: + case ShardSocketCloseCodes.ResumeClosingOldConnection: + case ShardSocketCloseCodes.ZombiedConnection: { + shard.state = ShardState.Disconnected; + shard.events.disconnected?.(shard); + + // gateway.debug("GW CLOSED_RECONNECT", { shardId, payload: event }); + return; + } + // Gateway connection closes which require a new identify. + case GatewayCloseEventCodes.UnknownOpcode: + case GatewayCloseEventCodes.NotAuthenticated: + case GatewayCloseEventCodes.InvalidSeq: + case GatewayCloseEventCodes.RateLimited: + case GatewayCloseEventCodes.SessionTimedOut: { + shard.state = ShardState.Identifying; + shard.events.disconnected?.(shard); + + return await shard.identify(); + } + // When these codes are received something went really wrong. + // On those we cannot start a reconnect attempt. + case GatewayCloseEventCodes.AuthenticationFailed: + case GatewayCloseEventCodes.InvalidShard: + case GatewayCloseEventCodes.ShardingRequired: + case GatewayCloseEventCodes.InvalidApiVersion: + case GatewayCloseEventCodes.InvalidIntents: + case GatewayCloseEventCodes.DisallowedIntents: { + shard.state = ShardState.Offline; + shard.events.disconnected?.(shard); + + throw new Error(close.reason || "Discord gave no reason! GG! You broke Discord!"); + } + // Gateway connection closes on which a resume is allowed. + case GatewayCloseEventCodes.UnknownError: + case GatewayCloseEventCodes.DecodeError: + case GatewayCloseEventCodes.AlreadyAuthenticated: + default: { + shard.state = ShardState.Resuming; + shard.events.disconnected?.(shard); + + return await shard.resume(); + } } - // On these codes a manual start will be done. - case ShardSocketCloseCodes.Shutdown: - case ShardSocketCloseCodes.ReIdentifying: - case ShardSocketCloseCodes.Resharded: - case ShardSocketCloseCodes.ResumeClosingOldConnection: - case ShardSocketCloseCodes.ZombiedConnection: { - shard.state = ShardState.Disconnected; - shard.events.disconnected?.(shard); - - // gateway.debug("GW CLOSED_RECONNECT", { shardId, payload: event }); - return; - } - // Gateway connection closes which require a new identify. - case GatewayCloseEventCodes.UnknownOpcode: - case GatewayCloseEventCodes.NotAuthenticated: - case GatewayCloseEventCodes.InvalidSeq: - case GatewayCloseEventCodes.RateLimited: - case GatewayCloseEventCodes.SessionTimedOut: { - shard.state = ShardState.Identifying; - shard.events.disconnected?.(shard); - - return await shard.identify(); - } - // When these codes are received something went really wrong. - // On those we cannot start a reconnect attempt. - case GatewayCloseEventCodes.AuthenticationFailed: - case GatewayCloseEventCodes.InvalidShard: - case GatewayCloseEventCodes.ShardingRequired: - case GatewayCloseEventCodes.InvalidApiVersion: - case GatewayCloseEventCodes.InvalidIntents: - case GatewayCloseEventCodes.DisallowedIntents: { - shard.state = ShardState.Offline; - shard.events.disconnected?.(shard); - - throw new Error(close.reason || "Discord gave no reason! GG! You broke Discord!"); - } - // Gateway connection closes on which a resume is allowed. - case GatewayCloseEventCodes.UnknownError: - case GatewayCloseEventCodes.DecodeError: - case GatewayCloseEventCodes.AlreadyAuthenticated: - default: { - shard.state = ShardState.Resuming; - shard.events.disconnected?.(shard); - - return await shard.resume(); - } - } } diff --git a/vendor/gateway/shard/handleMessage.ts b/vendor/gateway/shard/handleMessage.ts index f1042d2..1bb3e89 100644 --- a/vendor/gateway/shard/handleMessage.ts +++ b/vendor/gateway/shard/handleMessage.ts @@ -8,149 +8,149 @@ import { GATEWAY_RATE_LIMIT_RESET_INTERVAL, Shard, ShardState } from "./types.ts const decoder = new TextDecoder(); export async function handleMessage(shard: Shard, message: MessageEvent): Promise { - message = message.data; + message = message.data; - // If message compression is enabled, - // Discord might send zlib compressed payloads. - if (shard.gatewayConfig.compress && message instanceof Blob) { - message = decompressWith( - new Uint8Array(await message.arrayBuffer()), - 0, - (slice: Uint8Array) => decoder.decode(slice), - ); - } - - // Safeguard incase decompression failed to make a string. - if (typeof message !== "string") return; - - const messageData = JSON.parse(message) as DiscordGatewayPayload; - // gateway.debug("GW RAW", { shardId, payload: messageData }); - - // TODO: remove - // console.log({ messageData: censor(messageData) }); - - switch (messageData.op) { - case GatewayOpcodes.Heartbeat: { - // TODO: can this actually happen - if (!shard.isOpen()) return; - - shard.heart.lastBeat = Date.now(); - // Discord randomly sends this requiring an immediate heartbeat back. - // Using a direct socket.send call here because heartbeat requests are reserved by us. - shard.socket?.send( - JSON.stringify({ - op: GatewayOpcodes.Heartbeat, - d: shard.previousSequenceNumber, - }), - ); - shard.events.heartbeat?.(shard); - - break; + // If message compression is enabled, + // Discord might send zlib compressed payloads. + if (shard.gatewayConfig.compress && message instanceof Blob) { + message = decompressWith( + new Uint8Array(await message.arrayBuffer()), + 0, + (slice: Uint8Array) => decoder.decode(slice), + ); } - case GatewayOpcodes.Hello: { - const interval = (messageData.d as DiscordHello).heartbeat_interval; - shard.startHeartbeating(interval); + // Safeguard incase decompression failed to make a string. + if (typeof message !== "string") return; - if (shard.state !== ShardState.Resuming) { - // HELLO has been send on a non resume action. - // This means that the shard starts a new session, - // therefore the rate limit interval has been reset too. - shard.bucket = createLeakyBucket({ - max: shard.calculateSafeRequests(), - refillInterval: GATEWAY_RATE_LIMIT_RESET_INTERVAL, - refillAmount: shard.calculateSafeRequests(), - // Waiting acquires should not be lost on a re-identify. - waiting: shard.bucket.waiting, - }); - } + const messageData = JSON.parse(message) as DiscordGatewayPayload; + // gateway.debug("GW RAW", { shardId, payload: messageData }); - shard.events.hello?.(shard); + // TODO: remove + // console.log({ messageData: censor(messageData) }); - break; + switch (messageData.op) { + case GatewayOpcodes.Heartbeat: { + // TODO: can this actually happen + if (!shard.isOpen()) return; + + shard.heart.lastBeat = Date.now(); + // Discord randomly sends this requiring an immediate heartbeat back. + // Using a direct socket.send call here because heartbeat requests are reserved by us. + shard.socket?.send( + JSON.stringify({ + op: GatewayOpcodes.Heartbeat, + d: shard.previousSequenceNumber, + }), + ); + shard.events.heartbeat?.(shard); + + break; + } + case GatewayOpcodes.Hello: { + const interval = (messageData.d as DiscordHello).heartbeat_interval; + + shard.startHeartbeating(interval); + + if (shard.state !== ShardState.Resuming) { + // HELLO has been send on a non resume action. + // This means that the shard starts a new session, + // therefore the rate limit interval has been reset too. + shard.bucket = createLeakyBucket({ + max: shard.calculateSafeRequests(), + refillInterval: GATEWAY_RATE_LIMIT_RESET_INTERVAL, + refillAmount: shard.calculateSafeRequests(), + // Waiting acquires should not be lost on a re-identify. + waiting: shard.bucket.waiting, + }); + } + + shard.events.hello?.(shard); + + break; + } + case GatewayOpcodes.HeartbeatACK: { + shard.heart.acknowledged = true; + shard.heart.lastAck = Date.now(); + // Manually calculating the round trip time for users who need it. + if (shard.heart.lastBeat) { + shard.heart.rtt = shard.heart.lastAck - shard.heart.lastBeat; + } + + shard.events.heartbeatAck?.(shard); + + break; + } + case GatewayOpcodes.Reconnect: { + // gateway.debug("GW RECONNECT", { shardId }); + + shard.events.requestedReconnect?.(shard); + + await shard.resume(); + + break; + } + case GatewayOpcodes.InvalidSession: { + // gateway.debug("GW INVALID_SESSION", { shardId, payload: messageData }); + const resumable = messageData.d as boolean; + + shard.events.invalidSession?.(shard, resumable); + + // We need to wait for a random amount of time between 1 and 5 + // Reference: https://discord.com/developers/docs/topics/gateway#resuming + await delay(Math.floor((Math.random() * 4 + 1) * 1000)); + + shard.resolves.get("INVALID_SESSION")?.(messageData); + shard.resolves.delete("INVALID_SESSION"); + + // When resumable is false we need to re-identify + if (!resumable) { + await shard.identify(); + + break; + } + + // The session is invalid but apparently it is resumable + await shard.resume(); + + break; + } } - case GatewayOpcodes.HeartbeatACK: { - shard.heart.acknowledged = true; - shard.heart.lastAck = Date.now(); - // Manually calculating the round trip time for users who need it. - if (shard.heart.lastBeat) { - shard.heart.rtt = shard.heart.lastAck - shard.heart.lastBeat; - } - shard.events.heartbeatAck?.(shard); + if (messageData.t === "RESUMED") { + // gateway.debug("GW RESUMED", { shardId }); - break; + shard.state = ShardState.Connected; + shard.events.resumed?.(shard); + + // Continue the requests which have been queued since the shard went offline. + shard.offlineSendQueue.map((resolve) => resolve()); + + shard.resolves.get("RESUMED")?.(messageData); + shard.resolves.delete("RESUMED"); + } // Important for future resumes. + else if (messageData.t === "READY") { + const payload = messageData.d as DiscordReady; + + shard.sessionId = payload.session_id; + shard.state = ShardState.Connected; + + // Continue the requests which have been queued since the shard went offline. + // Important when this is a re-identify + shard.offlineSendQueue.map((resolve) => resolve()); + + shard.resolves.get("READY")?.(messageData); + shard.resolves.delete("READY"); } - case GatewayOpcodes.Reconnect: { - // gateway.debug("GW RECONNECT", { shardId }); - shard.events.requestedReconnect?.(shard); - - await shard.resume(); - - break; + // Update the sequence number if it is present + // `s` can be either `null` or a `number`. + // In order to prevent update misses when `s` is `0` we check against null. + if (messageData.s !== null) { + shard.previousSequenceNumber = messageData.s; } - case GatewayOpcodes.InvalidSession: { - // gateway.debug("GW INVALID_SESSION", { shardId, payload: messageData }); - const resumable = messageData.d as boolean; - shard.events.invalidSession?.(shard, resumable); - - // We need to wait for a random amount of time between 1 and 5 - // Reference: https://discord.com/developers/docs/topics/gateway#resuming - await delay(Math.floor((Math.random() * 4 + 1) * 1000)); - - shard.resolves.get("INVALID_SESSION")?.(messageData); - shard.resolves.delete("INVALID_SESSION"); - - // When resumable is false we need to re-identify - if (!resumable) { - await shard.identify(); - - break; - } - - // The session is invalid but apparently it is resumable - await shard.resume(); - - break; - } - } - - if (messageData.t === "RESUMED") { - // gateway.debug("GW RESUMED", { shardId }); - - shard.state = ShardState.Connected; - shard.events.resumed?.(shard); - - // Continue the requests which have been queued since the shard went offline. - shard.offlineSendQueue.map((resolve) => resolve()); - - shard.resolves.get("RESUMED")?.(messageData); - shard.resolves.delete("RESUMED"); - } // Important for future resumes. - else if (messageData.t === "READY") { - const payload = messageData.d as DiscordReady; - - shard.sessionId = payload.session_id; - shard.state = ShardState.Connected; - - // Continue the requests which have been queued since the shard went offline. - // Important when this is a re-identify - shard.offlineSendQueue.map((resolve) => resolve()); - - shard.resolves.get("READY")?.(messageData); - shard.resolves.delete("READY"); - } - - // Update the sequence number if it is present - // `s` can be either `null` or a `number`. - // In order to prevent update misses when `s` is `0` we check against null. - if (messageData.s !== null) { - shard.previousSequenceNumber = messageData.s; - } - - // The necessary handling required for the Shards connection has been finished. - // Now the event can be safely forwarded. - shard.events.message?.(shard, messageData); + // The necessary handling required for the Shards connection has been finished. + // Now the event can be safely forwarded. + shard.events.message?.(shard, messageData); } diff --git a/vendor/gateway/shard/identify.ts b/vendor/gateway/shard/identify.ts index 6cd7589..3829101 100644 --- a/vendor/gateway/shard/identify.ts +++ b/vendor/gateway/shard/identify.ts @@ -2,49 +2,49 @@ import { GatewayOpcodes } from "../../types/shared.ts"; import { Shard, ShardSocketCloseCodes, ShardState } from "./types.ts"; export async function identify(shard: Shard): Promise { - // A new identify has been requested even though there is already a connection open. - // Therefore we need to close the old connection and heartbeating before creating a new one. - if (shard.state === ShardState.Connected) { - console.log("CLOSING EXISTING SHARD: #" + shard.id); - shard.close(ShardSocketCloseCodes.ReIdentifying, "Re-identifying closure of old connection."); - } + // A new identify has been requested even though there is already a connection open. + // Therefore we need to close the old connection and heartbeating before creating a new one. + if (shard.state === ShardState.Connected) { + console.log("CLOSING EXISTING SHARD: #" + shard.id); + shard.close(ShardSocketCloseCodes.ReIdentifying, "Re-identifying closure of old connection."); + } - shard.state = ShardState.Identifying; - shard.events.identifying?.(shard); + shard.state = ShardState.Identifying; + shard.events.identifying?.(shard); - // It is possible that the shard is in Heartbeating state but not identified, - // so check whether there is already a gateway connection existing. - // If not we need to create one before we identify. - if (!shard.isOpen()) { - await shard.connect(); - } + // It is possible that the shard is in Heartbeating state but not identified, + // so check whether there is already a gateway connection existing. + // If not we need to create one before we identify. + if (!shard.isOpen()) { + await shard.connect(); + } - // Wait until an identify is free for this shard. - await shard.requestIdentify(); + // Wait until an identify is free for this shard. + await shard.requestIdentify(); - shard.send({ - op: GatewayOpcodes.Identify, - d: { - token: `Bot ${shard.gatewayConfig.token}`, - compress: shard.gatewayConfig.compress, - properties: shard.gatewayConfig.properties, - intents: shard.gatewayConfig.intents, - shard: [shard.id, shard.totalShards], - presence: await shard.makePresence?.(shard.id), - }, - }, true); + shard.send({ + op: GatewayOpcodes.Identify, + d: { + token: `Bot ${shard.gatewayConfig.token}`, + compress: shard.gatewayConfig.compress, + properties: shard.gatewayConfig.properties, + intents: shard.gatewayConfig.intents, + shard: [shard.id, shard.totalShards], + presence: await shard.makePresence?.(shard.id), + }, + }, true); - return new Promise((resolve) => { - shard.resolves.set("READY", () => { - shard.events.identified?.(shard); - resolve(); + return new Promise((resolve) => { + shard.resolves.set("READY", () => { + shard.events.identified?.(shard); + resolve(); + }); + // When identifying too fast, + // Discord sends an invalid session payload. + // This can safely be ignored though and the shard starts a new identify action. + shard.resolves.set("INVALID_SESSION", () => { + shard.resolves.delete("READY"); + resolve(); + }); }); - // When identifying too fast, - // Discord sends an invalid session payload. - // This can safely be ignored though and the shard starts a new identify action. - shard.resolves.set("INVALID_SESSION", () => { - shard.resolves.delete("READY"); - resolve(); - }); - }); } diff --git a/vendor/gateway/shard/isOpen.ts b/vendor/gateway/shard/isOpen.ts index c5bb149..7708262 100644 --- a/vendor/gateway/shard/isOpen.ts +++ b/vendor/gateway/shard/isOpen.ts @@ -1,5 +1,5 @@ import { Shard } from "./types.ts"; export function isOpen(shard: Shard): boolean { - return shard.socket?.readyState === WebSocket.OPEN; + return shard.socket?.readyState === WebSocket.OPEN; } diff --git a/vendor/gateway/shard/resume.ts b/vendor/gateway/shard/resume.ts index 0739c90..1d46839 100644 --- a/vendor/gateway/shard/resume.ts +++ b/vendor/gateway/shard/resume.ts @@ -2,47 +2,50 @@ import { GatewayOpcodes } from "../../types/shared.ts"; import { Shard, ShardSocketCloseCodes, ShardState } from "./types.ts"; export async function resume(shard: Shard): Promise { - // gateway.debug("GW RESUMING", { shardId }); - // It has been requested to resume the Shards session. - // It's possible that the shard is still connected with Discord's gateway therefore we need to forcefully close it. - if (shard.isOpen()) { - shard.close(ShardSocketCloseCodes.ResumeClosingOldConnection, "Reconnecting the shard, closing old connection."); - } + // gateway.debug("GW RESUMING", { shardId }); + // It has been requested to resume the Shards session. + // It's possible that the shard is still connected with Discord's gateway therefore we need to forcefully close it. + if (shard.isOpen()) { + shard.close( + ShardSocketCloseCodes.ResumeClosingOldConnection, + "Reconnecting the shard, closing old connection.", + ); + } - // Shard has never identified, so we cannot resume. - if (!shard.sessionId) { - // gateway.debug( - // "GW DEBUG", - // `[Error] Trying to resume a shard (id: ${shardId}) that was not first identified.`, - // ); + // Shard has never identified, so we cannot resume. + if (!shard.sessionId) { + // gateway.debug( + // "GW DEBUG", + // `[Error] Trying to resume a shard (id: ${shardId}) that was not first identified.`, + // ); - return await shard.identify(); + return await shard.identify(); - // throw new Error(`[SHARD] Trying to resume a shard (id: ${shard.id}) which was never identified`); - } + // throw new Error(`[SHARD] Trying to resume a shard (id: ${shard.id}) which was never identified`); + } - shard.state = ShardState.Resuming; + shard.state = ShardState.Resuming; - // Before we can resume, we need to create a new connection with Discord's gateway. - await shard.connect(); + // Before we can resume, we need to create a new connection with Discord's gateway. + await shard.connect(); - shard.send({ - op: GatewayOpcodes.Resume, - d: { - token: `Bot ${shard.gatewayConfig.token}`, - session_id: shard.sessionId, - seq: shard.previousSequenceNumber ?? 0, - }, - }, true); + shard.send({ + op: GatewayOpcodes.Resume, + d: { + token: `Bot ${shard.gatewayConfig.token}`, + session_id: shard.sessionId, + seq: shard.previousSequenceNumber ?? 0, + }, + }, true); - return new Promise((resolve) => { - shard.resolves.set("RESUMED", () => resolve()); - // If it is attempted to resume with an invalid session id, - // Discord sends an invalid session payload - // Not erroring here since it is easy that this happens, also it would be not catchable - shard.resolves.set("INVALID_SESSION", () => { - shard.resolves.delete("RESUMED"); - resolve(); + return new Promise((resolve) => { + shard.resolves.set("RESUMED", () => resolve()); + // If it is attempted to resume with an invalid session id, + // Discord sends an invalid session payload + // Not erroring here since it is easy that this happens, also it would be not catchable + shard.resolves.set("INVALID_SESSION", () => { + shard.resolves.delete("RESUMED"); + resolve(); + }); }); - }); } diff --git a/vendor/gateway/shard/send.ts b/vendor/gateway/shard/send.ts index 4adc3a7..2cbaa4b 100644 --- a/vendor/gateway/shard/send.ts +++ b/vendor/gateway/shard/send.ts @@ -1,27 +1,27 @@ import { Shard, ShardSocketRequest } from "./types.ts"; async function checkOffline(shard: Shard, highPriority: boolean): Promise { - if (!shard.isOpen()) { - await new Promise((resolve) => { - if (highPriority) { - // Higher priority requests get added at the beginning of the array. - shard.offlineSendQueue.unshift(resolve); - } else { - shard.offlineSendQueue.push(resolve); - } - }); - } + if (!shard.isOpen()) { + await new Promise((resolve) => { + if (highPriority) { + // Higher priority requests get added at the beginning of the array. + shard.offlineSendQueue.unshift(resolve); + } else { + shard.offlineSendQueue.push(resolve); + } + }); + } } export async function send(shard: Shard, message: ShardSocketRequest, highPriority: boolean): Promise { - // Before acquiring a token from the bucket, check whether the shard is currently offline or not. - // Else bucket and token wait time just get wasted. - await checkOffline(shard, highPriority); + // Before acquiring a token from the bucket, check whether the shard is currently offline or not. + // Else bucket and token wait time just get wasted. + await checkOffline(shard, highPriority); - await shard.bucket.acquire(1, highPriority); + await shard.bucket.acquire(1, highPriority); - // It's possible, that the shard went offline after a token has been acquired from the bucket. - await checkOffline(shard, highPriority); + // It's possible, that the shard went offline after a token has been acquired from the bucket. + await checkOffline(shard, highPriority); - shard.socket?.send(JSON.stringify(message)); + shard.socket?.send(JSON.stringify(message)); } diff --git a/vendor/gateway/shard/shutdown.ts b/vendor/gateway/shard/shutdown.ts index 13b9d8a..c329c93 100644 --- a/vendor/gateway/shard/shutdown.ts +++ b/vendor/gateway/shard/shutdown.ts @@ -1,6 +1,6 @@ import { Shard, ShardSocketCloseCodes, ShardState } from "./types.ts"; export async function shutdown(shard: Shard): Promise { - shard.close(ShardSocketCloseCodes.Shutdown, "Shard shutting down."); - shard.state = ShardState.Offline; + shard.close(ShardSocketCloseCodes.Shutdown, "Shard shutting down."); + shard.state = ShardState.Offline; } diff --git a/vendor/gateway/shard/startHeartbeating.ts b/vendor/gateway/shard/startHeartbeating.ts index 8ee804f..dd01302 100644 --- a/vendor/gateway/shard/startHeartbeating.ts +++ b/vendor/gateway/shard/startHeartbeating.ts @@ -2,63 +2,63 @@ import { GatewayOpcodes } from "../../types/shared.ts"; import { Shard, ShardSocketCloseCodes, ShardState } from "./types.ts"; export function startHeartbeating(shard: Shard, interval: number) { - // gateway.debug("GW HEARTBEATING_STARTED", { shardId, interval }); + // gateway.debug("GW HEARTBEATING_STARTED", { shardId, interval }); - shard.heart.interval = interval; + shard.heart.interval = interval; - // Only set the shard's state to `Unidentified` - // if heartbeating has not been started due to an identify or resume action. - if ([ShardState.Disconnected, ShardState.Offline].includes(shard.state)) { - shard.state = ShardState.Unidentified; - } + // Only set the shard's state to `Unidentified` + // if heartbeating has not been started due to an identify or resume action. + if ([ShardState.Disconnected, ShardState.Offline].includes(shard.state)) { + shard.state = ShardState.Unidentified; + } - // The first heartbeat needs to be send with a random delay between `0` and `interval` - // Using a `setTimeout(_, jitter)` here to accomplish that. - // `Math.random()` can be `0` so we use `0.5` if this happens - // Reference: https://discord.com/developers/docs/topics/gateway#heartbeating - const jitter = Math.ceil(shard.heart.interval * (Math.random() || 0.5)); - shard.heart.timeoutId = setTimeout(() => { - // Using a direct socket.send call here because heartbeat requests are reserved by us. - shard.socket?.send(JSON.stringify({ - op: GatewayOpcodes.Heartbeat, - d: shard.previousSequenceNumber, - })); + // The first heartbeat needs to be send with a random delay between `0` and `interval` + // Using a `setTimeout(_, jitter)` here to accomplish that. + // `Math.random()` can be `0` so we use `0.5` if this happens + // Reference: https://discord.com/developers/docs/topics/gateway#heartbeating + const jitter = Math.ceil(shard.heart.interval * (Math.random() || 0.5)); + shard.heart.timeoutId = setTimeout(() => { + // Using a direct socket.send call here because heartbeat requests are reserved by us. + shard.socket?.send(JSON.stringify({ + op: GatewayOpcodes.Heartbeat, + d: shard.previousSequenceNumber, + })); - shard.heart.lastBeat = Date.now(); - shard.heart.acknowledged = false; + shard.heart.lastBeat = Date.now(); + shard.heart.acknowledged = false; - // After the random heartbeat jitter we can start a normal interval. - shard.heart.intervalId = setInterval(async () => { - // gateway.debug("GW DEBUG", `Running setInterval in heartbeat file. Shard: ${shardId}`); + // After the random heartbeat jitter we can start a normal interval. + shard.heart.intervalId = setInterval(async () => { + // gateway.debug("GW DEBUG", `Running setInterval in heartbeat file. Shard: ${shardId}`); - // gateway.debug("GW HEARTBEATING", { shardId, shard: currentShard }); + // gateway.debug("GW HEARTBEATING", { shardId, shard: currentShard }); - // The Shard did not receive a heartbeat ACK from Discord in time, - // therefore we have to assume that the connection has failed or got "zombied". - // The Shard needs to start a re-identify action accordingly. - // Reference: https://discord.com/developers/docs/topics/gateway#heartbeating-example-gateway-heartbeat-ack - if (!shard.heart.acknowledged) { - shard.close( - ShardSocketCloseCodes.ZombiedConnection, - "Zombied connection, did not receive an heartbeat ACK in time.", - ); + // The Shard did not receive a heartbeat ACK from Discord in time, + // therefore we have to assume that the connection has failed or got "zombied". + // The Shard needs to start a re-identify action accordingly. + // Reference: https://discord.com/developers/docs/topics/gateway#heartbeating-example-gateway-heartbeat-ack + if (!shard.heart.acknowledged) { + shard.close( + ShardSocketCloseCodes.ZombiedConnection, + "Zombied connection, did not receive an heartbeat ACK in time.", + ); - return await shard.identify(); - } + return await shard.identify(); + } - shard.heart.acknowledged = false; + shard.heart.acknowledged = false; - // Using a direct socket.send call here because heartbeat requests are reserved by us. - shard.socket?.send( - JSON.stringify({ - op: GatewayOpcodes.Heartbeat, - d: shard.previousSequenceNumber, - }), - ); + // Using a direct socket.send call here because heartbeat requests are reserved by us. + shard.socket?.send( + JSON.stringify({ + op: GatewayOpcodes.Heartbeat, + d: shard.previousSequenceNumber, + }), + ); - shard.heart.lastBeat = Date.now(); + shard.heart.lastBeat = Date.now(); - shard.events.heartbeat?.(shard); - }, shard.heart.interval); - }, jitter); + shard.events.heartbeat?.(shard); + }, shard.heart.interval); + }, jitter); } diff --git a/vendor/gateway/shard/stopHeartbeating.ts b/vendor/gateway/shard/stopHeartbeating.ts index f22739a..b2e7de8 100644 --- a/vendor/gateway/shard/stopHeartbeating.ts +++ b/vendor/gateway/shard/stopHeartbeating.ts @@ -1,9 +1,9 @@ import { Shard } from "./types.ts"; export function stopHeartbeating(shard: Shard): void { - // Clear the regular heartbeat interval. - clearInterval(shard.heart.intervalId); - // It's possible that the Shard got closed before the first jittered heartbeat. - // To go safe we should clear the related timeout too. - clearTimeout(shard.heart.timeoutId); + // Clear the regular heartbeat interval. + clearInterval(shard.heart.intervalId); + // It's possible that the Shard got closed before the first jittered heartbeat. + // To go safe we should clear the related timeout too. + clearTimeout(shard.heart.timeoutId); } diff --git a/vendor/gateway/shard/types.ts b/vendor/gateway/shard/types.ts index d7dc06e..d84cac0 100644 --- a/vendor/gateway/shard/types.ts +++ b/vendor/gateway/shard/types.ts @@ -11,138 +11,138 @@ export const DEFAULT_HEARTBEAT_INTERVAL = 45000; export type Shard = ReturnType; export enum ShardState { - /** Shard is fully connected to the gateway and receiving events from Discord. */ - Connected = 0, - /** Shard started to connect to the gateway. - * This is only used if the shard is not currently trying to identify or resume. - */ - Connecting = 1, - /** Shard got disconnected and reconnection actions have been started. */ - Disconnected = 2, - /** The shard is connected to the gateway but only heartbeating. - * At this state the shard has not been identified with discord. - */ - Unidentified = 3, - /** Shard is trying to identify with the gateway to create a new session. */ - Identifying = 4, - /** Shard is trying to resume a session with the gateway. */ - Resuming = 5, - /** Shard got shut down studied or due to a not (self) fixable error and may not attempt to reconnect on its own. */ - Offline = 6, + /** Shard is fully connected to the gateway and receiving events from Discord. */ + Connected = 0, + /** Shard started to connect to the gateway. + * This is only used if the shard is not currently trying to identify or resume. + */ + Connecting = 1, + /** Shard got disconnected and reconnection actions have been started. */ + Disconnected = 2, + /** The shard is connected to the gateway but only heartbeating. + * At this state the shard has not been identified with discord. + */ + Unidentified = 3, + /** Shard is trying to identify with the gateway to create a new session. */ + Identifying = 4, + /** Shard is trying to resume a session with the gateway. */ + Resuming = 5, + /** Shard got shut down studied or due to a not (self) fixable error and may not attempt to reconnect on its own. */ + Offline = 6, } export interface ShardGatewayConfig { - /** Whether incoming payloads are compressed using zlib. - * - * @default false - */ - compress: boolean; - /** The calculated intent value of the events which the shard should receive. - * - * @default 0 - */ - intents: number; - /** Identify properties to use */ - properties: { - /** Operating system the shard runs on. + /** Whether incoming payloads are compressed using zlib. * - * @default "darwin" | "linux" | "windows" + * @default false */ - os: string; - /** The "browser" where this shard is running on. + compress: boolean; + /** The calculated intent value of the events which the shard should receive. * - * @default "Discordeno" + * @default 0 */ - browser: string; - /** The device on which the shard is running. + intents: number; + /** Identify properties to use */ + properties: { + /** Operating system the shard runs on. + * + * @default "darwin" | "linux" | "windows" + */ + os: string; + /** The "browser" where this shard is running on. + * + * @default "Discordeno" + */ + browser: string; + /** The device on which the shard is running. + * + * @default "Discordeno" + */ + device: string; + }; + /** Bot token which is used to connect to Discord */ + token: string; + /** The URL of the gateway which should be connected to. * - * @default "Discordeno" + * @default "wss://gateway.discord.gg" */ - device: string; - }; - /** Bot token which is used to connect to Discord */ - token: string; - /** The URL of the gateway which should be connected to. - * - * @default "wss://gateway.discord.gg" - */ - url: string; - /** The gateway version which should be used. - * - * @default 10 - */ - version: number; + url: string; + /** The gateway version which should be used. + * + * @default 10 + */ + version: number; } export interface ShardHeart { - /** Whether or not the heartbeat was acknowledged by Discord in time. */ - acknowledged: boolean; - /** Interval between heartbeats requested by Discord. */ - interval: number; - /** Id of the interval, which is used for sending the heartbeats. */ - intervalId?: number; - /** Unix (in milliseconds) timestamp when the last heartbeat ACK was received from Discord. */ - lastAck?: number; - /** Unix timestamp (in milliseconds) when the last heartbeat was sent. */ - lastBeat?: number; - /** Round trip time (in milliseconds) from Shard to Discord and back. - * Calculated using the heartbeat system. - * Note: this value is undefined until the first heartbeat to Discord has happened. - */ - rtt?: number; - /** Id of the timeout which is used for sending the first heartbeat to Discord since it's "special". */ - timeoutId?: number; + /** Whether or not the heartbeat was acknowledged by Discord in time. */ + acknowledged: boolean; + /** Interval between heartbeats requested by Discord. */ + interval: number; + /** Id of the interval, which is used for sending the heartbeats. */ + intervalId?: number; + /** Unix (in milliseconds) timestamp when the last heartbeat ACK was received from Discord. */ + lastAck?: number; + /** Unix timestamp (in milliseconds) when the last heartbeat was sent. */ + lastBeat?: number; + /** Round trip time (in milliseconds) from Shard to Discord and back. + * Calculated using the heartbeat system. + * Note: this value is undefined until the first heartbeat to Discord has happened. + */ + rtt?: number; + /** Id of the timeout which is used for sending the first heartbeat to Discord since it's "special". */ + timeoutId?: number; } export interface ShardEvents { - /** A heartbeat has been send. */ - heartbeat?(shard: Shard): unknown; - /** A heartbeat ACK was received. */ - heartbeatAck?(shard: Shard): unknown; - /** Shard has received a Hello payload. */ - hello?(shard: Shard): unknown; - /** The Shards session has been invalidated. */ - invalidSession?(shard: Shard, resumable: boolean): unknown; - /** The shard has started a resume action. */ - resuming?(shard: Shard): unknown; - /** The shard has successfully resumed an old session. */ - resumed?(shard: Shard): unknown; - /** Discord has requested the Shard to reconnect. */ - requestedReconnect?(shard: Shard): unknown; - /** The shard started to connect to Discord's gateway. */ - connecting?(shard: Shard): unknown; - /** The shard is connected with Discord's gateway. */ - connected?(shard: Shard): unknown; - /** The shard has been disconnected from Discord's gateway. */ - disconnected?(shard: Shard): unknown; - /** The shard has started to identify itself to Discord. */ - identifying?(shard: Shard): unknown; - /** The shard has successfully been identified itself with Discord. */ - identified?(shard: Shard): unknown; - /** The shard has received a message from Discord. */ - message?(shard: Shard, payload: DiscordGatewayPayload): unknown; + /** A heartbeat has been send. */ + heartbeat?(shard: Shard): unknown; + /** A heartbeat ACK was received. */ + heartbeatAck?(shard: Shard): unknown; + /** Shard has received a Hello payload. */ + hello?(shard: Shard): unknown; + /** The Shards session has been invalidated. */ + invalidSession?(shard: Shard, resumable: boolean): unknown; + /** The shard has started a resume action. */ + resuming?(shard: Shard): unknown; + /** The shard has successfully resumed an old session. */ + resumed?(shard: Shard): unknown; + /** Discord has requested the Shard to reconnect. */ + requestedReconnect?(shard: Shard): unknown; + /** The shard started to connect to Discord's gateway. */ + connecting?(shard: Shard): unknown; + /** The shard is connected with Discord's gateway. */ + connected?(shard: Shard): unknown; + /** The shard has been disconnected from Discord's gateway. */ + disconnected?(shard: Shard): unknown; + /** The shard has started to identify itself to Discord. */ + identifying?(shard: Shard): unknown; + /** The shard has successfully been identified itself with Discord. */ + identified?(shard: Shard): unknown; + /** The shard has received a message from Discord. */ + message?(shard: Shard, payload: DiscordGatewayPayload): unknown; } export enum ShardSocketCloseCodes { - /** A regular Shard shutdown. */ - Shutdown = 3000, - /** A resume has been requested and therefore the old connection needs to be closed. */ - ResumeClosingOldConnection = 3024, - /** Did not receive a heartbeat ACK in time. - * Closing the shard and creating a new session. - */ - ZombiedConnection = 3010, - /** Discordeno's gateway tests hae been finished, therefore the Shard can be turned off. */ - TestingFinished = 3064, - /** Special close code reserved for Discordeno's zero-downtime resharding system. */ - Resharded = 3065, - /** Shard is re-identifying therefore the old connection needs to be closed. */ - ReIdentifying = 3066, + /** A regular Shard shutdown. */ + Shutdown = 3000, + /** A resume has been requested and therefore the old connection needs to be closed. */ + ResumeClosingOldConnection = 3024, + /** Did not receive a heartbeat ACK in time. + * Closing the shard and creating a new session. + */ + ZombiedConnection = 3010, + /** Discordeno's gateway tests hae been finished, therefore the Shard can be turned off. */ + TestingFinished = 3064, + /** Special close code reserved for Discordeno's zero-downtime resharding system. */ + Resharded = 3065, + /** Shard is re-identifying therefore the old connection needs to be closed. */ + ReIdentifying = 3066, } export interface ShardSocketRequest { - /** The OP-Code for the payload to send. */ - op: GatewayOpcodes; - /** Payload data. */ - d: unknown; + /** The OP-Code for the payload to send. */ + op: GatewayOpcodes; + /** Payload data. */ + d: unknown; } diff --git a/vendor/rest/checkRateLimits.ts b/vendor/rest/checkRateLimits.ts index f984a40..77514ec 100644 --- a/vendor/rest/checkRateLimits.ts +++ b/vendor/rest/checkRateLimits.ts @@ -2,16 +2,16 @@ import { RestManager } from "./restManager.ts"; /** Check the rate limits for a url or a bucket. */ export function checkRateLimits(rest: RestManager, url: string) { - const ratelimited = rest.rateLimitedPaths.get(url); - const global = rest.rateLimitedPaths.get("global"); - const now = Date.now(); + const ratelimited = rest.rateLimitedPaths.get(url); + const global = rest.rateLimitedPaths.get("global"); + const now = Date.now(); - if (ratelimited && now < ratelimited.resetTimestamp) { - return ratelimited.resetTimestamp - now; - } - if (global && now < global.resetTimestamp) { - return global.resetTimestamp - now; - } + if (ratelimited && now < ratelimited.resetTimestamp) { + return ratelimited.resetTimestamp - now; + } + if (global && now < global.resetTimestamp) { + return global.resetTimestamp - now; + } - return false; + return false; } diff --git a/vendor/rest/cleanupQueues.ts b/vendor/rest/cleanupQueues.ts index e4bee91..3ea9d53 100644 --- a/vendor/rest/cleanupQueues.ts +++ b/vendor/rest/cleanupQueues.ts @@ -2,13 +2,13 @@ import { RestManager } from "./restManager.ts"; /** Cleans up the queues by checking if there is nothing left and removing it. */ export function cleanupQueues(rest: RestManager) { - for (const [key, queue] of rest.pathQueues) { - rest.debug(`[REST - cleanupQueues] Running for of loop. ${key}`); - if (queue.requests.length) continue; - // REMOVE IT FROM CACHE - rest.pathQueues.delete(key); - } + for (const [key, queue] of rest.pathQueues) { + rest.debug(`[REST - cleanupQueues] Running for of loop. ${key}`); + if (queue.requests.length) continue; + // REMOVE IT FROM CACHE + rest.pathQueues.delete(key); + } - // NO QUEUE LEFT, DISABLE THE QUEUE - if (!rest.pathQueues.size) rest.processingQueue = false; + // NO QUEUE LEFT, DISABLE THE QUEUE + if (!rest.pathQueues.size) rest.processingQueue = false; } diff --git a/vendor/rest/convertRestError.ts b/vendor/rest/convertRestError.ts index b36a263..eb7cd08 100644 --- a/vendor/rest/convertRestError.ts +++ b/vendor/rest/convertRestError.ts @@ -1,6 +1,6 @@ import { RestRequestRejection } from "./rest.ts"; export function convertRestError(errorStack: Error, data: RestRequestRejection): Error { - errorStack.message = `[${data.status}] ${data.error}\n${data.body}`; - return errorStack; + errorStack.message = `[${data.status}] ${data.error}\n${data.body}`; + return errorStack; } diff --git a/vendor/rest/createRequestBody.ts b/vendor/rest/createRequestBody.ts index 9781c7a..3e43e82 100644 --- a/vendor/rest/createRequestBody.ts +++ b/vendor/rest/createRequestBody.ts @@ -6,62 +6,62 @@ import { RequestMethod, RestPayload, RestRequest } from "./rest.ts"; /** Creates the request body and headers that are necessary to send a request. Will handle different types of methods and everything necessary for discord. */ // export function createRequestBody(rest: RestManager, queuedRequest: { request: RestRequest; payload: RestPayload }) { export function createRequestBody(rest: RestManager, options: CreateRequestBodyOptions) { - const headers: Record = { - "user-agent": USER_AGENT, - }; + const headers: Record = { + "user-agent": USER_AGENT, + }; - if (!options.unauthorized) headers["authorization"] = `Bot ${rest.token}`; + if (!options.unauthorized) headers["authorization"] = `Bot ${rest.token}`; - // SOMETIMES SPECIAL HEADERS (E.G. CUSTOM AUTHORIZATION) NEED TO BE USED - if (options.headers) { - for (const key in options.headers) { - headers[key.toLowerCase()] = options.headers[key]; - } - } - - // GET METHODS SHOULD NOT HAVE A BODY - if (options.method === "GET") { - options.body = undefined; - } - - // IF A REASON IS PROVIDED ENCODE IT IN HEADERS - if (options.body?.reason) { - headers["X-Audit-Log-Reason"] = encodeURIComponent(options.body.reason as string); - options.body.reason = undefined; - } - - // IF A FILE/ATTACHMENT IS PRESENT WE NEED SPECIAL HANDLING - if (options.body?.file) { - if (!Array.isArray(options.body.file)) { - options.body.file = [options.body.file]; + // SOMETIMES SPECIAL HEADERS (E.G. CUSTOM AUTHORIZATION) NEED TO BE USED + if (options.headers) { + for (const key in options.headers) { + headers[key.toLowerCase()] = options.headers[key]; + } } - const form = new FormData(); - - for (let i = 0; i < (options.body.file as FileContent[]).length; i++) { - form.append( - `file${i}`, - (options.body.file as FileContent[])[i].blob, - (options.body.file as FileContent[])[i].name, - ); + // GET METHODS SHOULD NOT HAVE A BODY + if (options.method === "GET") { + options.body = undefined; } - form.append("payload_json", JSON.stringify({ ...options.body, file: undefined })); - options.body.file = form; - } else if (options.body && !["GET", "DELETE"].includes(options.method)) { - headers["Content-Type"] = "application/json"; - } + // IF A REASON IS PROVIDED ENCODE IT IN HEADERS + if (options.body?.reason) { + headers["X-Audit-Log-Reason"] = encodeURIComponent(options.body.reason as string); + options.body.reason = undefined; + } - return { - headers, - body: (options.body?.file ?? JSON.stringify(options.body)) as FormData | string, - method: options.method, - }; + // IF A FILE/ATTACHMENT IS PRESENT WE NEED SPECIAL HANDLING + if (options.body?.file) { + if (!Array.isArray(options.body.file)) { + options.body.file = [options.body.file]; + } + + const form = new FormData(); + + for (let i = 0; i < (options.body.file as FileContent[]).length; i++) { + form.append( + `file${i}`, + (options.body.file as FileContent[])[i].blob, + (options.body.file as FileContent[])[i].name, + ); + } + + form.append("payload_json", JSON.stringify({ ...options.body, file: undefined })); + options.body.file = form; + } else if (options.body && !["GET", "DELETE"].includes(options.method)) { + headers["Content-Type"] = "application/json"; + } + + return { + headers, + body: (options.body?.file ?? JSON.stringify(options.body)) as FormData | string, + method: options.method, + }; } export interface CreateRequestBodyOptions { - headers?: Record; - method: RequestMethod; - body?: Record; - unauthorized?: boolean; + headers?: Record; + method: RequestMethod; + body?: Record; + unauthorized?: boolean; } diff --git a/vendor/rest/processGlobalQueue.ts b/vendor/rest/processGlobalQueue.ts index 8877ad8..4d7932a 100644 --- a/vendor/rest/processGlobalQueue.ts +++ b/vendor/rest/processGlobalQueue.ts @@ -2,80 +2,80 @@ import { RestManager } from "./restManager.ts"; import { HTTPResponseCodes } from "../types/shared.ts"; export async function processGlobalQueue(rest: RestManager) { - // IF QUEUE IS EMPTY EXIT - if (!rest.globalQueue.length) return; - // IF QUEUE IS ALREADY RUNNING EXIT - if (rest.globalQueueProcessing) return; + // IF QUEUE IS EMPTY EXIT + if (!rest.globalQueue.length) return; + // IF QUEUE IS ALREADY RUNNING EXIT + if (rest.globalQueueProcessing) return; - // SET AS TRUE SO OTHER QUEUES DON'T START - rest.globalQueueProcessing = true; + // SET AS TRUE SO OTHER QUEUES DON'T START + rest.globalQueueProcessing = true; - while (rest.globalQueue.length) { - // IF THE BOT IS GLOBALLY RATE LIMITED TRY AGAIN - if (rest.globallyRateLimited) { - setTimeout(() => { - rest.debug(`[REST - processGlobalQueue] Globally rate limited, running setTimeout.`); - rest.processGlobalQueue(rest); - }, 1000); + while (rest.globalQueue.length) { + // IF THE BOT IS GLOBALLY RATE LIMITED TRY AGAIN + if (rest.globallyRateLimited) { + setTimeout(() => { + rest.debug(`[REST - processGlobalQueue] Globally rate limited, running setTimeout.`); + rest.processGlobalQueue(rest); + }, 1000); - // BREAK WHILE LOOP - break; + // BREAK WHILE LOOP + break; + } + + if (rest.invalidRequests === rest.maxInvalidRequests - rest.invalidRequestsSafetyAmount) { + setTimeout(() => { + const time = rest.invalidRequestsInterval - (Date.now() - rest.invalidRequestFrozenAt); + rest.debug( + `[REST - processGlobalQueue] Freeze global queue because of invalid requests. Time Remaining: ${ + time / 1000 + } seconds.`, + ); + rest.processGlobalQueue(rest); + }, 1000); + + // BREAK WHILE LOOP + break; + } + + const request = rest.globalQueue.shift(); + // REMOVES ANY POTENTIAL INVALID CONFLICTS + if (!request) continue; + + // CHECK RATE LIMITS FOR 429 REPEATS + // IF THIS URL IS STILL RATE LIMITED, TRY AGAIN + const urlResetIn = rest.checkRateLimits(rest, request.basicURL); + // IF A BUCKET EXISTS, CHECK THE BUCKET'S RATE LIMITS + const bucketResetIn = request.payload.bucketId ? rest.checkRateLimits(rest, request.payload.bucketId) : false; + + if (urlResetIn || bucketResetIn) { + // ONLY ADD TIMEOUT IF ANOTHER QUEUE IS NOT PENDING + setTimeout(() => { + rest.debug(`[REST - processGlobalQueue] rate limited, running setTimeout.`); + // THIS REST IS RATE LIMITED, SO PUSH BACK TO START + rest.globalQueue.unshift(request); + // START QUEUE IF NOT STARTED + rest.processGlobalQueue(rest); + }, urlResetIn || (bucketResetIn as number)); + + continue; + } + + await rest.sendRequest(rest, { + url: request.urlToUse, + method: request.request.method, + bucketId: request.payload.bucketId, + reject: request.request.reject, + respond: request.request.respond, + retryCount: request.payload.retryCount ?? 0, + payload: rest.createRequestBody(rest, { + method: request.request.method, + body: request.payload.body, + }), + }) + // Should be handled in sendRequest, this catch just prevents bots from dying + .catch(() => null); } - if (rest.invalidRequests === rest.maxInvalidRequests - rest.invalidRequestsSafetyAmount) { - setTimeout(() => { - const time = rest.invalidRequestsInterval - (Date.now() - rest.invalidRequestFrozenAt); - rest.debug( - `[REST - processGlobalQueue] Freeze global queue because of invalid requests. Time Remaining: ${ - time / 1000 - } seconds.`, - ); - rest.processGlobalQueue(rest); - }, 1000); - - // BREAK WHILE LOOP - break; - } - - const request = rest.globalQueue.shift(); - // REMOVES ANY POTENTIAL INVALID CONFLICTS - if (!request) continue; - - // CHECK RATE LIMITS FOR 429 REPEATS - // IF THIS URL IS STILL RATE LIMITED, TRY AGAIN - const urlResetIn = rest.checkRateLimits(rest, request.basicURL); - // IF A BUCKET EXISTS, CHECK THE BUCKET'S RATE LIMITS - const bucketResetIn = request.payload.bucketId ? rest.checkRateLimits(rest, request.payload.bucketId) : false; - - if (urlResetIn || bucketResetIn) { - // ONLY ADD TIMEOUT IF ANOTHER QUEUE IS NOT PENDING - setTimeout(() => { - rest.debug(`[REST - processGlobalQueue] rate limited, running setTimeout.`); - // THIS REST IS RATE LIMITED, SO PUSH BACK TO START - rest.globalQueue.unshift(request); - // START QUEUE IF NOT STARTED - rest.processGlobalQueue(rest); - }, urlResetIn || (bucketResetIn as number)); - - continue; - } - - await rest.sendRequest(rest, { - url: request.urlToUse, - method: request.request.method, - bucketId: request.payload.bucketId, - reject: request.request.reject, - respond: request.request.respond, - retryCount: request.payload.retryCount ?? 0, - payload: rest.createRequestBody(rest, { - method: request.request.method, - body: request.payload.body, - }), - }) - // Should be handled in sendRequest, this catch just prevents bots from dying - .catch(() => null); - } - - // ALLOW OTHER QUEUES TO START WHEN NEW REQUEST IS MADE - rest.globalQueueProcessing = false; + // ALLOW OTHER QUEUES TO START WHEN NEW REQUEST IS MADE + rest.globalQueueProcessing = false; } diff --git a/vendor/rest/processQueue.ts b/vendor/rest/processQueue.ts index c58d361..0ac08b7 100644 --- a/vendor/rest/processQueue.ts +++ b/vendor/rest/processQueue.ts @@ -2,56 +2,56 @@ import { RestManager } from "./restManager.ts"; /** Processes the queue by looping over each path separately until the queues are empty. */ export function processQueue(rest: RestManager, id: string) { - const queue = rest.pathQueues.get(id); - if (!queue) return; + const queue = rest.pathQueues.get(id); + if (!queue) return; - while (queue.requests.length) { - rest.debug(`[REST - processQueue] Running while loop.`); - // SELECT THE FIRST ITEM FROM THIS QUEUE - const queuedRequest = queue.requests[0]; - // IF THIS DOESN'T HAVE ANY ITEMS JUST CANCEL, THE CLEANER WILL REMOVE IT. - if (!queuedRequest) break; + while (queue.requests.length) { + rest.debug(`[REST - processQueue] Running while loop.`); + // SELECT THE FIRST ITEM FROM THIS QUEUE + const queuedRequest = queue.requests[0]; + // IF THIS DOESN'T HAVE ANY ITEMS JUST CANCEL, THE CLEANER WILL REMOVE IT. + if (!queuedRequest) break; - const basicURL = rest.simplifyUrl(queuedRequest.request.url, queuedRequest.request.method); + const basicURL = rest.simplifyUrl(queuedRequest.request.url, queuedRequest.request.method); - // IF THIS URL IS STILL RATE LIMITED, TRY AGAIN - const urlResetIn = rest.checkRateLimits(rest, basicURL); - if (urlResetIn) { - // ONLY ADD TIMEOUT IF ANOTHER QUEUE IS NOT PENDING - if (!queue.isWaiting) { - queue.isWaiting = true; + // IF THIS URL IS STILL RATE LIMITED, TRY AGAIN + const urlResetIn = rest.checkRateLimits(rest, basicURL); + if (urlResetIn) { + // ONLY ADD TIMEOUT IF ANOTHER QUEUE IS NOT PENDING + if (!queue.isWaiting) { + queue.isWaiting = true; - setTimeout(() => { - queue.isWaiting = false; + setTimeout(() => { + queue.isWaiting = false; - rest.debug(`[REST - processQueue] rate limited, running setTimeout.`); - rest.processQueue(rest, id); - }, urlResetIn); - } + rest.debug(`[REST - processQueue] rate limited, running setTimeout.`); + rest.processQueue(rest, id); + }, urlResetIn); + } - // BREAK WHILE LOOP - break; + // BREAK WHILE LOOP + break; + } + + // IF A BUCKET EXISTS, CHECK THE BUCKET'S RATE LIMITS + const bucketResetIn = queuedRequest.payload.bucketId + ? rest.checkRateLimits(rest, queuedRequest.payload.bucketId) + : false; + // THIS BUCKET IS STILL RATE LIMITED, RE-ADD TO QUEUE + if (bucketResetIn) continue; + // EXECUTE THE REQUEST + + // CUSTOM HANDLER FOR USER TO LOG OR WHATEVER WHENEVER A FETCH IS MADE + rest.debug(`[REST - Add To Global Queue] ${JSON.stringify(queuedRequest.payload)}`); + rest.globalQueue.push({ + ...queuedRequest, + urlToUse: queuedRequest.request.url, + basicURL, + }); + rest.processGlobalQueue(rest); + queue.requests.shift(); } - // IF A BUCKET EXISTS, CHECK THE BUCKET'S RATE LIMITS - const bucketResetIn = queuedRequest.payload.bucketId - ? rest.checkRateLimits(rest, queuedRequest.payload.bucketId) - : false; - // THIS BUCKET IS STILL RATE LIMITED, RE-ADD TO QUEUE - if (bucketResetIn) continue; - // EXECUTE THE REQUEST - - // CUSTOM HANDLER FOR USER TO LOG OR WHATEVER WHENEVER A FETCH IS MADE - rest.debug(`[REST - Add To Global Queue] ${JSON.stringify(queuedRequest.payload)}`); - rest.globalQueue.push({ - ...queuedRequest, - urlToUse: queuedRequest.request.url, - basicURL, - }); - rest.processGlobalQueue(rest); - queue.requests.shift(); - } - - // ONCE QUEUE IS DONE, WE CAN TRY CLEANING UP - rest.cleanupQueues(rest); + // ONCE QUEUE IS DONE, WE CAN TRY CLEANING UP + rest.cleanupQueues(rest); } diff --git a/vendor/rest/processRateLimitedPaths.ts b/vendor/rest/processRateLimitedPaths.ts index 14ad0ca..eae47cb 100644 --- a/vendor/rest/processRateLimitedPaths.ts +++ b/vendor/rest/processRateLimitedPaths.ts @@ -2,28 +2,28 @@ import { RestManager } from "./restManager.ts"; /** This will create a infinite loop running in 1 seconds using tail recursion to keep rate limits clean. When a rate limit resets, this will remove it so the queue can proceed. */ export function processRateLimitedPaths(rest: RestManager) { - const now = Date.now(); + const now = Date.now(); - for (const [key, value] of rest.rateLimitedPaths.entries()) { - rest.debug(`[REST - processRateLimitedPaths] Running for of loop. ${value.resetTimestamp - now}`); - // IF THE TIME HAS NOT REACHED CANCEL - if (value.resetTimestamp > now) continue; + for (const [key, value] of rest.rateLimitedPaths.entries()) { + rest.debug(`[REST - processRateLimitedPaths] Running for of loop. ${value.resetTimestamp - now}`); + // IF THE TIME HAS NOT REACHED CANCEL + if (value.resetTimestamp > now) continue; - // RATE LIMIT IS OVER, DELETE THE RATE LIMITER - rest.rateLimitedPaths.delete(key); - // IF IT WAS GLOBAL ALSO MARK THE GLOBAL VALUE AS FALSE - if (key === "global") rest.globallyRateLimited = false; - } + // RATE LIMIT IS OVER, DELETE THE RATE LIMITER + rest.rateLimitedPaths.delete(key); + // IF IT WAS GLOBAL ALSO MARK THE GLOBAL VALUE AS FALSE + if (key === "global") rest.globallyRateLimited = false; + } - // ALL PATHS ARE CLEARED CAN CANCEL OUT! - if (!rest.rateLimitedPaths.size) { - rest.processingRateLimitedPaths = false; - } else { - rest.processingRateLimitedPaths = true; - // RECHECK IN 1 SECOND - setTimeout(() => { - rest.debug(`[REST - processRateLimitedPaths] Running setTimeout.`); - rest.processRateLimitedPaths(rest); - }, 1000); - } + // ALL PATHS ARE CLEARED CAN CANCEL OUT! + if (!rest.rateLimitedPaths.size) { + rest.processingRateLimitedPaths = false; + } else { + rest.processingRateLimitedPaths = true; + // RECHECK IN 1 SECOND + setTimeout(() => { + rest.debug(`[REST - processRateLimitedPaths] Running setTimeout.`); + rest.processRateLimitedPaths(rest); + }, 1000); + } } diff --git a/vendor/rest/processRequest.ts b/vendor/rest/processRequest.ts index 1635074..5a72b1d 100644 --- a/vendor/rest/processRequest.ts +++ b/vendor/rest/processRequest.ts @@ -4,33 +4,33 @@ import { RestPayload, RestRequest } from "./rest.ts"; /** Processes a request and assigns it to a queue or creates a queue if none exists for it. */ export function processRequest(rest: RestManager, request: RestRequest, payload: RestPayload) { - const route = request.url.substring(request.url.indexOf("api/")); - const parts = route.split("/"); - // REMOVE THE API - parts.shift(); - // REMOVES THE VERSION NUMBER - if (parts[0]?.startsWith("v")) parts.shift(); - // SET THE NEW REQUEST URL - request.url = `${BASE_URL}/v${rest.version}/${parts.join("/")}`; - // REMOVE THE MAJOR PARAM - parts.shift(); + const route = request.url.substring(request.url.indexOf("api/")); + const parts = route.split("/"); + // REMOVE THE API + parts.shift(); + // REMOVES THE VERSION NUMBER + if (parts[0]?.startsWith("v")) parts.shift(); + // SET THE NEW REQUEST URL + request.url = `${BASE_URL}/v${rest.version}/${parts.join("/")}`; + // REMOVE THE MAJOR PARAM + parts.shift(); - const url = rest.simplifyUrl(request.url, request.method); + const url = rest.simplifyUrl(request.url, request.method); - const queue = rest.pathQueues.get(url); - if (queue) { - queue.requests.push({ request, payload }); - } else { - // CREATES A NEW QUEUE - rest.pathQueues.set(url, { - isWaiting: false, - requests: [ - { - request, - payload, - }, - ], - }); - rest.processQueue(rest, url); - } + const queue = rest.pathQueues.get(url); + if (queue) { + queue.requests.push({ request, payload }); + } else { + // CREATES A NEW QUEUE + rest.pathQueues.set(url, { + isWaiting: false, + requests: [ + { + request, + payload, + }, + ], + }); + rest.processQueue(rest, url); + } } diff --git a/vendor/rest/processRequestHeaders.ts b/vendor/rest/processRequestHeaders.ts index 3d943e2..2385792 100644 --- a/vendor/rest/processRequestHeaders.ts +++ b/vendor/rest/processRequestHeaders.ts @@ -2,62 +2,62 @@ import { RestManager } from "./restManager.ts"; /** Processes the rate limit headers and determines if it needs to be rate limited and returns the bucket id if available */ export function processRequestHeaders(rest: RestManager, url: string, headers: Headers) { - let rateLimited = false; + let rateLimited = false; - // GET ALL NECESSARY HEADERS - const remaining = headers.get("x-ratelimit-remaining"); - const retryAfter = headers.get("x-ratelimit-reset-after"); - const reset = Date.now() + Number(retryAfter) * 1000; - const global = headers.get("x-ratelimit-global"); - // undefined override null needed for typings - const bucketId = headers.get("x-ratelimit-bucket") || undefined; + // GET ALL NECESSARY HEADERS + const remaining = headers.get("x-ratelimit-remaining"); + const retryAfter = headers.get("x-ratelimit-reset-after"); + const reset = Date.now() + Number(retryAfter) * 1000; + const global = headers.get("x-ratelimit-global"); + // undefined override null needed for typings + const bucketId = headers.get("x-ratelimit-bucket") || undefined; - // IF THERE IS NO REMAINING RATE LIMIT, MARK IT AS RATE LIMITED - if (remaining === "0") { - rateLimited = true; + // IF THERE IS NO REMAINING RATE LIMIT, MARK IT AS RATE LIMITED + if (remaining === "0") { + rateLimited = true; - // SAVE THE URL AS LIMITED, IMPORTANT FOR NEW REQUESTS BY USER WITHOUT BUCKET - rest.rateLimitedPaths.set(url, { - url, - resetTimestamp: reset, - bucketId, - }); + // SAVE THE URL AS LIMITED, IMPORTANT FOR NEW REQUESTS BY USER WITHOUT BUCKET + rest.rateLimitedPaths.set(url, { + url, + resetTimestamp: reset, + bucketId, + }); - // SAVE THE BUCKET AS LIMITED SINCE DIFFERENT URLS MAY SHARE A BUCKET - if (bucketId) { - rest.rateLimitedPaths.set(bucketId, { - url, - resetTimestamp: reset, - bucketId, - }); + // SAVE THE BUCKET AS LIMITED SINCE DIFFERENT URLS MAY SHARE A BUCKET + if (bucketId) { + rest.rateLimitedPaths.set(bucketId, { + url, + resetTimestamp: reset, + bucketId, + }); + } } - } - // IF THERE IS NO REMAINING GLOBAL LIMIT, MARK IT RATE LIMITED GLOBALLY - if (global) { - const retryAfter = headers.get("retry-after"); - const globalReset = Date.now() + Number(retryAfter) * 1000; - rest.debug(`[REST = Globally Rate Limited] URL: ${url} | Global Rest: ${globalReset}`); - rest.globallyRateLimited = true; - rateLimited = true; + // IF THERE IS NO REMAINING GLOBAL LIMIT, MARK IT RATE LIMITED GLOBALLY + if (global) { + const retryAfter = headers.get("retry-after"); + const globalReset = Date.now() + Number(retryAfter) * 1000; + rest.debug(`[REST = Globally Rate Limited] URL: ${url} | Global Rest: ${globalReset}`); + rest.globallyRateLimited = true; + rateLimited = true; - rest.rateLimitedPaths.set("global", { - url: "global", - resetTimestamp: globalReset, - bucketId, - }); + rest.rateLimitedPaths.set("global", { + url: "global", + resetTimestamp: globalReset, + bucketId, + }); - if (bucketId) { - rest.rateLimitedPaths.set(bucketId, { - url: "global", - resetTimestamp: globalReset, - bucketId, - }); + if (bucketId) { + rest.rateLimitedPaths.set(bucketId, { + url: "global", + resetTimestamp: globalReset, + bucketId, + }); + } } - } - if (rateLimited && !rest.processingRateLimitedPaths) { - rest.processRateLimitedPaths(rest); - } - return rateLimited ? bucketId : undefined; + if (rateLimited && !rest.processingRateLimitedPaths) { + rest.processRateLimitedPaths(rest); + } + return rateLimited ? bucketId : undefined; } diff --git a/vendor/rest/rest.ts b/vendor/rest/rest.ts index cc7257f..c794dc0 100644 --- a/vendor/rest/rest.ts +++ b/vendor/rest/rest.ts @@ -1,31 +1,31 @@ export interface RestRequest { - url: string; - method: RequestMethod; - respond: (payload: RestRequestResponse) => unknown; - reject: (payload: RestRequestRejection) => unknown; + url: string; + method: RequestMethod; + respond: (payload: RestRequestResponse) => unknown; + reject: (payload: RestRequestRejection) => unknown; } export interface RestRequestResponse { - ok: boolean; - status: number; - body?: string; + ok: boolean; + status: number; + body?: string; } export interface RestRequestRejection extends RestRequestResponse { - error: string; + error: string; } export interface RestPayload { - bucketId?: string; - body?: Record; - retryCount: number; - headers?: Record; + bucketId?: string; + body?: Record; + retryCount: number; + headers?: Record; } export interface RestRateLimitedPath { - url: string; - resetTimestamp: number; - bucketId?: string; + url: string; + resetTimestamp: number; + bucketId?: string; } export type RequestMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH"; diff --git a/vendor/rest/restManager.ts b/vendor/rest/restManager.ts index efe4c8d..0f8a93b 100644 --- a/vendor/rest/restManager.ts +++ b/vendor/rest/restManager.ts @@ -16,87 +16,87 @@ import { removeTokenPrefix } from "../util/token.ts"; import { sendRequest } from "./sendRequest.ts"; export function createRestManager(options: CreateRestManagerOptions) { - const version = options.version || API_VERSION; + const version = options.version || API_VERSION; - if (options.customUrl) { - baseEndpoints.BASE_URL = `${options.customUrl}/v${version}`; - } + if (options.customUrl) { + baseEndpoints.BASE_URL = `${options.customUrl}/v${version}`; + } - return { - // current invalid amount - invalidRequests: 0, - // max invalid requests allowed until ban - maxInvalidRequests: 10000, - // 10 minutes - invalidRequestsInterval: 600000, - // timer to reset to 0 - invalidRequestsTimeoutId: 0, - // how safe to be from max - invalidRequestsSafetyAmount: 1, - // when first request in this period was made - invalidRequestFrozenAt: 0, - invalidRequestErrorStatuses: [401, 403, 429], - version, - token: removeTokenPrefix(options.token), - maxRetryCount: options.maxRetryCount || 10, - secretKey: options.secretKey || "discordeno_best_lib_ever", - customUrl: options.customUrl || "", - pathQueues: new Map< - string, - { - isWaiting: boolean; - requests: { - request: RestRequest; - payload: RestPayload; - }[]; - } - >(), - processingQueue: false, - processingRateLimitedPaths: false, - globallyRateLimited: false, - globalQueue: [] as { - request: RestRequest; - payload: RestPayload; - basicURL: string; - urlToUse: string; - }[], - globalQueueProcessing: false, - rateLimitedPaths: new Map(), - debug: options.debug || function (_text: string) {}, - checkRateLimits: options.checkRateLimits || checkRateLimits, - cleanupQueues: options.cleanupQueues || cleanupQueues, - processQueue: options.processQueue || processQueue, - processRateLimitedPaths: options.processRateLimitedPaths || processRateLimitedPaths, - processRequestHeaders: options.processRequestHeaders || processRequestHeaders, - processRequest: options.processRequest || processRequest, - createRequestBody: options.createRequestBody || createRequestBody, - runMethod: options.runMethod || runMethod, - simplifyUrl: options.simplifyUrl || simplifyUrl, - processGlobalQueue: options.processGlobalQueue || processGlobalQueue, - convertRestError: options.convertRestError || convertRestError, - sendRequest: options.sendRequest || sendRequest, - }; + return { + // current invalid amount + invalidRequests: 0, + // max invalid requests allowed until ban + maxInvalidRequests: 10000, + // 10 minutes + invalidRequestsInterval: 600000, + // timer to reset to 0 + invalidRequestsTimeoutId: 0, + // how safe to be from max + invalidRequestsSafetyAmount: 1, + // when first request in this period was made + invalidRequestFrozenAt: 0, + invalidRequestErrorStatuses: [401, 403, 429], + version, + token: removeTokenPrefix(options.token), + maxRetryCount: options.maxRetryCount || 10, + secretKey: options.secretKey || "discordeno_best_lib_ever", + customUrl: options.customUrl || "", + pathQueues: new Map< + string, + { + isWaiting: boolean; + requests: { + request: RestRequest; + payload: RestPayload; + }[]; + } + >(), + processingQueue: false, + processingRateLimitedPaths: false, + globallyRateLimited: false, + globalQueue: [] as { + request: RestRequest; + payload: RestPayload; + basicURL: string; + urlToUse: string; + }[], + globalQueueProcessing: false, + rateLimitedPaths: new Map(), + debug: options.debug || function (_text: string) {}, + checkRateLimits: options.checkRateLimits || checkRateLimits, + cleanupQueues: options.cleanupQueues || cleanupQueues, + processQueue: options.processQueue || processQueue, + processRateLimitedPaths: options.processRateLimitedPaths || processRateLimitedPaths, + processRequestHeaders: options.processRequestHeaders || processRequestHeaders, + processRequest: options.processRequest || processRequest, + createRequestBody: options.createRequestBody || createRequestBody, + runMethod: options.runMethod || runMethod, + simplifyUrl: options.simplifyUrl || simplifyUrl, + processGlobalQueue: options.processGlobalQueue || processGlobalQueue, + convertRestError: options.convertRestError || convertRestError, + sendRequest: options.sendRequest || sendRequest, + }; } export interface CreateRestManagerOptions { - token: string; - customUrl?: string; - maxRetryCount?: number; - version?: number; - secretKey?: string; - debug?: (text: string) => unknown; - checkRateLimits?: typeof checkRateLimits; - cleanupQueues?: typeof cleanupQueues; - processQueue?: typeof processQueue; - processRateLimitedPaths?: typeof processRateLimitedPaths; - processRequestHeaders?: typeof processRequestHeaders; - processRequest?: typeof processRequest; - createRequestBody?: typeof createRequestBody; - runMethod?: typeof runMethod; - simplifyUrl?: typeof simplifyUrl; - processGlobalQueue?: typeof processGlobalQueue; - convertRestError?: typeof convertRestError; - sendRequest?: typeof sendRequest; + token: string; + customUrl?: string; + maxRetryCount?: number; + version?: number; + secretKey?: string; + debug?: (text: string) => unknown; + checkRateLimits?: typeof checkRateLimits; + cleanupQueues?: typeof cleanupQueues; + processQueue?: typeof processQueue; + processRateLimitedPaths?: typeof processRateLimitedPaths; + processRequestHeaders?: typeof processRequestHeaders; + processRequest?: typeof processRequest; + createRequestBody?: typeof createRequestBody; + runMethod?: typeof runMethod; + simplifyUrl?: typeof simplifyUrl; + processGlobalQueue?: typeof processGlobalQueue; + convertRestError?: typeof convertRestError; + sendRequest?: typeof sendRequest; } export type RestManager = ReturnType; diff --git a/vendor/rest/runMethod.ts b/vendor/rest/runMethod.ts index b7ea8fd..f036715 100644 --- a/vendor/rest/runMethod.ts +++ b/vendor/rest/runMethod.ts @@ -3,76 +3,76 @@ import { API_VERSION, BASE_URL, baseEndpoints } from "../util/constants.ts"; import { RequestMethod, RestRequestRejection, RestRequestResponse } from "./rest.ts"; export async function runMethod( - rest: RestManager, - method: RequestMethod, - route: string, - body?: unknown, - options?: { - retryCount?: number; - bucketId?: string; - headers?: Record; - }, + rest: RestManager, + method: RequestMethod, + route: string, + body?: unknown, + options?: { + retryCount?: number; + bucketId?: string; + headers?: Record; + }, ): Promise { - rest.debug( - `[REST - RequestCreate] Method: ${method} | URL: ${route} | Retry Count: ${ - options?.retryCount ?? 0 - } | Bucket ID: ${options?.bucketId} | Body: ${ - JSON.stringify( - body, - ) - }`, - ); + rest.debug( + `[REST - RequestCreate] Method: ${method} | URL: ${route} | Retry Count: ${ + options?.retryCount ?? 0 + } | Bucket ID: ${options?.bucketId} | Body: ${ + JSON.stringify( + body, + ) + }`, + ); - const errorStack = new Error("Location:"); - // @ts-ignore Breaks deno deploy. Luca said add ts-ignore until it's fixed - Error.captureStackTrace(errorStack); + const errorStack = new Error("Location:"); + // @ts-ignore Breaks deno deploy. Luca said add ts-ignore until it's fixed + Error.captureStackTrace(errorStack); - // For proxies we don't need to do any of the legwork so we just forward the request - if (!baseEndpoints.BASE_URL.startsWith(BASE_URL) && route[0] === "/") { - const result = await fetch(`${baseEndpoints.BASE_URL}${route}`, { - body: body ? JSON.stringify(body) : undefined, - headers: { - Authorization: rest.secretKey, - "Content-Type": "application/json", - }, - method, - }).catch((error) => { - errorStack.message = (error as Error)?.message; - console.error(error); - throw errorStack; - }); + // For proxies we don't need to do any of the legwork so we just forward the request + if (!baseEndpoints.BASE_URL.startsWith(BASE_URL) && route[0] === "/") { + const result = await fetch(`${baseEndpoints.BASE_URL}${route}`, { + body: body ? JSON.stringify(body) : undefined, + headers: { + Authorization: rest.secretKey, + "Content-Type": "application/json", + }, + method, + }).catch((error) => { + errorStack.message = (error as Error)?.message; + console.error(error); + throw errorStack; + }); - if (!result.ok) { - errorStack.message = result.statusText; - rest.debug(`[ERROR] ${errorStack.message}`); - // Closes the response to prevent memory leak - await result.text(); - throw errorStack; + if (!result.ok) { + errorStack.message = result.statusText; + rest.debug(`[ERROR] ${errorStack.message}`); + // Closes the response to prevent memory leak + await result.text(); + throw errorStack; + } + + return result.status !== 204 ? await result.json() : undefined; } - return result.status !== 204 ? await result.json() : undefined; - } - - // No proxy so we need to handle all rate limiting and such - return new Promise((resolve, reject) => { - rest.processRequest( - rest, - { - url: route[0] === "/" ? `${BASE_URL}/v${API_VERSION}${route}` : route, - method, - reject: (data: RestRequestRejection) => { - const restError = rest.convertRestError(errorStack, data); - reject(restError); - }, - respond: (data: RestRequestResponse) => - resolve(data.status !== 204 ? JSON.parse(data.body ?? "{}") : (undefined as unknown as T)), - }, - { - bucketId: options?.bucketId, - body: body as Record | undefined, - retryCount: options?.retryCount ?? 0, - headers: options?.headers, - }, - ); - }); + // No proxy so we need to handle all rate limiting and such + return new Promise((resolve, reject) => { + rest.processRequest( + rest, + { + url: route[0] === "/" ? `${BASE_URL}/v${API_VERSION}${route}` : route, + method, + reject: (data: RestRequestRejection) => { + const restError = rest.convertRestError(errorStack, data); + reject(restError); + }, + respond: (data: RestRequestResponse) => + resolve(data.status !== 204 ? JSON.parse(data.body ?? "{}") : (undefined as unknown as T)), + }, + { + bucketId: options?.bucketId, + body: body as Record | undefined, + retryCount: options?.retryCount ?? 0, + headers: options?.headers, + }, + ); + }); } diff --git a/vendor/rest/runProxyMethod.ts b/vendor/rest/runProxyMethod.ts index 72d82fd..8ab4035 100644 --- a/vendor/rest/runProxyMethod.ts +++ b/vendor/rest/runProxyMethod.ts @@ -6,42 +6,42 @@ export type ProxyMethodResponse = Omit( - rest: RestManager, - method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH", - url: string, - body?: unknown, - retryCount = 0, - bucketId?: string, + rest: RestManager, + method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH", + url: string, + body?: unknown, + retryCount = 0, + bucketId?: string, ): Promise> { - rest.debug( - `[REST - RequestCreate] Method: ${method} | URL: ${url} | Retry Count: ${retryCount} | Bucket ID: ${bucketId} | Body: ${ - JSON.stringify( - body, - ) - }`, - ); - - // No proxy so we need to handle all rate limiting and such - return new Promise((resolve, reject) => { - rest.processRequest( - rest, - { - url, - method, - reject: (data: RestRequestRejection) => { - const { body: b, ...r } = data; - reject({ body: data.status !== 204 ? JSON.parse(b ?? "{}") : (undefined as unknown as T), ...r }); - }, - respond: (data: RestRequestResponse) => { - const { body: b, ...r } = data; - resolve({ body: data.status !== 204 ? JSON.parse(b ?? "{}") : (undefined as unknown as T), ...r }); - }, - }, - { - bucketId, - body: body as Record | undefined, - retryCount, - }, + rest.debug( + `[REST - RequestCreate] Method: ${method} | URL: ${url} | Retry Count: ${retryCount} | Bucket ID: ${bucketId} | Body: ${ + JSON.stringify( + body, + ) + }`, ); - }); + + // No proxy so we need to handle all rate limiting and such + return new Promise((resolve, reject) => { + rest.processRequest( + rest, + { + url, + method, + reject: (data: RestRequestRejection) => { + const { body: b, ...r } = data; + reject({ body: data.status !== 204 ? JSON.parse(b ?? "{}") : (undefined as unknown as T), ...r }); + }, + respond: (data: RestRequestResponse) => { + const { body: b, ...r } = data; + resolve({ body: data.status !== 204 ? JSON.parse(b ?? "{}") : (undefined as unknown as T), ...r }); + }, + }, + { + bucketId, + body: body as Record | undefined, + retryCount, + }, + ); + }); } diff --git a/vendor/rest/sendRequest.ts b/vendor/rest/sendRequest.ts index ef14082..f42f5a8 100644 --- a/vendor/rest/sendRequest.ts +++ b/vendor/rest/sendRequest.ts @@ -4,152 +4,152 @@ import { RequestMethod } from "./rest.ts"; import { RestManager } from "./restManager.ts"; export interface RestSendRequestOptions { - url: string; - method: RequestMethod; - bucketId?: string; - reject?: Function; - respond?: Function; - retryCount?: number; - payload?: { - headers: Record; - body: string | FormData; - }; + url: string; + method: RequestMethod; + bucketId?: string; + reject?: Function; + respond?: Function; + retryCount?: number; + payload?: { + headers: Record; + body: string | FormData; + }; } export async function sendRequest(rest: RestManager, options: RestSendRequestOptions): Promise { - try { - // CUSTOM HANDLER FOR USER TO LOG OR WHATEVER WHENEVER A FETCH IS MADE - rest.debug(`[REST - fetching] URL: ${options.url} | ${JSON.stringify(options)}`); + try { + // CUSTOM HANDLER FOR USER TO LOG OR WHATEVER WHENEVER A FETCH IS MADE + rest.debug(`[REST - fetching] URL: ${options.url} | ${JSON.stringify(options)}`); - const response = await fetch( - options.url.startsWith(BASE_URL) ? options.url : `${BASE_URL}/v${rest.version}/${options.url}`, - { - method: options.method, - headers: options.payload?.headers, - body: options.payload?.body, - }, - ); - rest.debug(`[REST - fetched] URL: ${options.url} | ${JSON.stringify(options)}`); + const response = await fetch( + options.url.startsWith(BASE_URL) ? options.url : `${BASE_URL}/v${rest.version}/${options.url}`, + { + method: options.method, + headers: options.payload?.headers, + body: options.payload?.body, + }, + ); + rest.debug(`[REST - fetched] URL: ${options.url} | ${JSON.stringify(options)}`); - const bucketIdFromHeaders = rest.processRequestHeaders( - rest, - rest.simplifyUrl(options.url, options.method), - response.headers, - ); - // SET THE BUCKET Id IF IT WAS PRESENT - if (bucketIdFromHeaders) { - options.bucketId = bucketIdFromHeaders; - } - - if (response.status < 200 || response.status >= 400) { - rest.debug( - `[REST - httpError] Payload: ${JSON.stringify(options)} | Response: ${JSON.stringify(response)}`, - ); - - let error = "REQUEST_UNKNOWN_ERROR"; - switch (response.status) { - case HTTPResponseCodes.BadRequest: - error = "The options was improperly formatted, or the server couldn't understand it."; - break; - case HTTPResponseCodes.Unauthorized: - error = "The Authorization header was missing or invalid."; - break; - case HTTPResponseCodes.Forbidden: - error = "The Authorization token you passed did not have permission to the resource."; - break; - case HTTPResponseCodes.NotFound: - error = "The resource at the location specified doesn't exist."; - break; - case HTTPResponseCodes.MethodNotAllowed: - error = "The HTTP method used is not valid for the location specified."; - break; - case HTTPResponseCodes.GatewayUnavailable: - error = "There was not a gateway available to process your options. Wait a bit and retry."; - break; - } - - if ( - rest.invalidRequestErrorStatuses.includes(response.status) && - !(response.status === 429 && response.headers.get("X-RateLimit-Scope")) - ) { - // INCREMENT CURRENT INVALID REQUESTS - ++rest.invalidRequests; - - if (!rest.invalidRequestsTimeoutId) { - rest.invalidRequestsTimeoutId = setTimeout(() => { - rest.debug(`[REST - processGlobalQueue] Resetting invalid optionss counter in setTimeout.`); - rest.invalidRequests = 0; - rest.invalidRequestsTimeoutId = 0; - }, rest.invalidRequestsInterval); + const bucketIdFromHeaders = rest.processRequestHeaders( + rest, + rest.simplifyUrl(options.url, options.method), + response.headers, + ); + // SET THE BUCKET Id IF IT WAS PRESENT + if (bucketIdFromHeaders) { + options.bucketId = bucketIdFromHeaders; } - } - // If NOT rate limited remove from queue - if (response.status !== 429) { + if (response.status < 200 || response.status >= 400) { + rest.debug( + `[REST - httpError] Payload: ${JSON.stringify(options)} | Response: ${JSON.stringify(response)}`, + ); + + let error = "REQUEST_UNKNOWN_ERROR"; + switch (response.status) { + case HTTPResponseCodes.BadRequest: + error = "The options was improperly formatted, or the server couldn't understand it."; + break; + case HTTPResponseCodes.Unauthorized: + error = "The Authorization header was missing or invalid."; + break; + case HTTPResponseCodes.Forbidden: + error = "The Authorization token you passed did not have permission to the resource."; + break; + case HTTPResponseCodes.NotFound: + error = "The resource at the location specified doesn't exist."; + break; + case HTTPResponseCodes.MethodNotAllowed: + error = "The HTTP method used is not valid for the location specified."; + break; + case HTTPResponseCodes.GatewayUnavailable: + error = "There was not a gateway available to process your options. Wait a bit and retry."; + break; + } + + if ( + rest.invalidRequestErrorStatuses.includes(response.status) && + !(response.status === 429 && response.headers.get("X-RateLimit-Scope")) + ) { + // INCREMENT CURRENT INVALID REQUESTS + ++rest.invalidRequests; + + if (!rest.invalidRequestsTimeoutId) { + rest.invalidRequestsTimeoutId = setTimeout(() => { + rest.debug(`[REST - processGlobalQueue] Resetting invalid optionss counter in setTimeout.`); + rest.invalidRequests = 0; + rest.invalidRequestsTimeoutId = 0; + }, rest.invalidRequestsInterval); + } + } + + // If NOT rate limited remove from queue + if (response.status !== 429) { + options.reject?.({ + ok: false, + status: response.status, + error, + body: response.type ? JSON.stringify(await response.json()) : undefined, + }); + + throw new Error( + JSON.stringify({ + ok: false, + status: response.status, + error, + body: response.type ? JSON.stringify(await response.json()) : undefined, + }), + ); + } else { + if (options.retryCount && options.retryCount++ >= rest.maxRetryCount) { + rest.debug(`[REST - RetriesMaxed] ${JSON.stringify(options)}`); + // REMOVE ITEM FROM QUEUE TO PREVENT RETRY + options.reject?.({ + ok: false, + status: response.status, + error: "The options was rate limited and it maxed out the retries limit.", + }); + + // @ts-ignore Code should never reach here + return; + } + } + } + + // SOMETIMES DISCORD RETURNS AN EMPTY 204 RESPONSE THAT CAN'T BE MADE TO JSON + if (response.status === 204) { + rest.debug(`[REST - FetchSuccess] URL: ${options.url} | ${JSON.stringify(options)}`); + options.respond?.({ + ok: true, + status: 204, + }); + // @ts-ignore 204 will be void + return; + } else { + // CONVERT THE RESPONSE TO JSON + const json = JSON.stringify(await response.json()); + + rest.debug(`[REST - fetchSuccess] ${JSON.stringify(options)}`); + options.respond?.({ + ok: true, + status: 200, + body: json, + }); + + return JSON.parse(json); + } + } catch (error) { + // SOMETHING WENT WRONG, LOG AND RESPOND WITH ERROR + rest.debug(`[REST - fetchFailed] Payload: ${JSON.stringify(options)} | Error: ${error}`); options.reject?.({ - ok: false, - status: response.status, - error, - body: response.type ? JSON.stringify(await response.json()) : undefined, + ok: false, + status: 599, + error: "Internal Proxy Error", }); - throw new Error( - JSON.stringify({ - ok: false, - status: response.status, - error, - body: response.type ? JSON.stringify(await response.json()) : undefined, - }), - ); - } else { - if (options.retryCount && options.retryCount++ >= rest.maxRetryCount) { - rest.debug(`[REST - RetriesMaxed] ${JSON.stringify(options)}`); - // REMOVE ITEM FROM QUEUE TO PREVENT RETRY - options.reject?.({ - ok: false, - status: response.status, - error: "The options was rate limited and it maxed out the retries limit.", - }); - - // @ts-ignore Code should never reach here - return; - } - } + throw new Error("Something went wrong in sendRequest", { + cause: error, + }); } - - // SOMETIMES DISCORD RETURNS AN EMPTY 204 RESPONSE THAT CAN'T BE MADE TO JSON - if (response.status === 204) { - rest.debug(`[REST - FetchSuccess] URL: ${options.url} | ${JSON.stringify(options)}`); - options.respond?.({ - ok: true, - status: 204, - }); - // @ts-ignore 204 will be void - return; - } else { - // CONVERT THE RESPONSE TO JSON - const json = JSON.stringify(await response.json()); - - rest.debug(`[REST - fetchSuccess] ${JSON.stringify(options)}`); - options.respond?.({ - ok: true, - status: 200, - body: json, - }); - - return JSON.parse(json); - } - } catch (error) { - // SOMETHING WENT WRONG, LOG AND RESPOND WITH ERROR - rest.debug(`[REST - fetchFailed] Payload: ${JSON.stringify(options)} | Error: ${error}`); - options.reject?.({ - ok: false, - status: 599, - error: "Internal Proxy Error", - }); - - throw new Error("Something went wrong in sendRequest", { - cause: error, - }); - } } diff --git a/vendor/rest/simplifyUrl.ts b/vendor/rest/simplifyUrl.ts index 899a40a..ae7c174 100644 --- a/vendor/rest/simplifyUrl.ts +++ b/vendor/rest/simplifyUrl.ts @@ -5,21 +5,21 @@ /** Split a url to separate rate limit buckets based on major/minor parameters. */ export function simplifyUrl(url: string, method: string) { - let route = url - .replace(/\/([a-z-]+)\/(?:[0-9]{17,19})/g, function (match, p) { - return ["channels", "guilds"].includes(p) ? match : `/${p}/skillzPrefersID`; - }) - .replace(/\/reactions\/[^/]+/g, "/reactions/skillzPrefersID"); + let route = url + .replace(/\/([a-z-]+)\/(?:[0-9]{17,19})/g, function (match, p) { + return ["channels", "guilds"].includes(p) ? match : `/${p}/skillzPrefersID`; + }) + .replace(/\/reactions\/[^/]+/g, "/reactions/skillzPrefersID"); - // GENERAL /reactions and /reactions/emoji/@me share the buckets - if (route.includes("/reactions")) { - route = route.substring(0, route.indexOf("/reactions") + "/reactions".length); - } + // GENERAL /reactions and /reactions/emoji/@me share the buckets + if (route.includes("/reactions")) { + route = route.substring(0, route.indexOf("/reactions") + "/reactions".length); + } - // Delete Message endpoint has its own rate limit - if (method === "DELETE" && route.endsWith("/messages/skillzPrefersID")) { - route = method + route; - } + // Delete Message endpoint has its own rate limit + if (method === "DELETE" && route.endsWith("/messages/skillzPrefersID")) { + route = method + route; + } - return route; + return route; } diff --git a/vendor/types/discord.ts b/vendor/types/discord.ts index 37cd84c..5fec56a 100644 --- a/vendor/types/discord.ts +++ b/vendor/types/discord.ts @@ -1,1323 +1,1358 @@ import { - ActivityTypes, - AllowedMentionsTypes, - ApplicationCommandOptionTypes, - ApplicationCommandPermissionTypes, - ApplicationCommandTypes, - ApplicationFlags, - AuditLogEvents, - ButtonStyles, - ChannelFlags, - ChannelTypes, - DefaultMessageNotificationLevels, - EmbedTypes, - ExplicitContentFilterLevels, - GatewayEventNames, - GuildFeatures, - GuildNsfwLevel, - IntegrationExpireBehaviors, - InteractionTypes, - Locales, - Localization, - MessageActivityTypes, - MessageComponentTypes, - MessageTypes, - MfaLevels, - OverwriteTypes, - PickPartial, - PremiumTiers, - PremiumTypes, - ScheduledEventEntityType, - ScheduledEventPrivacyLevel, - ScheduledEventStatus, - StickerFormatTypes, - StickerTypes, - SystemChannelFlags, - TargetTypes, - TeamMembershipStates, - TextStyles, - UserFlags, - VerificationLevels, - VideoQualityModes, - VisibilityTypes, - WebhookTypes, + ActivityTypes, + AllowedMentionsTypes, + ApplicationCommandOptionTypes, + ApplicationCommandPermissionTypes, + ApplicationCommandTypes, + ApplicationFlags, + AuditLogEvents, + ButtonStyles, + ChannelFlags, + ChannelTypes, + DefaultMessageNotificationLevels, + EmbedTypes, + ExplicitContentFilterLevels, + GatewayEventNames, + GuildFeatures, + GuildNsfwLevel, + IntegrationExpireBehaviors, + InteractionTypes, + Locales, + Localization, + MessageActivityTypes, + MessageComponentTypes, + MessageTypes, + MfaLevels, + OverwriteTypes, + PickPartial, + PremiumTiers, + PremiumTypes, + ScheduledEventEntityType, + ScheduledEventPrivacyLevel, + ScheduledEventStatus, + StickerFormatTypes, + StickerTypes, + SystemChannelFlags, + TargetTypes, + TeamMembershipStates, + TextStyles, + UserFlags, + VerificationLevels, + VideoQualityModes, + VisibilityTypes, + WebhookTypes, } from "./shared.ts"; /** https://discord.com/developers/docs/resources/user#user-object */ export interface DiscordUser { - /** The user's username, not unique across the platform */ - username: string; - /** The user's chosen language option */ - locale?: string; - /** The flags on a user's account */ - flags?: UserFlags; - /** The type of Nitro subscription on a user's account */ - premium_type?: PremiumTypes; - /** The public flags on a user's account */ - public_flags?: UserFlags; - /** the user's banner color encoded as an integer representation of hexadecimal color code */ - accent_color?: number; + /** The user's username, not unique across the platform */ + username: string; + /** The user's chosen language option */ + locale?: string; + /** The flags on a user's account */ + flags?: UserFlags; + /** The type of Nitro subscription on a user's account */ + premium_type?: PremiumTypes; + /** The public flags on a user's account */ + public_flags?: UserFlags; + /** the user's banner color encoded as an integer representation of hexadecimal color code */ + accent_color?: number; - /** The user's id */ - id: string; - /** The user's 4-digit discord-tag */ - discriminator: string; - /** The user's avatar hash */ - avatar: string | null; - /** Whether the user belongs to an OAuth2 application */ - bot?: boolean; - /** Whether the user is an Official Discord System user (part of the urgent message system) */ - system?: boolean; - /** Whether the user has two factor enabled on their account */ - mfa_enabled?: boolean; - /** Whether the email on this account has been verified */ - verified?: boolean; - /** The user's email */ - email?: string | null; - /** the user's banner, or null if unset */ - banner?: string; + /** The user's id */ + id: string; + /** The user's 4-digit discord-tag */ + discriminator: string; + /** The user's avatar hash */ + avatar: string | null; + /** Whether the user belongs to an OAuth2 application */ + bot?: boolean; + /** Whether the user is an Official Discord System user (part of the urgent message system) */ + system?: boolean; + /** Whether the user has two factor enabled on their account */ + mfa_enabled?: boolean; + /** Whether the email on this account has been verified */ + verified?: boolean; + /** The user's email */ + email?: string | null; + /** the user's banner, or null if unset */ + banner?: string; } /** https://discord.com/developers/docs/resources/user#connection-object */ export interface DiscordConnection { - /** id of the connection account */ - id: string; - /** The username of the connection account */ - name: string; - /** The service of the connection (twitch, youtube) */ - type: string; - /** Whether the connection is revoked */ - revoked?: boolean; - /** Whether the connection is verified */ - verified: boolean; - /** Whether friend sync is enabled for this connection */ - friendSync: boolean; - /** Whether activities related to this connection will be shown in presence updates */ - showActivity: boolean; - /** Visibility of this connection */ - visibility: VisibilityTypes; + /** id of the connection account */ + id: string; + /** The username of the connection account */ + name: string; + /** The service of the connection (twitch, youtube) */ + type: string; + /** Whether the connection is revoked */ + revoked?: boolean; + /** Whether the connection is verified */ + verified: boolean; + /** Whether friend sync is enabled for this connection */ + friendSync: boolean; + /** Whether activities related to this connection will be shown in presence updates */ + showActivity: boolean; + /** Visibility of this connection */ + visibility: VisibilityTypes; - /** An array of partial server integrations */ - integrations?: DiscordIntegration[]; + /** An array of partial server integrations */ + integrations?: DiscordIntegration[]; } /** https://discord.com/developers/docs/resources/guild#integration-object-integration-structure */ export interface DiscordIntegration { - /** Integration Id */ - id: string; - /** Integration name */ - name: string; - /** Integration type (twitch, youtube or discord) */ - type: "twitch" | "youtube" | "discord"; - /** Is this integration enabled */ - enabled?: boolean; - /** Is this integration syncing */ - syncing?: boolean; - /** Role Id that this integration uses for "subscribers" */ - role_id?: string; - /** Whether emoticons should be synced for this integration (twitch only currently) */ - enable_emoticons?: boolean; - /** The behavior of expiring subscribers */ - expire_behavior?: IntegrationExpireBehaviors; - /** The grace period (in days) before expiring subscribers */ - expire_grace_period?: number; - /** When this integration was last synced */ - synced_at?: string; - /** How many subscribers this integration has */ - subscriber_count?: number; - /** Has this integration been revoked */ - revoked?: boolean; + /** Integration Id */ + id: string; + /** Integration name */ + name: string; + /** Integration type (twitch, youtube or discord) */ + type: "twitch" | "youtube" | "discord"; + /** Is this integration enabled */ + enabled?: boolean; + /** Is this integration syncing */ + syncing?: boolean; + /** Role Id that this integration uses for "subscribers" */ + role_id?: string; + /** Whether emoticons should be synced for this integration (twitch only currently) */ + enable_emoticons?: boolean; + /** The behavior of expiring subscribers */ + expire_behavior?: IntegrationExpireBehaviors; + /** The grace period (in days) before expiring subscribers */ + expire_grace_period?: number; + /** When this integration was last synced */ + synced_at?: string; + /** How many subscribers this integration has */ + subscriber_count?: number; + /** Has this integration been revoked */ + revoked?: boolean; - /** User for this integration */ - user?: DiscordUser; - /** Integration account information */ - account: DiscordIntegrationAccount; - /** The bot/OAuth2 application for discord integrations */ - application?: DiscordIntegrationApplication; + /** User for this integration */ + user?: DiscordUser; + /** Integration account information */ + account: DiscordIntegrationAccount; + /** The bot/OAuth2 application for discord integrations */ + application?: DiscordIntegrationApplication; } /** https://discord.com/developers/docs/resources/guild#integration-account-object-integration-account-structure */ export interface DiscordIntegrationAccount { - /** Id of the account */ - id: string; - /** Name of the account */ - name: string; + /** Id of the account */ + id: string; + /** Name of the account */ + name: string; } /** https://discord.com/developers/docs/resources/guild#integration-application-object-integration-application-structure */ export interface DiscordIntegrationApplication { - /** The id of the app */ - id: string; - /** The name of the app */ - name: string; - /** the icon hash of the app */ - icon: string | null; - /** The description of the app */ - description: string; + /** The id of the app */ + id: string; + /** The name of the app */ + name: string; + /** the icon hash of the app */ + icon: string | null; + /** The description of the app */ + description: string; - /** The bot associated with this application */ - bot?: DiscordUser; + /** The bot associated with this application */ + bot?: DiscordUser; } /** https://github.com/discord/discord-api-docs/blob/master/docs/topics/Gateway.md#integration-create-event-additional-fields */ export interface DiscordIntegrationCreateUpdate extends DiscordIntegration { - /** Id of the guild */ - guild_id: string; + /** Id of the guild */ + guild_id: string; } /** https://github.com/discord/discord-api-docs/blob/master/docs/topics/Gateway.md#integration-delete-event-fields */ export interface DiscordIntegrationDelete { - /** Integration id */ - id: string; - /** Id of the guild */ - guild_id: string; - /** Id of the bot/OAuth2 application for this discord integration */ - application_id?: string; + /** Integration id */ + id: string; + /** Id of the guild */ + guild_id: string; + /** Id of the bot/OAuth2 application for this discord integration */ + application_id?: string; } /** https://discord.com/developers/docs/topics/gateway#guild-integrations-update */ export interface DiscordGuildIntegrationsUpdate { - /** id of the guild whose integrations were updated */ - guild_id: string; + /** id of the guild whose integrations were updated */ + guild_id: string; } /** https://discord.com/developers/docs/topics/gateway#typing-start */ export interface DiscordTypingStart { - /** Unix time (in seconds) of when the user started typing */ - timestamp: number; + /** Unix time (in seconds) of when the user started typing */ + timestamp: number; - /** id of the channel */ - channel_id: string; - /** id of the guild */ - guild_id?: string; - /** id of the user */ - user_id: string; - /** The member who started typing if this happened in a guild */ - member?: DiscordMember; + /** id of the channel */ + channel_id: string; + /** id of the guild */ + guild_id?: string; + /** id of the user */ + user_id: string; + /** The member who started typing if this happened in a guild */ + member?: DiscordMember; } /** https://discord.com/developers/docs/resources/guild#guild-member-object */ export interface DiscordMember { - /** Whether the user is deafened in voice channels */ - deaf?: boolean; - /** Whether the user is muted in voice channels */ - mute?: boolean; - /** Whether the user has not yet passed the guild's Membership Screening requirements */ - pending?: boolean; + /** Whether the user is deafened in voice channels */ + deaf?: boolean; + /** Whether the user is muted in voice channels */ + mute?: boolean; + /** Whether the user has not yet passed the guild's Membership Screening requirements */ + pending?: boolean; - /** The user this guild member represents */ - user?: DiscordUser; - /** This users guild nickname */ - nick?: string | null; - /** The members custom avatar for this server. */ - avatar?: string; - /** Array of role object ids */ - roles: string[]; - /** When the user joined the guild */ - joined_at: string; - /** When the user started boosting the guild */ - premium_since?: string | null; - /** The permissions this member has in the guild. Only present on interaction events. */ - permissions?: string; - /** when the user's timeout will expire and the user will be able to communicate in the guild again, null or a time in the past if the user is not timed out */ - communication_disabled_until?: string | null; + /** The user this guild member represents */ + user?: DiscordUser; + /** This users guild nickname */ + nick?: string | null; + /** The members custom avatar for this server. */ + avatar?: string; + /** Array of role object ids */ + roles: string[]; + /** When the user joined the guild */ + joined_at: string; + /** When the user started boosting the guild */ + premium_since?: string | null; + /** The permissions this member has in the guild. Only present on interaction events. */ + permissions?: string; + /** when the user's timeout will expire and the user will be able to communicate in the guild again, null or a time in the past if the user is not timed out */ + communication_disabled_until?: string | null; } /** https://discord.com/developers/docs/topics/oauth2#application-object */ export interface DiscordApplication { - /** The name of the app */ - name: string; - /** The description of the app */ - description: string; - /** An array of rpc origin urls, if rpc is enabled */ - rpc_origins?: string[]; - /** The url of the app's terms of service */ - terms_of_service_url?: string; - /** The url of the app's privacy policy */ - privacy_policy_url?: string; - /** The hex encoded key for verification in interactions and the GameSDK's GetTicket */ - verify_key: string; - /** If this application is a game sold on Discord, this field will be the id of the "Game SKU" that is created, if exists */ - primary_sku_id?: string; - /** If this application is a game sold on Discord, this field will be the URL slug that links to the store page */ - slug?: string; - /** The application's public flags */ - flags?: ApplicationFlags; + /** The name of the app */ + name: string; + /** The description of the app */ + description: string; + /** An array of rpc origin urls, if rpc is enabled */ + rpc_origins?: string[]; + /** The url of the app's terms of service */ + terms_of_service_url?: string; + /** The url of the app's privacy policy */ + privacy_policy_url?: string; + /** The hex encoded key for verification in interactions and the GameSDK's GetTicket */ + verify_key: string; + /** If this application is a game sold on Discord, this field will be the id of the "Game SKU" that is created, if exists */ + primary_sku_id?: string; + /** If this application is a game sold on Discord, this field will be the URL slug that links to the store page */ + slug?: string; + /** The application's public flags */ + flags?: ApplicationFlags; - /** The id of the app */ - id: string; - /** The icon hash of the app */ - icon: string | null; - /** When false only app owner can join the app's bot to guilds */ - bot_public: boolean; - /** When true the app's bot will only join upon completion of the full oauth2 code grant flow */ - bot_require_code_grant: boolean; - /** Partial user object containing info on the owner of the application */ - owner?: Partial; - /** If the application belongs to a team, this will be a list of the members of that team */ - team: DiscordTeam | null; - /** If this application is a game sold on Discord, this field will be the guild to which it has been linked */ - guild_id?: string; - /** If this application is a game sold on Discord, this field will be the hash of the image on store embeds */ - cover_image?: string; - /** up to 5 tags describing the content and functionality of the application */ - tags?: string[]; - /** settings for the application's default in-app authorization link, if enabled */ - install_params?: DiscordInstallParams; - /** the application's default custom authorization link, if enabled */ - custom_install_url?: string; + /** The id of the app */ + id: string; + /** The icon hash of the app */ + icon: string | null; + /** When false only app owner can join the app's bot to guilds */ + bot_public: boolean; + /** When true the app's bot will only join upon completion of the full oauth2 code grant flow */ + bot_require_code_grant: boolean; + /** Partial user object containing info on the owner of the application */ + owner?: Partial; + /** If the application belongs to a team, this will be a list of the members of that team */ + team: DiscordTeam | null; + /** If this application is a game sold on Discord, this field will be the guild to which it has been linked */ + guild_id?: string; + /** If this application is a game sold on Discord, this field will be the hash of the image on store embeds */ + cover_image?: string; + /** up to 5 tags describing the content and functionality of the application */ + tags?: string[]; + /** settings for the application's default in-app authorization link, if enabled */ + install_params?: DiscordInstallParams; + /** the application's default custom authorization link, if enabled */ + custom_install_url?: string; } /** https://discord.com/developers/docs/topics/teams#data-models-team-object */ export interface DiscordTeam { - /** A hash of the image of the team's icon */ - icon: string | null; - /** The unique id of the team */ - id: string; - /** The members of the team */ - members: DiscordTeamMember[]; - /** The user id of the current team owner */ - owner_user_id: string; - /** The name of the team */ - name: string; + /** A hash of the image of the team's icon */ + icon: string | null; + /** The unique id of the team */ + id: string; + /** The members of the team */ + members: DiscordTeamMember[]; + /** The user id of the current team owner */ + owner_user_id: string; + /** The name of the team */ + name: string; } /** https://discord.com/developers/docs/topics/teams#data-models-team-members-object */ export interface DiscordTeamMember { - /** The user's membership state on the team */ - membership_state: TeamMembershipStates; - /** Will always be `["*"]` */ - permissions: "*"[]; + /** The user's membership state on the team */ + membership_state: TeamMembershipStates; + /** Will always be `["*"]` */ + permissions: "*"[]; - /** The id of the parent team of which they are a member */ - team_id: string; - /** The avatar, discriminator, id, and username of the user */ - user: Partial & Pick; + /** The id of the parent team of which they are a member */ + team_id: string; + /** The avatar, discriminator, id, and username of the user */ + user: Partial & Pick; } /** https://discord.com/developers/docs/topics/gateway#webhooks-update-webhook-update-event-fields */ export interface DiscordWebhookUpdate { - /** id of the guild */ - guild_id: string; - /** id of the channel */ - channel_id: string; + /** id of the guild */ + guild_id: string; + /** id of the channel */ + channel_id: string; } /** https://discord.com/developers/docs/resources/channel#allowed-mentions-object */ export interface DiscordAllowedMentions { - /** An array of allowed mention types to parse from the content. */ - parse?: AllowedMentionsTypes[]; - /** For replies, whether to mention the author of the message being replied to (default false) */ - replied_user?: boolean; + /** An array of allowed mention types to parse from the content. */ + parse?: AllowedMentionsTypes[]; + /** For replies, whether to mention the author of the message being replied to (default false) */ + replied_user?: boolean; - /** Array of role_ids to mention (Max size of 100) */ - roles?: string[]; - /** Array of user_ids to mention (Max size of 100) */ - users?: string[]; + /** Array of role_ids to mention (Max size of 100) */ + roles?: string[]; + /** Array of user_ids to mention (Max size of 100) */ + users?: string[]; } /** https://discord.com/developers/docs/resources/channel#embed-object */ export interface DiscordEmbed { - /** Title of embed */ - title?: string; - /** Type of embed (always "rich" for webhook embeds) */ - type?: EmbedTypes; - /** Description of embed */ - description?: string; - /** Url of embed */ - url?: string; - /** Color code of the embed */ - color?: number; + /** Title of embed */ + title?: string; + /** Type of embed (always "rich" for webhook embeds) */ + type?: EmbedTypes; + /** Description of embed */ + description?: string; + /** Url of embed */ + url?: string; + /** Color code of the embed */ + color?: number; - /** Timestamp of embed content */ - timestamp?: string; - /** Footer information */ - footer?: DiscordEmbedFooter; - /** Image information */ - image?: DiscordEmbedImage; - /** Thumbnail information */ - thumbnail?: DiscordEmbedThumbnail; - /** Video information */ - video?: DiscordEmbedVideo; - /** Provider information */ - provider?: DiscordEmbedProvider; - /** Author information */ - author?: DiscordEmbedAuthor; - /** Fields information */ - fields?: DiscordEmbedField[]; + /** Timestamp of embed content */ + timestamp?: string; + /** Footer information */ + footer?: DiscordEmbedFooter; + /** Image information */ + image?: DiscordEmbedImage; + /** Thumbnail information */ + thumbnail?: DiscordEmbedThumbnail; + /** Video information */ + video?: DiscordEmbedVideo; + /** Provider information */ + provider?: DiscordEmbedProvider; + /** Author information */ + author?: DiscordEmbedAuthor; + /** Fields information */ + fields?: DiscordEmbedField[]; } /** https://discord.com/developers/docs/resources/channel#embed-object-embed-author-structure */ export interface DiscordEmbedAuthor { - /** Name of author */ - name: string; - /** Url of author */ - url?: string; - /** Url of author icon (only supports http(s) and attachments) */ - icon_url?: string; - /** A proxied url of author icon */ - proxy_icon_url?: string; + /** Name of author */ + name: string; + /** Url of author */ + url?: string; + /** Url of author icon (only supports http(s) and attachments) */ + icon_url?: string; + /** A proxied url of author icon */ + proxy_icon_url?: string; } /** https://discord.com/developers/docs/resources/channel#embed-object-embed-field-structure */ export interface DiscordEmbedField { - /** Name of the field */ - name: string; - /** Value of the field */ - value: string; - /** Whether or not this field should display inline */ - inline?: boolean; + /** Name of the field */ + name: string; + /** Value of the field */ + value: string; + /** Whether or not this field should display inline */ + inline?: boolean; } /** https://discord.com/developers/docs/resources/channel#embed-object-embed-footer-structure */ export interface DiscordEmbedFooter { - /** Footer text */ - text: string; - /** Url of footer icon (only supports http(s) and attachments) */ - icon_url?: string; - /** A proxied url of footer icon */ - proxy_icon_url?: string; + /** Footer text */ + text: string; + /** Url of footer icon (only supports http(s) and attachments) */ + icon_url?: string; + /** A proxied url of footer icon */ + proxy_icon_url?: string; } /** https://discord.com/developers/docs/resources/channel#embed-object-embed-image-structure */ export interface DiscordEmbedImage { - /** Source url of image (only supports http(s) and attachments) */ - url: string; - /** A proxied url of the image */ - proxy_url?: string; - /** Height of image */ - height?: number; - /** Width of image */ - width?: number; + /** Source url of image (only supports http(s) and attachments) */ + url: string; + /** A proxied url of the image */ + proxy_url?: string; + /** Height of image */ + height?: number; + /** Width of image */ + width?: number; } export interface DiscordEmbedProvider { - /** Name of provider */ - name?: string; - /** Url of provider */ - url?: string; + /** Name of provider */ + name?: string; + /** Url of provider */ + url?: string; } /** https://discord.com/developers/docs/resources/channel#embed-object-embed-thumbnail-structure */ export interface DiscordEmbedThumbnail { - /** Source url of thumbnail (only supports http(s) and attachments) */ - url: string; - /** A proxied url of the thumbnail */ - proxy_url?: string; - /** Height of thumbnail */ - height?: number; - /** Width of thumbnail */ - width?: number; + /** Source url of thumbnail (only supports http(s) and attachments) */ + url: string; + /** A proxied url of the thumbnail */ + proxy_url?: string; + /** Height of thumbnail */ + height?: number; + /** Width of thumbnail */ + width?: number; } /** https://discord.com/developers/docs/resources/channel#embed-object-embed-video-structure */ export interface DiscordEmbedVideo { - /** Source url of video */ - url?: string; - /** A proxied url of the video */ - proxy_url?: string; - /** Height of video */ - height?: number; - /** Width of video */ - width?: number; + /** Source url of video */ + url?: string; + /** A proxied url of the video */ + proxy_url?: string; + /** Height of video */ + height?: number; + /** Width of video */ + width?: number; } /** https://discord.com/developers/docs/resources/channel#attachment-object */ export interface DiscordAttachment { - /** Name of file attached */ - filename: string; - /** The attachment's [media type](https://en.wikipedia.org/wiki/Media_type) */ - content_type?: string; - /** Size of file in bytes */ - size: number; - /** Source url of file */ - url: string; - /** A proxied url of file */ - proxy_url: string; + /** Name of file attached */ + filename: string; + /** The attachment's [media type](https://en.wikipedia.org/wiki/Media_type) */ + content_type?: string; + /** Size of file in bytes */ + size: number; + /** Source url of file */ + url: string; + /** A proxied url of file */ + proxy_url: string; - /** Attachment id */ - id: string; - /** Height of file (if image) */ - height?: number | null; - /** Width of file (if image) */ - width?: number | null; - /** whether this attachment is ephemeral. Ephemeral attachments will automatically be removed after a set period of time. Ephemeral attachments on messages are guaranteed to be available as long as the message itself exists. */ - ephemeral?: boolean; + /** Attachment id */ + id: string; + /** Height of file (if image) */ + height?: number | null; + /** Width of file (if image) */ + width?: number | null; + /** whether this attachment is ephemeral. Ephemeral attachments will automatically be removed after a set period of time. Ephemeral attachments on messages are guaranteed to be available as long as the message itself exists. */ + ephemeral?: boolean; } /** https://discord.com/developers/docs/resources/webhook#webhook-object-webhook-structure */ export type DiscordWebhook = DiscordIncomingWebhook | DiscordApplicationWebhook; export interface DiscordIncomingWebhook { - /** The type of the webhook */ - type: WebhookTypes; - /** The secure token of the webhook (returned for Incoming Webhooks) */ - token?: string; - /** The url used for executing the webhook (returned by the webhooks OAuth2 flow) */ - url?: string; + /** The type of the webhook */ + type: WebhookTypes; + /** The secure token of the webhook (returned for Incoming Webhooks) */ + token?: string; + /** The url used for executing the webhook (returned by the webhooks OAuth2 flow) */ + url?: string; - /** The id of the webhook */ - id: string; - /** The guild id this webhook is for */ - guild_id?: string; - /** The channel id this webhook is for */ - channel_id: string; - /** The user this webhook was created by (not returned when getting a webhook with its token) */ - user?: DiscordUser; - /** The default name of the webhook */ - name: string | null; - /** The default user avatar hash of the webhook */ - avatar: string | null; - /** The bot/OAuth2 application that created this webhook */ - application_id: string | null; - /** The guild of the channel that this webhook is following (returned for Channel Follower Webhooks) */ - source_guild?: Partial; - /** The channel that this webhook is following (returned for Channel Follower Webhooks) */ - source_channel?: Partial; + /** The id of the webhook */ + id: string; + /** The guild id this webhook is for */ + guild_id?: string; + /** The channel id this webhook is for */ + channel_id: string; + /** The user this webhook was created by (not returned when getting a webhook with its token) */ + user?: DiscordUser; + /** The default name of the webhook */ + name: string | null; + /** The default user avatar hash of the webhook */ + avatar: string | null; + /** The bot/OAuth2 application that created this webhook */ + application_id: string | null; + /** The guild of the channel that this webhook is following (returned for Channel Follower Webhooks) */ + source_guild?: Partial; + /** The channel that this webhook is following (returned for Channel Follower Webhooks) */ + source_channel?: Partial; } export interface DiscordApplicationWebhook { - /** The type of the webhook */ - type: WebhookTypes.Application; - /** The secure token of the webhook (returned for Incoming Webhooks) */ - token?: string; - /** The url used for executing the webhook (returned by the webhooks OAuth2 flow) */ - url?: string; + /** The type of the webhook */ + type: WebhookTypes.Application; + /** The secure token of the webhook (returned for Incoming Webhooks) */ + token?: string; + /** The url used for executing the webhook (returned by the webhooks OAuth2 flow) */ + url?: string; - /** The id of the webhook */ - id: string; - /** The guild id this webhook is for */ - guild_id?: string | null; - /** The channel id this webhook is for */ - channel_id?: string | null; - /** The user this webhook was created by (not returned when getting a webhook with its token) */ - user?: DiscordUser; - /** The default name of the webhook */ - name: string | null; - /** The default user avatar hash of the webhook */ - avatar: string | null; - /** The bot/OAuth2 application that created this webhook */ - application_id: string | null; - /** The guild of the channel that this webhook is following (returned for Channel Follower Webhooks) */ - source_guild?: Partial; - /** The channel that this webhook is following (returned for Channel Follower Webhooks) */ - source_channel?: Partial; + /** The id of the webhook */ + id: string; + /** The guild id this webhook is for */ + guild_id?: string | null; + /** The channel id this webhook is for */ + channel_id?: string | null; + /** The user this webhook was created by (not returned when getting a webhook with its token) */ + user?: DiscordUser; + /** The default name of the webhook */ + name: string | null; + /** The default user avatar hash of the webhook */ + avatar: string | null; + /** The bot/OAuth2 application that created this webhook */ + application_id: string | null; + /** The guild of the channel that this webhook is following (returned for Channel Follower Webhooks) */ + source_guild?: Partial; + /** The channel that this webhook is following (returned for Channel Follower Webhooks) */ + source_channel?: Partial; } /** https://discord.com/developers/docs/resources/guild#guild-object */ export interface DiscordGuild { - /** Guild name (2-100 characters, excluding trailing and leading whitespace) */ - name: string; - /** True if the user is the owner of the guild */ - owner?: boolean; - /** Afk timeout in seconds */ - afk_timeout: number; - /** True if the server widget is enabled */ - widget_enabled?: boolean; - /** Verification level required for the guild */ - verification_level: VerificationLevels; - /** Default message notifications level */ - default_message_notifications: DefaultMessageNotificationLevels; - /** Explicit content filter level */ - explicit_content_filter: ExplicitContentFilterLevels; - /** Enabled guild features */ - features: GuildFeatures[]; - /** Required MFA level for the guild */ - mfa_level: MfaLevels; - /** System channel flags */ - system_channel_flags: SystemChannelFlags; - /** True if this is considered a large guild */ - large?: boolean; - /** True if this guild is unavailable due to an outage */ - unavailable?: boolean; - /** Total number of members in this guild */ - member_count?: number; - /** The maximum number of presences for the guild (the default value, currently 25000, is in effect when null is returned) */ - max_presences?: number | null; - /** The maximum number of members for the guild */ - max_members?: number; - /** The vanity url code for the guild */ - vanity_url_code: string | null; - /** The description of a guild */ - description: string | null; - /** Premium tier (Server Boost level) */ - premium_tier: PremiumTiers; - /** The number of boosts this guild currently has */ - premium_subscription_count?: number; - /** The maximum amount of users in a video channel */ - max_video_channel_users?: number; - /** Approximate number of members in this guild, returned from the GET /guilds/ endpoint when with_counts is true */ - approximate_member_count?: number; - /** Approximate number of non-offline members in this guild, returned from the GET /guilds/ endpoint when with_counts is true */ - approximate_presence_count?: number; - /** Guild NSFW level */ - nsfw_level: GuildNsfwLevel; - /** Whether the guild has the boost progress bar enabled */ - premium_progress_bar_enabled: boolean; + /** Guild name (2-100 characters, excluding trailing and leading whitespace) */ + name: string; + /** True if the user is the owner of the guild */ + owner?: boolean; + /** Afk timeout in seconds */ + afk_timeout: number; + /** True if the server widget is enabled */ + widget_enabled?: boolean; + /** Verification level required for the guild */ + verification_level: VerificationLevels; + /** Default message notifications level */ + default_message_notifications: DefaultMessageNotificationLevels; + /** Explicit content filter level */ + explicit_content_filter: ExplicitContentFilterLevels; + /** Enabled guild features */ + features: GuildFeatures[]; + /** Required MFA level for the guild */ + mfa_level: MfaLevels; + /** System channel flags */ + system_channel_flags: SystemChannelFlags; + /** True if this is considered a large guild */ + large?: boolean; + /** True if this guild is unavailable due to an outage */ + unavailable?: boolean; + /** Total number of members in this guild */ + member_count?: number; + /** The maximum number of presences for the guild (the default value, currently 25000, is in effect when null is returned) */ + max_presences?: number | null; + /** The maximum number of members for the guild */ + max_members?: number; + /** The vanity url code for the guild */ + vanity_url_code: string | null; + /** The description of a guild */ + description: string | null; + /** Premium tier (Server Boost level) */ + premium_tier: PremiumTiers; + /** The number of boosts this guild currently has */ + premium_subscription_count?: number; + /** The maximum amount of users in a video channel */ + max_video_channel_users?: number; + /** Approximate number of members in this guild, returned from the GET /guilds/ endpoint when with_counts is true */ + approximate_member_count?: number; + /** Approximate number of non-offline members in this guild, returned from the GET /guilds/ endpoint when with_counts is true */ + approximate_presence_count?: number; + /** Guild NSFW level */ + nsfw_level: GuildNsfwLevel; + /** Whether the guild has the boost progress bar enabled */ + premium_progress_bar_enabled: boolean; - /** Guild id */ - id: string; - /** Icon hash */ - icon: string | null; - /** Icon hash, returned when in the template object */ - icon_hash?: string | null; - /** Splash hash */ - splash: string | null; - /** Discovery splash hash; only present for guilds with the "DISCOVERABLE" feature */ - discovery_splash: string | null; - /** Id of the owner */ - owner_id: string; - /** Total permissions for the user in the guild (excludes overwrites) */ - permissions?: string; - /** Id of afk channel */ - afk_channel_id: string | null; - /** The channel id that the widget will generate an invite to, or null if set to no invite */ - widget_channel_id?: string | null; - /** Roles in the guild */ - roles: DiscordRole[]; - /** Custom guild emojis */ - emojis: DiscordEmoji[]; - /** Application id of the guild creator if it is bot-created */ - application_id: string | null; - /** The id of the channel where guild notices such as welcome messages and boost events are posted */ - system_channel_id: string | null; - /** The id of the channel where community guilds can display rules and/or guidelines */ - rules_channel_id: string | null; - /** When this guild was joined at */ - joined_at?: string; - /** States of members currently in voice channels; lacks the guild_id key */ - voice_states?: Omit[]; - /** Users in the guild */ - members?: DiscordMember[]; - /** Channels in the guild */ - channels?: DiscordChannel[]; - // TODO: check if need to omit - /** All active threads in the guild that the current user has permission to view */ - threads?: DiscordChannel[]; - /** Presences of the members in the guild, will only include non-offline members if the size is greater than large threshold */ - presences?: Partial[]; - /** Banner hash */ - banner: string | null; - // TODO: Can be optimized to a number but is it worth it? - /** The preferred locale of a Community guild; used in server discovery and notices from Discord; defaults to "en-US" */ - preferred_locale: string; - /** The id of the channel where admins and moderators of Community guilds receive notices from Discord */ - public_updates_channel_id: string | null; - /** The welcome screen of a Community guild, shown to new members, returned in an Invite's guild object */ - welcome_screen?: DiscordWelcomeScreen; - /** Stage instances in the guild */ - stage_instances?: DiscordStageInstance[]; + /** Guild id */ + id: string; + /** Icon hash */ + icon: string | null; + /** Icon hash, returned when in the template object */ + icon_hash?: string | null; + /** Splash hash */ + splash: string | null; + /** Discovery splash hash; only present for guilds with the "DISCOVERABLE" feature */ + discovery_splash: string | null; + /** Id of the owner */ + owner_id: string; + /** Total permissions for the user in the guild (excludes overwrites) */ + permissions?: string; + /** Id of afk channel */ + afk_channel_id: string | null; + /** The channel id that the widget will generate an invite to, or null if set to no invite */ + widget_channel_id?: string | null; + /** Roles in the guild */ + roles: DiscordRole[]; + /** Custom guild emojis */ + emojis: DiscordEmoji[]; + /** Application id of the guild creator if it is bot-created */ + application_id: string | null; + /** The id of the channel where guild notices such as welcome messages and boost events are posted */ + system_channel_id: string | null; + /** The id of the channel where community guilds can display rules and/or guidelines */ + rules_channel_id: string | null; + /** When this guild was joined at */ + joined_at?: string; + /** States of members currently in voice channels; lacks the guild_id key */ + voice_states?: Omit[]; + /** Users in the guild */ + members?: DiscordMember[]; + /** Channels in the guild */ + channels?: DiscordChannel[]; + // TODO: check if need to omit + /** All active threads in the guild that the current user has permission to view */ + threads?: DiscordChannel[]; + /** Presences of the members in the guild, will only include non-offline members if the size is greater than large threshold */ + presences?: Partial[]; + /** Banner hash */ + banner: string | null; + // TODO: Can be optimized to a number but is it worth it? + /** The preferred locale of a Community guild; used in server discovery and notices from Discord; defaults to "en-US" */ + preferred_locale: string; + /** The id of the channel where admins and moderators of Community guilds receive notices from Discord */ + public_updates_channel_id: string | null; + /** The welcome screen of a Community guild, shown to new members, returned in an Invite's guild object */ + welcome_screen?: DiscordWelcomeScreen; + /** Stage instances in the guild */ + stage_instances?: DiscordStageInstance[]; } /** https://discord.com/developers/docs/topics/permissions#role-object-role-structure */ export interface DiscordRole { - /** Role id */ - id: string; - /** If this role is showed separately in the user listing */ - hoist: boolean; - /** Permission bit set */ - permissions: string; - /** Whether this role is managed by an integration */ - managed: boolean; - /** Whether this role is mentionable */ - mentionable: boolean; - /** The tags this role has */ - tags?: DiscordRoleTags; - /** the role emoji hash */ - icon?: string; - /** Role name */ - name: string; - /** Integer representation of hexadecimal color code */ - color: number; - /** Position of this role */ - position: number; - /** role unicode emoji */ - unicode_emoji?: string; + /** Role id */ + id: string; + /** If this role is showed separately in the user listing */ + hoist: boolean; + /** Permission bit set */ + permissions: string; + /** Whether this role is managed by an integration */ + managed: boolean; + /** Whether this role is mentionable */ + mentionable: boolean; + /** The tags this role has */ + tags?: DiscordRoleTags; + /** the role emoji hash */ + icon?: string; + /** Role name */ + name: string; + /** Integer representation of hexadecimal color code */ + color: number; + /** Position of this role */ + position: number; + /** role unicode emoji */ + unicode_emoji?: string; } /** https://discord.com/developers/docs/topics/permissions#role-object-role-tags-structure */ export interface DiscordRoleTags { - /** The id of the bot this role belongs to */ - bot_id?: string; - /** The id of the integration this role belongs to */ - integration_id?: string; - /** Whether this is the guild's premium subscriber role */ - premium_subscriber?: null; + /** The id of the bot this role belongs to */ + bot_id?: string; + /** The id of the integration this role belongs to */ + integration_id?: string; + /** Whether this is the guild's premium subscriber role */ + premium_subscriber?: null; } /** https://discord.com/developers/docs/resources/emoji#emoji-object-emoji-structure */ export interface DiscordEmoji { - /** Emoji name (can only be null in reaction emoji objects) */ - name?: string; + /** Emoji name (can only be null in reaction emoji objects) */ + name?: string; - /** Emoji id */ - id?: string; - /** Roles allowed to use this emoji */ - roles?: string[]; - /** User that created this emoji */ - user?: DiscordUser; - /** Whether this emoji must be wrapped in colons */ - require_colons?: boolean; - /** Whether this emoji is managed */ - managed?: boolean; - /** Whether this emoji is animated */ - animated?: boolean; - /** Whether this emoji can be used, may be false due to loss of Server Boosts */ - available?: boolean; + /** Emoji id */ + id?: string; + /** Roles allowed to use this emoji */ + roles?: string[]; + /** User that created this emoji */ + user?: DiscordUser; + /** Whether this emoji must be wrapped in colons */ + require_colons?: boolean; + /** Whether this emoji is managed */ + managed?: boolean; + /** Whether this emoji is animated */ + animated?: boolean; + /** Whether this emoji can be used, may be false due to loss of Server Boosts */ + available?: boolean; } /** https://discord.com/developers/docs/resources/voice#voice-state-object-voice-state-structure */ export interface DiscordVoiceState { - /** The session id for this voice state */ - session_id: string; + /** The session id for this voice state */ + session_id: string; - /** The guild id this voice state is for */ - guild_id?: string; - /** The channel id this user is connected to */ - channel_id: string | null; - /** The user id this voice state is for */ - user_id: string; - /** The guild member this voice state is for */ - member?: DiscordMemberWithUser; - /** Whether this user is deafened by the server */ - deaf: boolean; - /** Whether this user is muted by the server */ - mute: boolean; - /** Whether this user is locally deafened */ - self_deaf: boolean; - /** Whether this user is locally muted */ - self_mute: boolean; - /** Whether this user is streaming using "Go Live" */ - self_stream?: boolean; - /** Whether this user's camera is enabled */ - self_video: boolean; - /** Whether this user is muted by the current user */ - suppress: boolean; - /** The time at which the user requested to speak */ - request_to_speak_timestamp: string | null; + /** The guild id this voice state is for */ + guild_id?: string; + /** The channel id this user is connected to */ + channel_id: string | null; + /** The user id this voice state is for */ + user_id: string; + /** The guild member this voice state is for */ + member?: DiscordMemberWithUser; + /** Whether this user is deafened by the server */ + deaf: boolean; + /** Whether this user is muted by the server */ + mute: boolean; + /** Whether this user is locally deafened */ + self_deaf: boolean; + /** Whether this user is locally muted */ + self_mute: boolean; + /** Whether this user is streaming using "Go Live" */ + self_stream?: boolean; + /** Whether this user's camera is enabled */ + self_video: boolean; + /** Whether this user is muted by the current user */ + suppress: boolean; + /** The time at which the user requested to speak */ + request_to_speak_timestamp: string | null; } /** https://discord.com/developers/docs/resources/channel#channel-object */ export interface DiscordChannel { - /** The type of channel */ - type: ChannelTypes; - /** The flags of the channel */ - flags?: ChannelFlags; - /** Sorting position of the channel */ - position?: number; - /** The name of the channel (1-100 characters) */ - name?: string; - /** The channel topic (0-1024 characters) */ - topic?: string | null; - /** The bitrate (in bits) of the voice channel */ - bitrate?: number; - /** The user limit of the voice channel */ - user_limit?: number; - /** Amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission `manage_messages` or `manage_channel`, are unaffected */ - rate_limit_per_user?: number; - /** Voice region id for the voice channel, automatic when set to null */ - rtc_region?: string | null; - /** The camera video quality mode of the voice channel, 1 when not present */ - video_quality_mode?: VideoQualityModes; - /** An approximate count of messages in a thread, stops counting at 50 */ - message_count?: number; - /** An approximate count of users in a thread, stops counting at 50 */ - member_count?: number; - /** Default duration for newly created threads, in minutes, to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080 */ - default_auto_archive_duration?: number; + /** The type of channel */ + type: ChannelTypes; + /** The flags of the channel */ + flags?: ChannelFlags; + /** Sorting position of the channel */ + position?: number; + /** The name of the channel (1-100 characters) */ + name?: string; + /** The channel topic (0-1024 characters) */ + topic?: string | null; + /** The bitrate (in bits) of the voice channel */ + bitrate?: number; + /** The user limit of the voice channel */ + user_limit?: number; + /** Amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission `manage_messages` or `manage_channel`, are unaffected */ + rate_limit_per_user?: number; + /** Voice region id for the voice channel, automatic when set to null */ + rtc_region?: string | null; + /** The camera video quality mode of the voice channel, 1 when not present */ + video_quality_mode?: VideoQualityModes; + /** An approximate count of messages in a thread, stops counting at 50 */ + message_count?: number; + /** An approximate count of users in a thread, stops counting at 50 */ + member_count?: number; + /** Default duration for newly created threads, in minutes, to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080 */ + default_auto_archive_duration?: number; - /** The id of the channel */ - id: string; - /** The id of the guild */ - guild_id?: string; - /** Explicit permission overwrites for members and roles */ - permission_overwrites?: DiscordOverwrite[]; - /** Whether the channel is nsfw */ - nsfw?: boolean; - /** The id of the last message sent in this channel (may not point to an existing or valid message) */ - last_message_id?: string | null; - /** Id of the creator of the thread */ - owner_id?: string; - /** Application id of the group DM creator if it is bot-created */ - application_id?: string; - /** For guild channels: Id of the parent category for a channel (each parent category can contain up to 50 channels), for threads: id of the text channel this thread was created */ - parent_id?: string | null; - /** When the last pinned message was pinned. This may be null in events such as GUILD_CREATE when a message is not pinned. */ - last_pin_timestamp?: string | null; - /** Thread-specific fields not needed by other channels */ - thread_metadata?: DiscordThreadMetadata; - /** Thread member object for the current user, if they have joined the thread, only included on certain API endpoints */ - member?: DiscordThreadMember; - /** computed permissions for the invoking user in the channel, including overwrites, only included when part of the resolved data received on a application command interaction */ - permissions?: string; - /** When a thread is created this will be true on that channel payload for the thread. */ - newly_created?: boolean; + /** The id of the channel */ + id: string; + /** The id of the guild */ + guild_id?: string; + /** Explicit permission overwrites for members and roles */ + permission_overwrites?: DiscordOverwrite[]; + /** Whether the channel is nsfw */ + nsfw?: boolean; + /** The id of the last message sent in this channel (may not point to an existing or valid message) */ + last_message_id?: string | null; + /** Id of the creator of the thread */ + owner_id?: string; + /** Application id of the group DM creator if it is bot-created */ + application_id?: string; + /** For guild channels: Id of the parent category for a channel (each parent category can contain up to 50 channels), for threads: id of the text channel this thread was created */ + parent_id?: string | null; + /** When the last pinned message was pinned. This may be null in events such as GUILD_CREATE when a message is not pinned. */ + last_pin_timestamp?: string | null; + /** Thread-specific fields not needed by other channels */ + thread_metadata?: DiscordThreadMetadata; + /** Thread member object for the current user, if they have joined the thread, only included on certain API endpoints */ + member?: DiscordThreadMember; + /** computed permissions for the invoking user in the channel, including overwrites, only included when part of the resolved data received on a application command interaction */ + permissions?: string; + /** When a thread is created this will be true on that channel payload for the thread. */ + newly_created?: boolean; } /** https://discord.com/developers/docs/topics/gateway#presence-update */ export interface DiscordPresenceUpdate { - /** Either "idle", "dnd", "online", or "offline" */ - status: "idle" | "dnd" | "online" | "offline"; - /** The user presence is being updated for */ - user: DiscordUser; - /** id of the guild */ - guild_id: string; - /** User's current activities */ - activities: DiscordActivity[]; - /** User's platform-dependent status */ - client_status: DiscordClientStatus; + /** Either "idle", "dnd", "online", or "offline" */ + status: "idle" | "dnd" | "online" | "offline"; + /** The user presence is being updated for */ + user: DiscordUser; + /** id of the guild */ + guild_id: string; + /** User's current activities */ + activities: DiscordActivity[]; + /** User's platform-dependent status */ + client_status: DiscordClientStatus; } export interface DiscordStatusUpdate { - /** User's current activities */ - activities: DiscordActivity[]; - /** Either "idle", "dnd", "online", or "offline" */ - status: "idle" | "dnd" | "online" | "offline"; + /** User's current activities */ + activities: DiscordActivity[]; + /** Either "idle", "dnd", "online", or "offline" */ + status: "idle" | "dnd" | "online" | "offline"; } /** https://discord.com/developers/docs/resources/guild#welcome-screen-object-welcome-screen-structure */ export interface DiscordWelcomeScreen { - /** The server description shown in the welcome screen */ - description: string | null; - /** The channels shown in the welcome screen, up to 5 */ - welcome_channels: DiscordWelcomeScreenChannel[]; + /** The server description shown in the welcome screen */ + description: string | null; + /** The channels shown in the welcome screen, up to 5 */ + welcome_channels: DiscordWelcomeScreenChannel[]; } /** https://discord.com/developers/docs/resources/guild#welcome-screen-object-welcome-screen-channel-structure */ export interface DiscordWelcomeScreenChannel { - /** The description shown for the channel */ - description: string; + /** The description shown for the channel */ + description: string; - /** The channel's id */ - channel_id: string; - /** The emoji id, if the emoji is custom */ - emoji_id: string | null; - /** The emoji name if custom, the unicode character if standard, or `null` if no emoji is set */ - emoji_name: string | null; + /** The channel's id */ + channel_id: string; + /** The emoji id, if the emoji is custom */ + emoji_id: string | null; + /** The emoji name if custom, the unicode character if standard, or `null` if no emoji is set */ + emoji_name: string | null; } /** https://discord.com/developers/docs/resources/stage-instance#auto-closing-stage-instance-structure */ export interface DiscordStageInstance { - /** The topic of the Stage instance (1-120 characters) */ - topic: string; - /** The id of this Stage instance */ - id: string; - /** The guild id of the associated Stage channel */ - guild_id: string; - /** The id of the associated Stage channel */ - channel_id: string; - /** The id of the scheduled event for this Stage instance */ - guild_scheduled_event_id?: string; + /** The topic of the Stage instance (1-120 characters) */ + topic: string; + /** The id of this Stage instance */ + id: string; + /** The guild id of the associated Stage channel */ + guild_id: string; + /** The id of the associated Stage channel */ + channel_id: string; + /** The id of the scheduled event for this Stage instance */ + guild_scheduled_event_id?: string; } export interface DiscordThreadMetadata { - /** Whether the thread is archived */ - archived: boolean; - /** Duration in minutes to automatically archive the thread after recent activity */ - auto_archive_duration: 60 | 1440 | 4320 | 10080; - /** When a thread is locked, only users with `MANAGE_THREADS` can unarchive it */ - locked: boolean; - /** whether non-moderators can add other non-moderators to a thread; only available on private threads */ - invitable?: boolean; - /** Timestamp when the thread's archive status was last changed, used for calculating recent activity */ - archive_timestamp: string; - /** Timestamp when the thread was created; only populated for threads created after 2022-01-09 */ - create_timestamp?: string | null; + /** Whether the thread is archived */ + archived: boolean; + /** Duration in minutes to automatically archive the thread after recent activity */ + auto_archive_duration: 60 | 1440 | 4320 | 10080; + /** When a thread is locked, only users with `MANAGE_THREADS` can unarchive it */ + locked: boolean; + /** whether non-moderators can add other non-moderators to a thread; only available on private threads */ + invitable?: boolean; + /** Timestamp when the thread's archive status was last changed, used for calculating recent activity */ + archive_timestamp: string; + /** Timestamp when the thread was created; only populated for threads created after 2022-01-09 */ + create_timestamp?: string | null; } export interface DiscordThreadMemberBase { - /** Any user-thread settings, currently only used for notifications */ - flags: number; + /** Any user-thread settings, currently only used for notifications */ + flags: number; } export interface DiscordThreadMember { - /** Any user-thread settings, currently only used for notifications */ - flags: number; - /** The id of the thread */ - id: string; - /** The id of the user */ - user_id: string; - /** The time the current user last joined the thread */ - join_timestamp: string; + /** Any user-thread settings, currently only used for notifications */ + flags: number; + /** The id of the thread */ + id: string; + /** The id of the user */ + user_id: string; + /** The time the current user last joined the thread */ + join_timestamp: string; } export interface DiscordThreadMemberGuildCreate { - /** Any user-thread settings, currently only used for notifications */ - flags: number; - /** The time the current user last joined the thread */ - join_timestamp: string; + /** Any user-thread settings, currently only used for notifications */ + flags: number; + /** The time the current user last joined the thread */ + join_timestamp: string; } /** https://discord.com/developers/docs/topics/gateway#activity-object */ export interface DiscordActivity { - /** The activity's name */ - name: string; - /** Activity type */ - type: ActivityTypes; - /** Stream url, is validated when type is 1 */ - url?: string | null; - /** Unix timestamp of when the activity was added to the user's session */ - created_at: number; - /** What the player is currently doing */ - details?: string | null; - /** The user's current party status */ - state?: string | null; - /** Whether or not the activity is an instanced game session */ - instance?: boolean; - /** Activity flags `OR`d together, describes what the payload includes */ - flags?: number; - /** Unix timestamps for start and/or end of the game */ - timestamps?: DiscordActivityTimestamps; - /** Application id for the game */ - application_id?: string; - /** The emoji used for a custom status */ - emoji?: DiscordActivityEmoji | null; - /** Information for the current party of the player */ - party?: DiscordActivityParty; - /** Images for the presence and their hover texts */ - assets?: DiscordActivityAssets; - /** Secrets for Rich Presence joining and spectating */ - secrets?: DiscordActivitySecrets; - /** The custom buttons shown in the Rich Presence (max 2) */ - buttons?: DiscordActivityButton[]; + /** The activity's name */ + name: string; + /** Activity type */ + type: ActivityTypes; + /** Stream url, is validated when type is 1 */ + url?: string | null; + /** Unix timestamp of when the activity was added to the user's session */ + created_at: number; + /** What the player is currently doing */ + details?: string | null; + /** The user's current party status */ + state?: string | null; + /** Whether or not the activity is an instanced game session */ + instance?: boolean; + /** Activity flags `OR`d together, describes what the payload includes */ + flags?: number; + /** Unix timestamps for start and/or end of the game */ + timestamps?: DiscordActivityTimestamps; + /** Application id for the game */ + application_id?: string; + /** The emoji used for a custom status */ + emoji?: DiscordActivityEmoji | null; + /** Information for the current party of the player */ + party?: DiscordActivityParty; + /** Images for the presence and their hover texts */ + assets?: DiscordActivityAssets; + /** Secrets for Rich Presence joining and spectating */ + secrets?: DiscordActivitySecrets; + /** The custom buttons shown in the Rich Presence (max 2) */ + buttons?: DiscordActivityButton[]; } /** https://discord.com/developers/docs/topics/gateway#client-status-object */ export interface DiscordClientStatus { - /** The user's status set for an active desktop (Windows, Linux, Mac) application session */ - desktop?: string; - /** The user's status set for an active mobile (iOS, Android) application session */ - mobile?: string; - /** The user's status set for an active web (browser, bot account) application session */ - web?: string; + /** The user's status set for an active desktop (Windows, Linux, Mac) application session */ + desktop?: string; + /** The user's status set for an active mobile (iOS, Android) application session */ + mobile?: string; + /** The user's status set for an active web (browser, bot account) application session */ + web?: string; } /** https://discord.com/developers/docs/topics/gateway#activity-object-activity-timestamps */ export interface DiscordActivityTimestamps { - /** Unix time (in milliseconds) of when the activity started */ - start?: number; - /** Unix time (in milliseconds) of when the activity ends */ - end?: number; + /** Unix time (in milliseconds) of when the activity started */ + start?: number; + /** Unix time (in milliseconds) of when the activity ends */ + end?: number; } /** https://discord.com/developers/docs/topics/gateway#activity-object-activity-emoji */ export interface DiscordActivityEmoji { - /** The name of the emoji */ - name: string; - /** Whether this emoji is animated */ - animated?: boolean; - /** The id of the emoji */ - id?: string; + /** The name of the emoji */ + name: string; + /** Whether this emoji is animated */ + animated?: boolean; + /** The id of the emoji */ + id?: string; } /** https://discord.com/developers/docs/topics/gateway#activity-object-activity-party */ export interface DiscordActivityParty { - /** Used to show the party's current and maximum size */ - size?: [currentSize: number, maxSize: number]; - /** The id of the party */ - id?: string; + /** Used to show the party's current and maximum size */ + size?: [currentSize: number, maxSize: number]; + /** The id of the party */ + id?: string; } /** https://discord.com/developers/docs/topics/gateway#activity-object-activity-assets */ export interface DiscordActivityAssets { - /** Text displayed when hovering over the large image of the activity */ - large_text?: string; - /** Text displayed when hovering over the small image of the activity */ - small_text?: string; - /** The id for a large asset of the activity, usually a snowflake */ - large_image?: string; - /** The id for a small asset of the activity, usually a snowflake */ - small_image?: string; + /** Text displayed when hovering over the large image of the activity */ + large_text?: string; + /** Text displayed when hovering over the small image of the activity */ + small_text?: string; + /** The id for a large asset of the activity, usually a snowflake */ + large_image?: string; + /** The id for a small asset of the activity, usually a snowflake */ + small_image?: string; } /** https://discord.com/developers/docs/topics/gateway#activity-object-activity-secrets */ export interface DiscordActivitySecrets { - /** The secret for joining a party */ - join?: string; - /** The secret for spectating a game */ - spectate?: string; - /** The secret for a specific instanced match */ - match?: string; + /** The secret for joining a party */ + join?: string; + /** The secret for spectating a game */ + spectate?: string; + /** The secret for a specific instanced match */ + match?: string; } /** https://discord.com/developers/docs/topics/gateway#activity-object-activity-buttons */ export interface DiscordActivityButton { - /** The text shown on the button (1-32 characters) */ - label: string; - /** The url opened when clicking the button (1-512 characters) */ - url: string; + /** The text shown on the button (1-32 characters) */ + label: string; + /** The url opened when clicking the button (1-512 characters) */ + url: string; } export interface DiscordOverwrite { - /** Either 0 (role) or 1 (member) */ - type: OverwriteTypes; - /** Role or user id */ - id: string; - /** Permission bit set */ - allow?: string; - /** Permission bit set */ - deny?: string; + /** Either 0 (role) or 1 (member) */ + type: OverwriteTypes; + /** Role or user id */ + id: string; + /** Permission bit set */ + allow?: string; + /** Permission bit set */ + deny?: string; } export interface DiscordMemberWithUser extends DiscordMember { - /** The user object for this member */ - user: DiscordUser; + /** The user object for this member */ + user: DiscordUser; } /** https://discord.com/developers/docs/resources/channel#message-object */ export interface DiscordMessage { - /** id of the message */ - id: string; - /** id of the channel the message was sent in */ - channel_id: string; - /** - * id of the guild the message was sent in - * Note: For MESSAGE_CREATE and MESSAGE_UPDATE events, the message object may not contain a guild_id or member field since the events are sent directly to the receiving user and the bot who sent the message, rather than being sent through the guild like non-ephemeral messages. - */ - guild_id?: string; - /** - * The author of this message (not guaranteed to be a valid user) - * Note: The author object follows the structure of the user object, but is only a valid user in the case where the message is generated by a user or bot user. If the message is generated by a webhook, the author object corresponds to the webhook's id, username, and avatar. You can tell if a message is generated by a webhook by checking for the webhook_id on the message object. - */ - author: DiscordUser; - /** - * Member properties for this message's author - * Note: The member object exists in `MESSAGE_CREATE` and `MESSAGE_UPDATE` events from text-based guild channels. This allows bots to obtain real-time member data without requiring bots to store member state in memory. - */ - member?: DiscordMember; - /** Contents of the message */ - content?: string; - /** When this message was sent */ - timestamp: string; - /** When this message was edited (or null if never) */ - edited_timestamp: string | null; - /** Whether this was a TTS message */ - tts: boolean; - /** Whether this message mentions everyone */ - mention_everyone: boolean; - /** - * Users specifically mentioned in the message - * Note: The user objects in the mentions array will only have the partial member field present in `MESSAGE_CREATE` and `MESSAGE_UPDATE` events from text-based guild channels. - */ - mentions?: (DiscordUser & { member?: Partial })[]; - /** Roles specifically mentioned in this message */ - mention_roles?: string[]; - /** - * Channels specifically mentioned in this message - * Note: Not all channel mentions in a message will appear in `mention_channels`. Only textual channels that are visible to everyone in a lurkable guild will ever be included. Only crossposted messages (via Channel Following) currently include `mention_channels` at all. If no mentions in the message meet these requirements, this field will not be sent. - */ - mention_channels?: DiscordChannelMention[]; - /** Any attached files */ - attachments: DiscordAttachment[]; - /** Any embedded content */ - embeds: DiscordEmbed[]; - /** Reactions to the message */ - reactions?: DiscordReaction[]; - /** Used for validating a message was sent */ - nonce?: number | string; - /** Whether this message is pinned */ - pinned: boolean; - /** If the message is generated by a webhook, this is the webhook's id */ - webhook_id?: string; - /** Type of message */ - type: MessageTypes; - /** Sent with Rich Presence-related chat embeds */ - activity?: DiscordMessageActivity; - /** Sent with Rich Presence-related chat embeds */ - application?: Partial; - /** if the message is an Interaction or application-owned webhook, this is the id of the application */ - application_id?: string; - /** Data showing the source of a crossposted channel follow add, pin or reply message */ - message_reference?: Omit; - /** Message flags combined as a bitfield */ - flags?: number; - /** - * The stickers sent with the message (bots currently can only receive messages with stickers, not send) - * @deprecated - */ - stickers?: DiscordSticker[]; - /** - * The message associated with the `message_reference` - * Note: This field is only returned for messages with a `type` of `19` (REPLY). If the message is a reply but the `referenced_message` field is not present, the backend did not attempt to fetch the message that was being replied to, so its state is unknown. If the field exists but is null, the referenced message was deleted. - */ - referenced_message?: DiscordMessage; - /** Sent if the message is a response to an Interaction */ - interaction?: DiscordMessageInteraction; - /** The thread that was started from this message, includes thread member object */ - thread?: Omit & { member: DiscordThreadMember }; - /** The components related to this message */ - components?: DiscordMessageComponents; - /** Sent if the message contains stickers */ - sticker_items?: DiscordStickerItem[]; + /** id of the message */ + id: string; + /** id of the channel the message was sent in */ + channel_id: string; + /** + * id of the guild the message was sent in + * Note: For MESSAGE_CREATE and MESSAGE_UPDATE events, the message object may not contain a guild_id or member field since the events are sent directly to the receiving user and the bot who sent the message, rather than being sent through the guild like non-ephemeral messages. + */ + guild_id?: string; + /** + * The author of this message (not guaranteed to be a valid user) + * Note: The author object follows the structure of the user object, but is only a valid user in the case where the message is generated by a user or bot user. If the message is generated by a webhook, the author object corresponds to the webhook's id, username, and avatar. You can tell if a message is generated by a webhook by checking for the webhook_id on the message object. + */ + author: DiscordUser; + /** + * Member properties for this message's author + * Note: The member object exists in `MESSAGE_CREATE` and `MESSAGE_UPDATE` events from text-based guild channels. This allows bots to obtain real-time member data without requiring bots to store member state in memory. + */ + member?: DiscordMember; + /** Contents of the message */ + content?: string; + /** When this message was sent */ + timestamp: string; + /** When this message was edited (or null if never) */ + edited_timestamp: string | null; + /** Whether this was a TTS message */ + tts: boolean; + /** Whether this message mentions everyone */ + mention_everyone: boolean; + /** + * Users specifically mentioned in the message + * Note: The user objects in the mentions array will only have the partial member field present in `MESSAGE_CREATE` and `MESSAGE_UPDATE` events from text-based guild channels. + */ + mentions?: (DiscordUser & { member?: Partial })[]; + /** Roles specifically mentioned in this message */ + mention_roles?: string[]; + /** + * Channels specifically mentioned in this message + * Note: Not all channel mentions in a message will appear in `mention_channels`. Only textual channels that are visible to everyone in a lurkable guild will ever be included. Only crossposted messages (via Channel Following) currently include `mention_channels` at all. If no mentions in the message meet these requirements, this field will not be sent. + */ + mention_channels?: DiscordChannelMention[]; + /** Any attached files */ + attachments: DiscordAttachment[]; + /** Any embedded content */ + embeds: DiscordEmbed[]; + /** Reactions to the message */ + reactions?: DiscordReaction[]; + /** Used for validating a message was sent */ + nonce?: number | string; + /** Whether this message is pinned */ + pinned: boolean; + /** If the message is generated by a webhook, this is the webhook's id */ + webhook_id?: string; + /** Type of message */ + type: MessageTypes; + /** Sent with Rich Presence-related chat embeds */ + activity?: DiscordMessageActivity; + /** Sent with Rich Presence-related chat embeds */ + application?: Partial; + /** if the message is an Interaction or application-owned webhook, this is the id of the application */ + application_id?: string; + /** Data showing the source of a crossposted channel follow add, pin or reply message */ + message_reference?: Omit; + /** Message flags combined as a bitfield */ + flags?: number; + /** + * The stickers sent with the message (bots currently can only receive messages with stickers, not send) + * @deprecated + */ + stickers?: DiscordSticker[]; + /** + * The message associated with the `message_reference` + * Note: This field is only returned for messages with a `type` of `19` (REPLY). If the message is a reply but the `referenced_message` field is not present, the backend did not attempt to fetch the message that was being replied to, so its state is unknown. If the field exists but is null, the referenced message was deleted. + */ + referenced_message?: DiscordMessage; + /** Sent if the message is a response to an Interaction */ + interaction?: DiscordMessageInteraction; + /** The thread that was started from this message, includes thread member object */ + thread?: Omit & { member: DiscordThreadMember }; + /** The components related to this message */ + components?: DiscordMessageComponents; + /** Sent if the message contains stickers */ + sticker_items?: DiscordStickerItem[]; } /** https://discord.com/developers/docs/resources/channel#channel-mention-object */ export interface DiscordChannelMention { - /** id of the channel */ - id: string; - /** id of the guild containing the channel */ - guild_id: string; - /** The type of channel */ - type: number; - /** The name of the channel */ - name: string; + /** id of the channel */ + id: string; + /** id of the guild containing the channel */ + guild_id: string; + /** The type of channel */ + type: number; + /** The name of the channel */ + name: string; } /** https://discord.com/developers/docs/resources/channel#reaction-object */ export interface DiscordReaction { - /** Times this emoji has been used to react */ - count: number; - /** Whether the current user reacted using this emoji */ - me: boolean; - /** Emoji information */ - emoji: Partial; + /** Times this emoji has been used to react */ + count: number; + /** Whether the current user reacted using this emoji */ + me: boolean; + /** Emoji information */ + emoji: Partial; } /** https://discord.com/developers/docs/resources/channel#message-object-message-activity-structure */ export interface DiscordMessageActivity { - /** Type of message activity */ - type: MessageActivityTypes; - /** `party_id` from a Rich Presence event */ - party_id?: string; + /** Type of message activity */ + type: MessageActivityTypes; + /** `party_id` from a Rich Presence event */ + party_id?: string; } /** https://discord.com/developers/docs/resources/channel#message-object-message-reference-structure */ export interface DiscordMessageReference { - /** id of the originating message */ - message_id?: string; - /** - * id of the originating message's channel - * Note: `channel_id` is optional when creating a reply, but will always be present when receiving an event/response that includes this data model. - */ - channel_id?: string; - /** id of the originating message's guild */ - guild_id?: string; - /** When sending, whether to error if the referenced message doesn't exist instead of sending as a normal (non-reply) message, default true */ - fail_if_not_exists: boolean; + /** id of the originating message */ + message_id?: string; + /** + * id of the originating message's channel + * Note: `channel_id` is optional when creating a reply, but will always be present when receiving an event/response that includes this data model. + */ + channel_id?: string; + /** id of the originating message's guild */ + guild_id?: string; + /** When sending, whether to error if the referenced message doesn't exist instead of sending as a normal (non-reply) message, default true */ + fail_if_not_exists: boolean; } /** https://discord.com/developers/docs/resources/sticker#sticker-object-sticker-structure */ export interface DiscordSticker { - /** [Id of the sticker](https://discord.com/developers/docs/reference#image-formatting) */ - id: string; - /** Id of the pack the sticker is from */ - pack_id?: string; - /** Name of the sticker */ - name: string; - /** Description of the sticker */ - description: string; - /** a unicode emoji representing the sticker's expression */ - tags: string; - /** [type of sticker](https://discord.com/developers/docs/resources/sticker#sticker-object-sticker-types) */ - type: StickerTypes; - /** [Type of sticker format](https://discord.com/developers/docs/resources/sticker#sticker-object-sticker-format-types) */ - format_type: StickerFormatTypes; - /** Whether or not the sticker is available */ - available?: boolean; - /** Id of the guild that owns this sticker */ - guild_id?: string; - /** The user that uploaded the sticker */ - user?: DiscordUser; - /** A sticker's sort order within a pack */ - sort_value?: number; + /** [Id of the sticker](https://discord.com/developers/docs/reference#image-formatting) */ + id: string; + /** Id of the pack the sticker is from */ + pack_id?: string; + /** Name of the sticker */ + name: string; + /** Description of the sticker */ + description: string; + /** a unicode emoji representing the sticker's expression */ + tags: string; + /** [type of sticker](https://discord.com/developers/docs/resources/sticker#sticker-object-sticker-types) */ + type: StickerTypes; + /** [Type of sticker format](https://discord.com/developers/docs/resources/sticker#sticker-object-sticker-format-types) */ + format_type: StickerFormatTypes; + /** Whether or not the sticker is available */ + available?: boolean; + /** Id of the guild that owns this sticker */ + guild_id?: string; + /** The user that uploaded the sticker */ + user?: DiscordUser; + /** A sticker's sort order within a pack */ + sort_value?: number; } /** https://discord.com/developers/docs/interactions/receiving-and-responding#message-interaction-object-message-interaction-structure */ export interface DiscordMessageInteraction { - /** Id of the interaction */ - id: string; - /** The type of interaction */ - type: InteractionTypes; - /** The name of the ApplicationCommand */ - name: string; - /** The user who invoked the interaction */ - user: DiscordUser; - /** The member who invoked the interaction in the guild */ - member?: Partial; + /** Id of the interaction */ + id: string; + /** The type of interaction */ + type: InteractionTypes; + /** The name of the ApplicationCommand */ + name: string; + /** The user who invoked the interaction */ + user: DiscordUser; + /** The member who invoked the interaction in the guild */ + member?: Partial; } export type DiscordMessageComponents = DiscordActionRow[]; /** https://discord.com/developers/docs/interactions/message-components#actionrow */ export interface DiscordActionRow { - /** Action rows are a group of buttons. */ - type: 1; - /** The components in this row */ - components: - | [DiscordSelectMenuComponent | DiscordButtonComponent | DiscordInputTextComponent] - | [DiscordButtonComponent, DiscordButtonComponent] - | [DiscordButtonComponent, DiscordButtonComponent, DiscordButtonComponent] - | [DiscordButtonComponent, DiscordButtonComponent, DiscordButtonComponent, DiscordButtonComponent] - | [ - DiscordButtonComponent, - DiscordButtonComponent, - DiscordButtonComponent, - DiscordButtonComponent, - DiscordButtonComponent, - ]; + /** Action rows are a group of buttons. */ + type: 1; + /** The components in this row */ + components: + | [DiscordSelectMenuComponent | DiscordButtonComponent | DiscordInputTextComponent] + | [DiscordButtonComponent, DiscordButtonComponent] + | [DiscordButtonComponent, DiscordButtonComponent, DiscordButtonComponent] + | [DiscordButtonComponent, DiscordButtonComponent, DiscordButtonComponent, DiscordButtonComponent] + | [ + DiscordButtonComponent, + DiscordButtonComponent, + DiscordButtonComponent, + DiscordButtonComponent, + DiscordButtonComponent, + ]; } export interface DiscordSelectMenuComponent { - type: MessageComponentTypes.SelectMenu; - /** A custom identifier for this component. Maximum 100 characters. */ - custom_id: string; - /** A custom placeholder text if nothing is selected. Maximum 150 characters. */ - placeholder?: string; - /** The minimum number of items that must be selected. Default 1. Between 1-25. */ - min_values?: number; - /** The maximum number of items that can be selected. Default 1. Between 1-25. */ - max_values?: number; - /** The choices! Maximum of 25 items. */ - options: DiscordSelectOption[]; + type: MessageComponentTypes.SelectMenu; + /** A custom identifier for this component. Maximum 100 characters. */ + custom_id: string; + /** A custom placeholder text if nothing is selected. Maximum 150 characters. */ + placeholder?: string; + /** The minimum number of items that must be selected. Default 1. Between 1-25. */ + min_values?: number; + /** The maximum number of items that can be selected. Default 1. Between 1-25. */ + max_values?: number; + /** The choices! Maximum of 25 items. */ + options: DiscordSelectOption[]; } export interface DiscordSelectOption { - /** The user-facing name of the option. Maximum 25 characters. */ - label: string; - /** The dev-defined value of the option. Maximum 100 characters. */ - value: string; - /** An additional description of the option. Maximum 50 characters. */ - description?: string; - /** The id, name, and animated properties of an emoji. */ - emoji?: { - /** Emoji id */ - id?: string; - /** Emoji name */ - name?: string; - /** Whether this emoji is animated */ - animated?: boolean; - }; - /** Will render this option as already-selected by default. */ - default?: boolean; + /** The user-facing name of the option. Maximum 25 characters. */ + label: string; + /** The dev-defined value of the option. Maximum 100 characters. */ + value: string; + /** An additional description of the option. Maximum 50 characters. */ + description?: string; + /** The id, name, and animated properties of an emoji. */ + emoji?: { + /** Emoji id */ + id?: string; + /** Emoji name */ + name?: string; + /** Whether this emoji is animated */ + animated?: boolean; + }; + /** Will render this option as already-selected by default. */ + default?: boolean; } /** https://discord.com/developers/docs/interactions/message-components#buttons-button-object */ export interface DiscordButtonComponent { - /** All button components have type 2 */ - type: MessageComponentTypes.Button; - /** for what the button says (max 80 characters) */ - label: string; - /** a dev-defined unique string sent on click (max 100 characters). type 5 Link buttons can not have a custom_id */ - custom_id?: string; - /** For different styles/colors of the buttons */ - style: ButtonStyles; - /** Emoji object that includes fields of name, id, and animated supporting unicode and custom emojis. */ - emoji?: { - /** Emoji id */ - id?: string; - /** Emoji name */ - name?: string; - /** Whether this emoji is animated */ - animated?: boolean; - }; - /** optional url for link-style buttons that can navigate a user to the web. Only type 5 Link buttons can have a url */ - url?: string; - /** Whether or not this button is disabled */ - disabled?: boolean; + /** All button components have type 2 */ + type: MessageComponentTypes.Button; + /** for what the button says (max 80 characters) */ + label: string; + /** a dev-defined unique string sent on click (max 100 characters). type 5 Link buttons can not have a custom_id */ + custom_id?: string; + /** For different styles/colors of the buttons */ + style: ButtonStyles; + /** Emoji object that includes fields of name, id, and animated supporting unicode and custom emojis. */ + emoji?: { + /** Emoji id */ + id?: string; + /** Emoji name */ + name?: string; + /** Whether this emoji is animated */ + animated?: boolean; + }; + /** optional url for link-style buttons that can navigate a user to the web. Only type 5 Link buttons can have a url */ + url?: string; + /** Whether or not this button is disabled */ + disabled?: boolean; } /** https://discord.com/developers/docs/interactions/message-components#text-inputs-text-input-structure */ export interface DiscordInputTextComponent { - /** InputText Component is of type 3 */ - type: MessageComponentTypes.InputText; - /** The style of the InputText */ - style: TextStyles; - /** The customId of the InputText */ - custom_id: string; - /** The label of the InputText */ - label: string; - /** The placeholder of the InputText */ - placeholder?: string; - /** The minimum length of the text the user has to provide */ - min_length?: number; - /** The maximum length of the text the user has to provide */ - max_length?: number; - /** Whether or not this input is required. */ - required?: boolean; - /** Pre-filled value for input text. */ - value?: string; + /** InputText Component is of type 3 */ + type: MessageComponentTypes.InputText; + /** The style of the InputText */ + style: TextStyles; + /** The customId of the InputText */ + custom_id: string; + /** The label of the InputText */ + label: string; + /** The placeholder of the InputText */ + placeholder?: string; + /** The minimum length of the text the user has to provide */ + min_length?: number; + /** The maximum length of the text the user has to provide */ + max_length?: number; + /** Whether or not this input is required. */ + required?: boolean; + /** Pre-filled value for input text. */ + value?: string; } /** https://discord.com/developers/docs/resources/sticker#sticker-item-object-sticker-item-structure */ export interface DiscordStickerItem { - /** Id of the sticker */ - id: string; - /** Name of the sticker */ - name: string; - /** [Type of sticker format](https://discord.com/developers/docs/resources/sticker#sticker-object-sticker-format-types) */ - format_type: StickerFormatTypes; + /** Id of the sticker */ + id: string; + /** Name of the sticker */ + name: string; + /** [Type of sticker format](https://discord.com/developers/docs/resources/sticker#sticker-object-sticker-format-types) */ + format_type: StickerFormatTypes; } /** https://discord.com/developers/docs/resources/sticker#sticker-pack-object-sticker-pack-structure */ export interface DiscordStickerPack { - /** id of the sticker pack */ - id: string; - /** the stickers in the pack */ - stickers: DiscordSticker[]; - /** name of the sticker pack */ - name: string; - /** id of the pack's SKU */ - sku_id: string; - /** id of a sticker in the pack which is shown as the pack's icon */ - cover_sticker_id?: string; - /** description of the sticker pack */ - description: string; - /** id of the sticker pack's [banner image](https://discord.com/developers/docs/reference#image-formatting) */ - banner_asset_id?: string; + /** id of the sticker pack */ + id: string; + /** the stickers in the pack */ + stickers: DiscordSticker[]; + /** name of the sticker pack */ + name: string; + /** id of the pack's SKU */ + sku_id: string; + /** id of a sticker in the pack which is shown as the pack's icon */ + cover_sticker_id?: string; + /** description of the sticker pack */ + description: string; + /** id of the sticker pack's [banner image](https://discord.com/developers/docs/reference#image-formatting) */ + banner_asset_id?: string; } export interface DiscordInteraction { - /** Id of the interaction */ - id: string; - /** Id of the application this interaction is for */ - application_id: string; - /** The type of interaction */ - type: InteractionTypes; - /** The guild it was sent from */ - guild_id?: string; - /** The channel it was sent from */ - channel_id?: string; - /** Guild member data for the invoking user, including permissions */ - member?: DiscordInteractionMember; - /** User object for the invoking user, if invoked in a DM */ - user?: DiscordUser; - /** A continuation token for responding to the interaction */ - token: string; - /** Read-only property, always `1` */ - version: 1; - /** For the message the button was attached to */ - message?: DiscordMessage; - /** the command data payload */ - data?: DiscordInteractionData; - /** The selected language of the invoking user */ - locale?: string; - /** The guild's preferred locale, if invoked in a guild */ - guild_locale?: string; + /** Id of the interaction */ + id: string; + /** Id of the application this interaction is for */ + application_id: string; + /** The type of interaction */ + type: InteractionTypes; + /** The guild it was sent from */ + guild_id?: string; + /** The channel it was sent from */ + channel_id?: string; + /** Guild member data for the invoking user, including permissions */ + member?: DiscordInteractionMember; + /** User object for the invoking user, if invoked in a DM */ + user?: DiscordUser; + /** A continuation token for responding to the interaction */ + token: string; + /** Read-only property, always `1` */ + version: 1; + /** For the message the button was attached to */ + message?: DiscordMessage; + /** the command data payload */ + data?: DiscordInteractionData; + /** The selected language of the invoking user */ + locale?: string; + /** The guild's preferred locale, if invoked in a guild */ + guild_locale?: string; } /** https://discord.com/developers/docs/resources/guild#guild-member-object */ export interface DiscordInteractionMember extends DiscordMemberWithUser { - /** Total permissions of the member in the channel, including overwrites, returned when in the interaction object */ - permissions: string; + /** Total permissions of the member in the channel, including overwrites, returned when in the interaction object */ + permissions: string; } export interface DiscordInteractionData { - /** The type of component */ - component_type?: MessageComponentTypes; - /** The custom id provided for this component. */ - custom_id?: string; - /** The components if its a Modal Submit interaction. */ - components?: DiscordMessageComponents; - /** The values chosen by the user. */ - values?: string[]; - /** The Id of the invoked command */ - id: string; - /** The name of the invoked command */ - name: string; - /** the type of the invoked command */ - type: ApplicationCommandTypes; - /** Converted users + roles + channels + attachments */ - resolved?: { + /** The type of component */ + component_type?: MessageComponentTypes; + /** The custom id provided for this component. */ + custom_id?: string; + /** The components if its a Modal Submit interaction. */ + components?: DiscordMessageComponents; + /** The values chosen by the user. */ + values?: string[]; + /** The Id of the invoked command */ + id: string; + /** The name of the invoked command */ + name: string; + /** the type of the invoked command */ + type: ApplicationCommandTypes; + /** Converted users + roles + channels + attachments */ + resolved?: { + /** The Ids and Message objects */ + messages?: Record; + /** The Ids and User objects */ + users?: Record; + /** The Ids and partial Member objects */ + members?: Record>; + /** The Ids and Role objects */ + roles?: Record; + /** The Ids and partial Channel objects */ + channels?: Record>; + /** The ids and attachment objects */ + attachments: Record; + }; + /** The params + values from the user */ + options?: DiscordInteractionDataOption[]; + /** The target id if this is a context menu command. */ + target_id?: string; + /** the id of the guild the command is registered to */ + guild_id?: string; +} + +export type DiscordInteractionDataOption = { + /** Name of the parameter */ + name: string; + /** Value of application command option type */ + type: ApplicationCommandOptionTypes; + /** Value of the option resulting from user input */ + value?: string | boolean | number | DiscordMember | DiscordChannel | DiscordRole; + /** Present if this option is a group or subcommand */ + options?: DiscordInteractionDataOption[]; + /** `true` if this option is the currently focused option for autocomplete */ + focused?: boolean; +}; + +export interface DiscordInteractionDataResolved { /** The Ids and Message objects */ messages?: Record; /** The Ids and User objects */ @@ -1328,759 +1363,724 @@ export interface DiscordInteractionData { roles?: Record; /** The Ids and partial Channel objects */ channels?: Record>; - /** The ids and attachment objects */ - attachments: Record; - }; - /** The params + values from the user */ - options?: DiscordInteractionDataOption[]; - /** The target id if this is a context menu command. */ - target_id?: string; - /** the id of the guild the command is registered to */ - guild_id?: string; -} - -export type DiscordInteractionDataOption = { - /** Name of the parameter */ - name: string; - /** Value of application command option type */ - type: ApplicationCommandOptionTypes; - /** Value of the option resulting from user input */ - value?: string | boolean | number | DiscordMember | DiscordChannel | DiscordRole; - /** Present if this option is a group or subcommand */ - options?: DiscordInteractionDataOption[]; - /** `true` if this option is the currently focused option for autocomplete */ - focused?: boolean; -}; - -export interface DiscordInteractionDataResolved { - /** The Ids and Message objects */ - messages?: Record; - /** The Ids and User objects */ - users?: Record; - /** The Ids and partial Member objects */ - members?: Record>; - /** The Ids and Role objects */ - roles?: Record; - /** The Ids and partial Channel objects */ - channels?: Record>; - /** The Ids and attachments objects */ - attachments?: Record; + /** The Ids and attachments objects */ + attachments?: Record; } export interface DiscordListActiveThreads { - /** The active threads */ - threads: DiscordChannel[]; - /** A thread member object for each returned thread the current user has joined */ - members: DiscordThreadMember[]; + /** The active threads */ + threads: DiscordChannel[]; + /** A thread member object for each returned thread the current user has joined */ + members: DiscordThreadMember[]; } export interface DiscordListArchivedThreads extends DiscordListActiveThreads { - /** Whether there are potentially additional threads that could be returned on a subsequent call */ - has_more: boolean; + /** Whether there are potentially additional threads that could be returned on a subsequent call */ + has_more: boolean; } export interface DiscordThreadListSync { - /** The id of the guild */ - guild_id: string; - /** The parent channel ids whose threads are being synced. If omitted, then threads were synced for the entire guild. This array may contain channelIds that have no active threads as well, so you know to clear that data */ - channel_ids?: string[]; - /** All active threads in the given channels that the current user can access */ - threads: DiscordChannel[]; - /** All thread member objects from the synced threads for the current user, indicating which threads the current user has been added to */ - members: DiscordThreadMember[]; + /** The id of the guild */ + guild_id: string; + /** The parent channel ids whose threads are being synced. If omitted, then threads were synced for the entire guild. This array may contain channelIds that have no active threads as well, so you know to clear that data */ + channel_ids?: string[]; + /** All active threads in the given channels that the current user can access */ + threads: DiscordChannel[]; + /** All thread member objects from the synced threads for the current user, indicating which threads the current user has been added to */ + members: DiscordThreadMember[]; } /** https://discord.com/developers/docs/resources/audit-log#audit-log-object */ export interface DiscordAuditLog { - /** List of webhooks found in the audit log */ - webhooks: DiscordWebhook[]; - /** List of users found in the audit log */ - users: DiscordUser[]; - /** List of audit log entries, sorted from most to least recent */ - audit_log_entries: DiscordAuditLogEntry[]; - /** List of partial integration objects */ - integrations: Partial[]; - /** - * List of threads found in the audit log. - * Threads referenced in `THREAD_CREATE` and `THREAD_UPDATE` events are included in the threads map since archived threads might not be kept in memory by clients. - */ - threads: DiscordChannel[]; - /** List of guild scheduled events found in the audit log */ - guild_scheduled_events?: DiscordScheduledEvent[]; - /** List of auto moderation rules referenced in the audit log */ - auto_moderation_rules?: DiscordAutoModerationRule[]; + /** List of webhooks found in the audit log */ + webhooks: DiscordWebhook[]; + /** List of users found in the audit log */ + users: DiscordUser[]; + /** List of audit log entries, sorted from most to least recent */ + audit_log_entries: DiscordAuditLogEntry[]; + /** List of partial integration objects */ + integrations: Partial[]; + /** + * List of threads found in the audit log. + * Threads referenced in `THREAD_CREATE` and `THREAD_UPDATE` events are included in the threads map since archived threads might not be kept in memory by clients. + */ + threads: DiscordChannel[]; + /** List of guild scheduled events found in the audit log */ + guild_scheduled_events?: DiscordScheduledEvent[]; + /** List of auto moderation rules referenced in the audit log */ + auto_moderation_rules?: DiscordAutoModerationRule[]; } export interface DiscordAutoModerationRule { - /** The id of this rule */ - id: string; - /** The guild id */ - guild_id: string; - /** The name of the rule */ - name: string; - /** The id of the user who created this rule. */ - creator_id: string; - /** Indicates in what event context a rule should be checked. */ - event_type: AutoModerationEventTypes; - /** The type of trigger for this rule */ - trigger_type: AutoModerationTriggerTypes; - /** The metadata used to determine whether a rule should be triggered. */ - trigger_metadata: DiscordAutoModerationRuleTriggerMetadata; - /** Actions which will execute whenever a rule is triggered. */ - actions: DiscordAutoModerationAction[]; - /** Whether the rule is enabled. */ - enabled: boolean; - /** The role ids that are whitelisted. Max 20. */ - exempt_roles: string[]; - /** The channel ids that are whitelisted. Max 50. */ - exempt_channels: string[]; + /** The id of this rule */ + id: string; + /** The guild id */ + guild_id: string; + /** The name of the rule */ + name: string; + /** The id of the user who created this rule. */ + creator_id: string; + /** Indicates in what event context a rule should be checked. */ + event_type: AutoModerationEventTypes; + /** The type of trigger for this rule */ + trigger_type: AutoModerationTriggerTypes; + /** The metadata used to determine whether a rule should be triggered. */ + trigger_metadata: DiscordAutoModerationRuleTriggerMetadata; + /** Actions which will execute whenever a rule is triggered. */ + actions: DiscordAutoModerationAction[]; + /** Whether the rule is enabled. */ + enabled: boolean; + /** The role ids that are whitelisted. Max 20. */ + exempt_roles: string[]; + /** The channel ids that are whitelisted. Max 50. */ + exempt_channels: string[]; } export enum AutoModerationEventTypes { - /** When a user sends a message */ - MessageSend = 1, + /** When a user sends a message */ + MessageSend = 1, } export enum AutoModerationTriggerTypes { - Keyword = 1, - HarmfulLink, - Spam, - KeywordPreset, + Keyword = 1, + HarmfulLink, + Spam, + KeywordPreset, } export interface DiscordAutoModerationRuleTriggerMetadata { - // TODO: discord is considering renaming this before release - /** The keywords needed to match. Only present when TriggerType.Keyword */ - keyword_filter?: string[]; - /** The pre-defined lists of words to match from. Only present when TriggerType.KeywordPreset */ - presets?: DiscordAutoModerationRuleTriggerMetadataPresets[]; + // TODO: discord is considering renaming this before release + /** The keywords needed to match. Only present when TriggerType.Keyword */ + keyword_filter?: string[]; + /** The pre-defined lists of words to match from. Only present when TriggerType.KeywordPreset */ + presets?: DiscordAutoModerationRuleTriggerMetadataPresets[]; } export enum DiscordAutoModerationRuleTriggerMetadataPresets { - /** Words that may be considered forms of swearing or cursing */ - Profanity = 1, - /** Words that refer to sexually explicit behavior or activity */ - SexualContent, - /** Personal insults or words that may be considered hate speech */ - Slurs, + /** Words that may be considered forms of swearing or cursing */ + Profanity = 1, + /** Words that refer to sexually explicit behavior or activity */ + SexualContent, + /** Personal insults or words that may be considered hate speech */ + Slurs, } export interface DiscordAutoModerationAction { - /** The type of action to take when a rule is triggered */ - type: AutoModerationActionType; - /** additional metadata needed during execution for this specific action type */ - metadata: DiscordAutoModerationActionMetadata; + /** The type of action to take when a rule is triggered */ + type: AutoModerationActionType; + /** additional metadata needed during execution for this specific action type */ + metadata: DiscordAutoModerationActionMetadata; } export enum AutoModerationActionType { - /** Blocks the content of a message according to the rule */ - BlockMessage = 1, - /** Logs user content to a specified channel */ - SendAlertMessage, - /** Times out user for specified duration */ - Timeout, + /** Blocks the content of a message according to the rule */ + BlockMessage = 1, + /** Logs user content to a specified channel */ + SendAlertMessage, + /** Times out user for specified duration */ + Timeout, } export interface DiscordAutoModerationActionMetadata { - /** The id of channel to which user content should be logged. Only in ActionType.SendAlertMessage */ - channel_id?: string; - /** Timeout duration in seconds maximum of 2419200 seconds (4 weeks). Only supported for TriggerType.Keyword && Only in ActionType.Timeout */ - duration_seconds?: number; + /** The id of channel to which user content should be logged. Only in ActionType.SendAlertMessage */ + channel_id?: string; + /** Timeout duration in seconds maximum of 2419200 seconds (4 weeks). Only supported for TriggerType.Keyword && Only in ActionType.Timeout */ + duration_seconds?: number; } export interface DiscordAutoModerationActionExecution { - /** The id of the guild */ - guild_id: string; - /** The id of the rule that was executed */ - rule_id: string; - /** The id of the user which generated the content which triggered the rule */ - user_id: string; - /** The content from the user */ - content: string; - /** Action which was executed */ - action: DiscordAutoModerationAction; - /** The trigger type of the rule that was executed. */ - rule_trigger_type: AutoModerationTriggerTypes; - /** The id of the channel in which user content was posted */ - channel_id?: string | null; - /** The id of the message. Will not exist if message was blocked by automod or content was not part of any message */ - message_id?: string | null; - /** The id of any system auto moderation messages posted as a result of this action */ - alert_system_message_id?: string | null; - /** The word or phrase that triggerred the rule. */ - matched_keyword: string | null; - /** The substring in content that triggered rule */ - matched_content: string | null; + /** The id of the guild */ + guild_id: string; + /** The id of the rule that was executed */ + rule_id: string; + /** The id of the user which generated the content which triggered the rule */ + user_id: string; + /** The content from the user */ + content: string; + /** Action which was executed */ + action: DiscordAutoModerationAction; + /** The trigger type of the rule that was executed. */ + rule_trigger_type: AutoModerationTriggerTypes; + /** The id of the channel in which user content was posted */ + channel_id?: string | null; + /** The id of the message. Will not exist if message was blocked by automod or content was not part of any message */ + message_id?: string | null; + /** The id of any system auto moderation messages posted as a result of this action */ + alert_system_message_id?: string | null; + /** The word or phrase that triggerred the rule. */ + matched_keyword: string | null; + /** The substring in content that triggered rule */ + matched_content: string | null; } /** https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-entry-structure */ export interface DiscordAuditLogEntry { - /** ID of the affected entity (webhook, user, role, etc.) */ - target_id: string | null; - /** Changes made to the `target_id` */ - changes?: DiscordAuditLogChange[]; - /** User or app that made the changes */ - user_id: string | null; - /** ID of the entry */ - id: string; - /** Type of action that occurred */ - action_type: AuditLogEvents; - /** Additional info for certain event types */ - options?: DiscordOptionalAuditEntryInfo; - /** Reason for the change (1-512 characters) */ - reason?: string; + /** ID of the affected entity (webhook, user, role, etc.) */ + target_id: string | null; + /** Changes made to the `target_id` */ + changes?: DiscordAuditLogChange[]; + /** User or app that made the changes */ + user_id: string | null; + /** ID of the entry */ + id: string; + /** Type of action that occurred */ + action_type: AuditLogEvents; + /** Additional info for certain event types */ + options?: DiscordOptionalAuditEntryInfo; + /** Reason for the change (1-512 characters) */ + reason?: string; } /** https://discord.com/developers/docs/resources/audit-log#audit-log-change-object-audit-log-change-structure */ export type DiscordAuditLogChange = - | { - new_value: string; - old_value: string; - key: - | "name" - | "description" - | "discovery_splash_hash" - | "banner_hash" - | "preferred_locale" - | "rules_channel_id" - | "public_updates_channel_id" - | "icon_hash" - | "image_hash" - | "splash_hash" - | "owner_id" - | "region" - | "afk_channel_id" - | "vanity_url_code" - | "widget_channel_id" - | "system_channel_id" - | "topic" - | "application_id" - | "permissions" - | "allow" - | "deny" - | "code" - | "channel_id" - | "inviter_id" - | "nick" - | "avatar_hash" - | "id" - | "location" - | "command_id"; - } - | { - new_value: number; - old_value: number; - key: - | "afk_timeout" - | "mfa_level" - | "verification_level" - | "explicit_content_filter" - | "default_message_notifications" - | "prune_delete_days" - | "position" - | "bitrate" - | "rate_limit_per_user" - | "color" - | "max_uses" - | "uses" - | "max_age" - | "expire_behavior" - | "expire_grace_period" - | "user_limit" - | "privacy_level" - | "auto_archive_duration" - | "default_auto_archive_duration" - | "entity_type" - | "status" - | "communication_disabled_until"; - } - | { - new_value: Partial[]; - old_value?: Partial[]; - key: "$add" | "$remove"; - } - | { - new_value: boolean; - old_value: boolean; - key: - | "widget_enabled" - | "nsfw" - | "hoist" - | "mentionable" - | "temporary" - | "deaf" - | "mute" - | "enable_emoticons" - | "archived" - | "locked" - | "invitable"; - } - | { - new_value: DiscordOverwrite[]; - old_value: DiscordOverwrite[]; - key: "permission_overwrites"; - } - | { - new_value: string | number; - old_value: string | number; - key: "type"; - }; + | { + new_value: string; + old_value: string; + key: + | "name" + | "description" + | "discovery_splash_hash" + | "banner_hash" + | "preferred_locale" + | "rules_channel_id" + | "public_updates_channel_id" + | "icon_hash" + | "image_hash" + | "splash_hash" + | "owner_id" + | "region" + | "afk_channel_id" + | "vanity_url_code" + | "widget_channel_id" + | "system_channel_id" + | "topic" + | "application_id" + | "permissions" + | "allow" + | "deny" + | "code" + | "channel_id" + | "inviter_id" + | "nick" + | "avatar_hash" + | "id" + | "location" + | "command_id"; + } + | { + new_value: number; + old_value: number; + key: + | "afk_timeout" + | "mfa_level" + | "verification_level" + | "explicit_content_filter" + | "default_message_notifications" + | "prune_delete_days" + | "position" + | "bitrate" + | "rate_limit_per_user" + | "color" + | "max_uses" + | "uses" + | "max_age" + | "expire_behavior" + | "expire_grace_period" + | "user_limit" + | "privacy_level" + | "auto_archive_duration" + | "default_auto_archive_duration" + | "entity_type" + | "status" + | "communication_disabled_until"; + } + | { + new_value: Partial[]; + old_value?: Partial[]; + key: "$add" | "$remove"; + } + | { + new_value: boolean; + old_value: boolean; + key: + | "widget_enabled" + | "nsfw" + | "hoist" + | "mentionable" + | "temporary" + | "deaf" + | "mute" + | "enable_emoticons" + | "archived" + | "locked" + | "invitable"; + } + | { + new_value: DiscordOverwrite[]; + old_value: DiscordOverwrite[]; + key: "permission_overwrites"; + } + | { + new_value: string | number; + old_value: string | number; + key: "type"; + }; /** https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-optional-audit-entry-info */ export interface DiscordOptionalAuditEntryInfo { - /** - * Number of days after which inactive members were kicked. - * - * Event types: `MEMBER_PRUNE` - */ - delete_member_days: string; - /** - * Number of members removed by the prune. - * - * Event types: `MEMBER_PRUNE` - */ - members_removed: string; - /** - * Channel in which the entities were targeted. - * - * Event types: `MEMBER_MOVE`, `MESSAGE_PIN`, `MESSAGE_UNPIN`, `MESSAGE_DELETE`, `STAGE_INSTANCE_CREATE`, `STAGE_INSTANCE_UPDATE`, `STAGE_INSTANCE_DELETE` - */ - channel_id: string; - /** - * ID of the message that was targeted. - * - * Event types: `MESSAGE_PIN`, `MESSAGE_UNPIN`, `STAGE_INSTANCE_CREATE`, `STAGE_INSTANCE_UPDATE`, `STAGE_INSTANCE_DELETE` - */ - message_id: string; - /** - * Number of entities that were targeted. - * - * Event types: `MESSAGE_DELETE`, `MESSAGE_BULK_DELETE`, `MEMBER_DISCONNECT`, `MEMBER_MOVE` - */ - count: string; - /** - * ID of the overwritten entity. - * - * Event types: `CHANNEL_OVERWRITE_CREATE`, `CHANNEL_OVERWRITE_UPDATE`, `CHANNEL_OVERWRITE_DELETE` - */ - id: string; - /** - * Type of overwritten entity - "0", for "role", or "1" for "member". - * - * Event types: `CHANNEL_OVERWRITE_CREATE`, `CHANNEL_OVERWRITE_UPDATE`, `CHANNEL_OVERWRITE_DELETE` - */ - type: string; - /** - * Name of the role if type is "0" (not present if type is "1"). - * - * Event types: `CHANNEL_OVERWRITE_CREATE`, `CHANNEL_OVERWRITE_UPDATE`, `CHANNEL_OVERWRITE_DELETE` - */ - role_name: string; - /** - * ID of the app whose permissions were targeted. - * - * Event types: `APPLICATION_COMMAND_PERMISSION_UPDATE` - */ - application_id: string; + /** + * Number of days after which inactive members were kicked. + * + * Event types: `MEMBER_PRUNE` + */ + delete_member_days: string; + /** + * Number of members removed by the prune. + * + * Event types: `MEMBER_PRUNE` + */ + members_removed: string; + /** + * Channel in which the entities were targeted. + * + * Event types: `MEMBER_MOVE`, `MESSAGE_PIN`, `MESSAGE_UNPIN`, `MESSAGE_DELETE`, `STAGE_INSTANCE_CREATE`, `STAGE_INSTANCE_UPDATE`, `STAGE_INSTANCE_DELETE` + */ + channel_id: string; + /** + * ID of the message that was targeted. + * + * Event types: `MESSAGE_PIN`, `MESSAGE_UNPIN`, `STAGE_INSTANCE_CREATE`, `STAGE_INSTANCE_UPDATE`, `STAGE_INSTANCE_DELETE` + */ + message_id: string; + /** + * Number of entities that were targeted. + * + * Event types: `MESSAGE_DELETE`, `MESSAGE_BULK_DELETE`, `MEMBER_DISCONNECT`, `MEMBER_MOVE` + */ + count: string; + /** + * ID of the overwritten entity. + * + * Event types: `CHANNEL_OVERWRITE_CREATE`, `CHANNEL_OVERWRITE_UPDATE`, `CHANNEL_OVERWRITE_DELETE` + */ + id: string; + /** + * Type of overwritten entity - "0", for "role", or "1" for "member". + * + * Event types: `CHANNEL_OVERWRITE_CREATE`, `CHANNEL_OVERWRITE_UPDATE`, `CHANNEL_OVERWRITE_DELETE` + */ + type: string; + /** + * Name of the role if type is "0" (not present if type is "1"). + * + * Event types: `CHANNEL_OVERWRITE_CREATE`, `CHANNEL_OVERWRITE_UPDATE`, `CHANNEL_OVERWRITE_DELETE` + */ + role_name: string; + /** + * ID of the app whose permissions were targeted. + * + * Event types: `APPLICATION_COMMAND_PERMISSION_UPDATE` + */ + application_id: string; } export interface DiscordScheduledEvent { - /** the id of the scheduled event */ - id: string; - /** the guild id which the scheduled event belongs to */ - guild_id: string; - /** the channel id in which the scheduled event will be hosted if specified */ - channel_id: string | null; - /** the id of the user that created the scheduled event */ - creator_id?: string | null; - /** the name of the scheduled event */ - name: string; - /** the description of the scheduled event */ - description?: string; - /** the time the scheduled event will start */ - scheduled_start_time: string; - /** the time the scheduled event will end if it does end. */ - scheduled_end_time: string | null; - /** the privacy level of the scheduled event */ - privacy_level: ScheduledEventPrivacyLevel; - /** the status of the scheduled event */ - status: ScheduledEventStatus; - /** the type of hosting entity associated with a scheduled event */ - entity_type: ScheduledEventEntityType; - /** any additional id of the hosting entity associated with event */ - entity_id: string | null; - /** the entity metadata for the scheduled event */ - entity_metadata: DiscordScheduledEventEntityMetadata | null; - /** the user that created the scheduled event */ - creator?: DiscordUser; - /** the number of users subscribed to the scheduled event */ - user_count?: number; - /** the cover image hash of the scheduled event */ - image?: string | null; + /** the id of the scheduled event */ + id: string; + /** the guild id which the scheduled event belongs to */ + guild_id: string; + /** the channel id in which the scheduled event will be hosted if specified */ + channel_id: string | null; + /** the id of the user that created the scheduled event */ + creator_id?: string | null; + /** the name of the scheduled event */ + name: string; + /** the description of the scheduled event */ + description?: string; + /** the time the scheduled event will start */ + scheduled_start_time: string; + /** the time the scheduled event will end if it does end. */ + scheduled_end_time: string | null; + /** the privacy level of the scheduled event */ + privacy_level: ScheduledEventPrivacyLevel; + /** the status of the scheduled event */ + status: ScheduledEventStatus; + /** the type of hosting entity associated with a scheduled event */ + entity_type: ScheduledEventEntityType; + /** any additional id of the hosting entity associated with event */ + entity_id: string | null; + /** the entity metadata for the scheduled event */ + entity_metadata: DiscordScheduledEventEntityMetadata | null; + /** the user that created the scheduled event */ + creator?: DiscordUser; + /** the number of users subscribed to the scheduled event */ + user_count?: number; + /** the cover image hash of the scheduled event */ + image?: string | null; } export interface DiscordScheduledEventEntityMetadata { - /** location of the event */ - location?: string; + /** location of the event */ + location?: string; } /** https://discord.com/developers/docs/topics/gateway#get-gateway-bot */ export interface DiscordGetGatewayBot { - /** The WSS URL that can be used for connecting to the gateway */ - url: string; - /** The recommended number of shards to use when connecting */ - shards: number; - /** Information on the current session start limit */ - session_start_limit: DiscordSessionStartLimit; + /** The WSS URL that can be used for connecting to the gateway */ + url: string; + /** The recommended number of shards to use when connecting */ + shards: number; + /** Information on the current session start limit */ + session_start_limit: DiscordSessionStartLimit; } /** https://discord.com/developers/docs/topics/gateway#session-start-limit-object */ export interface DiscordSessionStartLimit { - /** The total number of session starts the current user is allowed */ - total: number; - /** The remaining number of session starts the current user is allowed */ - remaining: number; - /** The number of milliseconds after which the limit resets */ - reset_after: number; - /** The number of identify requests allowed per 5 seconds */ - max_concurrency: number; + /** The total number of session starts the current user is allowed */ + total: number; + /** The remaining number of session starts the current user is allowed */ + remaining: number; + /** The number of milliseconds after which the limit resets */ + reset_after: number; + /** The number of identify requests allowed per 5 seconds */ + max_concurrency: number; } /** https://discord.com/developers/docs/resources/invite#invite-metadata-object */ export interface DiscordInviteMetadata extends DiscordInvite { - /** Number of times this invite has been used */ - uses: number; - /** Max number of times this invite can be used */ - max_uses: number; - /** Duration (in seconds) after which the invite expires */ - max_age: number; - /** Whether this invite only grants temporary membership */ - temporary: boolean; - /** When this invite was created */ - created_at: string; + /** Number of times this invite has been used */ + uses: number; + /** Max number of times this invite can be used */ + max_uses: number; + /** Duration (in seconds) after which the invite expires */ + max_age: number; + /** Whether this invite only grants temporary membership */ + temporary: boolean; + /** When this invite was created */ + created_at: string; } /** https://discord.com/developers/docs/resources/invite#invite-object */ export interface DiscordInvite { - /** The invite code (unique Id) */ - code: string; - /** The guild this invite is for */ - guild?: Partial; - /** The channel this invite is for */ - channel: Partial | null; - /** The user who created the invite */ - inviter?: DiscordUser; - /** The type of target for this voice channel invite */ - target_type?: TargetTypes; - /** The target user for this invite */ - target_user?: DiscordUser; - /** The embedded application to open for this voice channel embedded application invite */ - target_application?: Partial; - /** Approximate count of online members (only present when target_user is set) */ - approximate_presence_count?: number; - /** Approximate count of total members */ - approximate_member_count?: number; - /** The expiration date of this invite, returned from the `GET /invites/` endpoint when `with_expiration` is `true` */ - expires_at?: string | null; - /** Stage instance data if there is a public Stage instance in the Stage channel this invite is for */ - stage_instance?: DiscordInviteStageInstance; - /** guild scheduled event data */ - guild_scheduled_event?: DiscordScheduledEvent; + /** The invite code (unique Id) */ + code: string; + /** The guild this invite is for */ + guild?: Partial; + /** The channel this invite is for */ + channel: Partial | null; + /** The user who created the invite */ + inviter?: DiscordUser; + /** The type of target for this voice channel invite */ + target_type?: TargetTypes; + /** The target user for this invite */ + target_user?: DiscordUser; + /** The embedded application to open for this voice channel embedded application invite */ + target_application?: Partial; + /** Approximate count of online members (only present when target_user is set) */ + approximate_presence_count?: number; + /** Approximate count of total members */ + approximate_member_count?: number; + /** The expiration date of this invite, returned from the `GET /invites/` endpoint when `with_expiration` is `true` */ + expires_at?: string | null; + /** Stage instance data if there is a public Stage instance in the Stage channel this invite is for */ + stage_instance?: DiscordInviteStageInstance; + /** guild scheduled event data */ + guild_scheduled_event?: DiscordScheduledEvent; } export interface DiscordInviteStageInstance { - /** The members speaking in the Stage */ - members: Partial[]; - /** The number of users in the Stage */ - participant_count: number; - /** The number of users speaking in the Stage */ - speaker_count: number; - /** The topic of the Stage instance (1-120 characters) */ - topic: string; + /** The members speaking in the Stage */ + members: Partial[]; + /** The number of users in the Stage */ + participant_count: number; + /** The number of users speaking in the Stage */ + speaker_count: number; + /** The topic of the Stage instance (1-120 characters) */ + topic: string; } /** https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-structure */ export interface DiscordApplicationCommand { - /** Unique ID of command */ - id: string; - /** Type of command, defaults to `ApplicationCommandTypes.ChatInput` */ - type?: ApplicationCommandTypes; - /** ID of the parent application */ - application_id: string; - /** Guild id of the command, if not global */ - guild_id?: string; - /** - * Name of command, 1-32 characters. - * `ApplicationCommandTypes.ChatInput` command names must match the following regex `^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$` with the unicode flag set. - * If there is a lowercase variant of any letters used, you must use those. - * Characters with no lowercase variants and/or uncased letters are still allowed. - * ApplicationCommandTypes.User` and `ApplicationCommandTypes.Message` commands may be mixed case and can include spaces. - */ - name: string; - /** Localization object for `name` field. Values follow the same restrictions as `name` */ - name_localizations?: Localization | null; - /** Description for `ApplicationCommandTypes.ChatInput` commands, 1-100 characters. Empty string for `ApplicationCommandTypes.User` and `ApplicationCommandTypes.Message` commands */ - description: string; - /** Localization object for `description` field. Values follow the same restrictions as `description` */ - description_localizations?: Localization | null; - /** Parameters for the command, max of 25 */ - options?: DiscordApplicationCommandOption[]; - /** Set of permissions represented as a bit set */ - default_member_permissions: string | null; - /** Indicates whether the command is available in DMs with the app, only for globally-scoped commands. By default, commands are visible. */ - dm_permission?: boolean; - /** Auto incrementing version identifier updated during substantial record changes */ - version: string; + /** Unique ID of command */ + id: string; + /** Type of command, defaults to `ApplicationCommandTypes.ChatInput` */ + type?: ApplicationCommandTypes; + /** ID of the parent application */ + application_id: string; + /** Guild id of the command, if not global */ + guild_id?: string; + /** + * Name of command, 1-32 characters. + * `ApplicationCommandTypes.ChatInput` command names must match the following regex `^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$` with the unicode flag set. + * If there is a lowercase variant of any letters used, you must use those. + * Characters with no lowercase variants and/or uncased letters are still allowed. + * ApplicationCommandTypes.User` and `ApplicationCommandTypes.Message` commands may be mixed case and can include spaces. + */ + name: string; + /** Localization object for `name` field. Values follow the same restrictions as `name` */ + name_localizations?: Localization | null; + /** Description for `ApplicationCommandTypes.ChatInput` commands, 1-100 characters. Empty string for `ApplicationCommandTypes.User` and `ApplicationCommandTypes.Message` commands */ + description: string; + /** Localization object for `description` field. Values follow the same restrictions as `description` */ + description_localizations?: Localization | null; + /** Parameters for the command, max of 25 */ + options?: DiscordApplicationCommandOption[]; + /** Set of permissions represented as a bit set */ + default_member_permissions: string | null; + /** Indicates whether the command is available in DMs with the app, only for globally-scoped commands. By default, commands are visible. */ + dm_permission?: boolean; + /** Auto incrementing version identifier updated during substantial record changes */ + version: string; } /** https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-structure */ export interface DiscordApplicationCommandOption { - /** Type of option */ - type: ApplicationCommandOptionTypes; - /** - * Name of command, 1-32 characters. - * `ApplicationCommandTypes.ChatInput` command names must match the following regex `^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$` with the unicode flag set. - * If there is a lowercase variant of any letters used, you must use those. - * Characters with no lowercase variants and/or uncased letters are still allowed. - * ApplicationCommandTypes.User` and `ApplicationCommandTypes.Message` commands may be mixed case and can include spaces. - */ - name: string; - /** Localization object for the `name` field. Values follow the same restrictions as `name` */ - name_localizations?: Localization | null; - /** 1-100 character description */ - description: string; - /** Localization object for the `description` field. Values follow the same restrictions as `description` */ - description_localizations?: Localization | null; - /** If the parameter is required or optional--default `false` */ - required?: boolean; - /** Choices for the option types `ApplicationCommandOptionTypes.String`, `ApplicationCommandOptionTypes.Integer`, and `ApplicationCommandOptionTypes.Number`, from which the user can choose, max 25 */ - choices?: DiscordApplicationCommandOptionChoice[]; - /** If the option is a subcommand or subcommand group type, these nested options will be the parameters */ - options?: DiscordApplicationCommandOption[]; - /** - * If autocomplete interactions are enabled for this option. - * - * Only available for `ApplicationCommandOptionTypes.String`, `ApplicationCommandOptionTypes.Integer` and `ApplicationCommandOptionTypes.Number` option types - */ - autocomplete?: boolean; - /** If the option is a channel type, the channels shown will be restricted to these types */ - channel_types?: ChannelTypes[]; - /** If the option type is `ApplicationCommandOptionTypes.Integer` or `ApplicationCommandOptionTypes.Number`, the minimum permitted value */ - min_value?: number; - /** If the option type is `ApplicationCommandOptionTypes.Integer` or `ApplicationCommandOptionTypes.Number`, the maximum permitted value */ - max_value?: number; + /** Type of option */ + type: ApplicationCommandOptionTypes; + /** + * Name of command, 1-32 characters. + * `ApplicationCommandTypes.ChatInput` command names must match the following regex `^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$` with the unicode flag set. + * If there is a lowercase variant of any letters used, you must use those. + * Characters with no lowercase variants and/or uncased letters are still allowed. + * ApplicationCommandTypes.User` and `ApplicationCommandTypes.Message` commands may be mixed case and can include spaces. + */ + name: string; + /** Localization object for the `name` field. Values follow the same restrictions as `name` */ + name_localizations?: Localization | null; + /** 1-100 character description */ + description: string; + /** Localization object for the `description` field. Values follow the same restrictions as `description` */ + description_localizations?: Localization | null; + /** If the parameter is required or optional--default `false` */ + required?: boolean; + /** Choices for the option types `ApplicationCommandOptionTypes.String`, `ApplicationCommandOptionTypes.Integer`, and `ApplicationCommandOptionTypes.Number`, from which the user can choose, max 25 */ + choices?: DiscordApplicationCommandOptionChoice[]; + /** If the option is a subcommand or subcommand group type, these nested options will be the parameters */ + options?: DiscordApplicationCommandOption[]; + /** + * If autocomplete interactions are enabled for this option. + * + * Only available for `ApplicationCommandOptionTypes.String`, `ApplicationCommandOptionTypes.Integer` and `ApplicationCommandOptionTypes.Number` option types + */ + autocomplete?: boolean; + /** If the option is a channel type, the channels shown will be restricted to these types */ + channel_types?: ChannelTypes[]; + /** If the option type is `ApplicationCommandOptionTypes.Integer` or `ApplicationCommandOptionTypes.Number`, the minimum permitted value */ + min_value?: number; + /** If the option type is `ApplicationCommandOptionTypes.Integer` or `ApplicationCommandOptionTypes.Number`, the maximum permitted value */ + max_value?: number; } /** https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-choice-structure */ export interface DiscordApplicationCommandOptionChoice { - /** 1-100 character choice name */ - name: string; - /** Localization object for the `name` field. Values follow the same restrictions as `name` */ - name_localizations?: Localization | null; - /** Value for the choice, up to 100 characters if string */ - value: string | number; + /** 1-100 character choice name */ + name: string; + /** Localization object for the `name` field. Values follow the same restrictions as `name` */ + name_localizations?: Localization | null; + /** Value for the choice, up to 100 characters if string */ + value: string | number; } /** https://discord.com/developers/docs/interactions/slash-commands#guildapplicationcommandpermissions */ export interface DiscordGuildApplicationCommandPermissions { - /** ID of the command or the application ID. When the `id` field is the application ID instead of a command ID, the permissions apply to all commands that do not contain explicit overwrites. */ - id: string; - /** ID of the application the command belongs to */ - application_id: string; - /** ID of the guild */ - guild_id: string; - /** Permissions for the command in the guild, max of 100 */ - permissions: DiscordApplicationCommandPermissions[]; + /** ID of the command or the application ID. When the `id` field is the application ID instead of a command ID, the permissions apply to all commands that do not contain explicit overwrites. */ + id: string; + /** ID of the application the command belongs to */ + application_id: string; + /** ID of the guild */ + guild_id: string; + /** Permissions for the command in the guild, max of 100 */ + permissions: DiscordApplicationCommandPermissions[]; } /** https://discord.com/developers/docs/interactions/slash-commands#applicationcommandpermissions */ export interface DiscordApplicationCommandPermissions { - /** ID of the role, user, or channel. It can also be a permission constant */ - id: string; - /** ApplicationCommandPermissionTypes.Role, ApplicationCommandPermissionTypes.User, or ApplicationCommandPermissionTypes.Channel */ - type: ApplicationCommandPermissionTypes; - /** `true` to allow, `false`, to disallow */ - permission: boolean; + /** ID of the role, user, or channel. It can also be a permission constant */ + id: string; + /** ApplicationCommandPermissionTypes.Role, ApplicationCommandPermissionTypes.User, or ApplicationCommandPermissionTypes.Channel */ + type: ApplicationCommandPermissionTypes; + /** `true` to allow, `false`, to disallow */ + permission: boolean; } /** https://discord.com/developers/docs/resources/guild#get-guild-widget-example-get-guild-widget */ export interface DiscordGuildWidget { - id: string; - name: string; - instant_invite: string; - channels: { id: string; name: string; - position: number; - }[]; - members: { - id: string; - username: string; - discriminator: string; - avatar?: string | null; - status: string; - avatar_url: string; - }[]; - presence_count: number; + instant_invite: string; + channels: { + id: string; + name: string; + position: number; + }[]; + members: { + id: string; + username: string; + discriminator: string; + avatar?: string | null; + status: string; + avatar_url: string; + }[]; + presence_count: number; } /** https://discord.com/developers/docs/resources/guild#guild-preview-object */ export interface DiscordGuildPreview { - /** Guild id */ - id: string; - /** Guild name (2-100 characters) */ - name: string; - /** Icon hash */ - icon: string | null; - /** Splash hash */ - splash: string | null; - /** Discovery splash hash */ - discovery_splash: string | null; - /** Custom guild emojis */ - emojis: DiscordEmoji[]; - /** Enabled guild features */ - features: GuildFeatures[]; - /** Approximate number of members in this guild */ - approximate_member_count: number; - /** Approximate number of online members in this guild */ - approximate_presence_count: number; - /** The description for the guild, if the guild is discoverable */ - description: string | null; - /** Custom guild stickers */ - stickers: DiscordSticker[]; + /** Guild id */ + id: string; + /** Guild name (2-100 characters) */ + name: string; + /** Icon hash */ + icon: string | null; + /** Splash hash */ + splash: string | null; + /** Discovery splash hash */ + discovery_splash: string | null; + /** Custom guild emojis */ + emojis: DiscordEmoji[]; + /** Enabled guild features */ + features: GuildFeatures[]; + /** Approximate number of members in this guild */ + approximate_member_count: number; + /** Approximate number of online members in this guild */ + approximate_presence_count: number; + /** The description for the guild, if the guild is discoverable */ + description: string | null; + /** Custom guild stickers */ + stickers: DiscordSticker[]; } export interface DiscordDiscoveryCategory { - /** Numeric id of the category */ - id: number; - /** The name of this category, in multiple languages */ - name: DiscordDiscoveryName; - /** Whether this category can be set as a guild's primary category */ - is_primary: boolean; + /** Numeric id of the category */ + id: number; + /** The name of this category, in multiple languages */ + name: DiscordDiscoveryName; + /** Whether this category can be set as a guild's primary category */ + is_primary: boolean; } export interface DiscordDiscoveryName { - /** The name in English */ - default: string; - /** The name in other languages */ - localizations?: Record; + /** The name in English */ + default: string; + /** The name in other languages */ + localizations?: Record; } export interface DiscordDiscoveryMetadata { - /** The guild Id */ - guild_id: string; - /** The id of the primary discovery category set for this guild */ - primary_category_id: number; - /** Up to 10 discovery search keywords set for this guild */ - keywords: string[] | null; - /** Whether guild info is shown when custom emojis from this guild are clicked */ - emoji_discoverability_enabled: boolean; - /** When the server's partner application was accepted or denied, for applications via Server Settings */ - partner_actioned_timestamp: string | null; - /** When the server applied for partnership, if it has a pending application */ - partner_application_timestamp: string | null; - /** Ids of up to 5 discovery subcategories set for this guild */ - category_ids: number[]; + /** The guild Id */ + guild_id: string; + /** The id of the primary discovery category set for this guild */ + primary_category_id: number; + /** Up to 10 discovery search keywords set for this guild */ + keywords: string[] | null; + /** Whether guild info is shown when custom emojis from this guild are clicked */ + emoji_discoverability_enabled: boolean; + /** When the server's partner application was accepted or denied, for applications via Server Settings */ + partner_actioned_timestamp: string | null; + /** When the server applied for partnership, if it has a pending application */ + partner_application_timestamp: string | null; + /** Ids of up to 5 discovery subcategories set for this guild */ + category_ids: number[]; } /** https://discord.com/developers/docs/resources/channel#followed-channel-object */ export interface DiscordFollowedChannel { - /** Source message id */ - channel_id: string; - /** Created target webhook id */ - webhook_id: string; + /** Source message id */ + channel_id: string; + /** Created target webhook id */ + webhook_id: string; } /** https://discord.com/developers/docs/topics/gateway#payloads-gateway-payload-structure */ export interface DiscordGatewayPayload { - /** opcode for the payload */ - op: number; - /** Event data */ - d: unknown | null; - /** Sequence number, used for resuming sessions and heartbeats */ - s: number | null; - /** The event name for this payload */ - t: GatewayEventNames | null; + /** opcode for the payload */ + op: number; + /** Event data */ + d: unknown | null; + /** Sequence number, used for resuming sessions and heartbeats */ + s: number | null; + /** The event name for this payload */ + t: GatewayEventNames | null; } /** https://discord.com/developers/docs/topics/gateway#guild-members-chunk */ export interface DiscordGuildMembersChunk { - /** The id of the guild */ - guild_id: string; - /** Set of guild members */ - members: DiscordMemberWithUser[]; - /** The chunk index in the expected chunks for this response (0 <= chunk_index < chunk_count) */ - chunk_index: number; - /** The total number of expected chunks for this response */ - chunk_count: number; - /** If passing an invalid id to `REQUEST_GUILD_MEMBERS`, it will be returned here */ - not_found?: string[]; - /** If passing true to `REQUEST_GUILD_MEMBERS`, presences of the returned members will be here */ - presences?: DiscordPresenceUpdate[]; - /** The nonce used in the Guild Members Request */ - nonce?: string; + /** The id of the guild */ + guild_id: string; + /** Set of guild members */ + members: DiscordMemberWithUser[]; + /** The chunk index in the expected chunks for this response (0 <= chunk_index < chunk_count) */ + chunk_index: number; + /** The total number of expected chunks for this response */ + chunk_count: number; + /** If passing an invalid id to `REQUEST_GUILD_MEMBERS`, it will be returned here */ + not_found?: string[]; + /** If passing true to `REQUEST_GUILD_MEMBERS`, presences of the returned members will be here */ + presences?: DiscordPresenceUpdate[]; + /** The nonce used in the Guild Members Request */ + nonce?: string; } export interface DiscordComponent { - /** component type */ - type: MessageComponentTypes; - /** a developer-defined identifier for the component, max 100 characters */ - custom_id?: string; - /** whether the component is disabled, default false */ - disabled?: boolean; - /** For different styles/colors of the buttons */ - style?: ButtonStyles | TextStyles; - /** text that appears on the button (max 80 characters) */ - label?: string; - /** the dev-define value of the option, max 100 characters for select or 4000 for input. */ - value?: string; - /** Emoji object that includes fields of name, id, and animated supporting unicode and custom emojis. */ - emoji?: { - /** Emoji id */ - id?: string; - /** Emoji name */ - name?: string; - /** Whether this emoji is animated */ - animated?: boolean; - }; - /** optional url for link-style buttons that can navigate a user to the web. Only type 5 Link buttons can have a url */ - url?: string; - /** The choices! Maximum of 25 items. */ - options?: DiscordSelectOption[]; - /** A custom placeholder text if nothing is selected. Maximum 150 characters. */ - placeholder?: string; - /** The minimum number of items that must be selected. Default 1. Between 1-25. */ - min_values?: number; - /** The maximum number of items that can be selected. Default 1. Between 1-25. */ - max_values?: number; - /** a list of child components */ - components?: DiscordComponent[]; + /** component type */ + type: MessageComponentTypes; + /** a developer-defined identifier for the component, max 100 characters */ + custom_id?: string; + /** whether the component is disabled, default false */ + disabled?: boolean; + /** For different styles/colors of the buttons */ + style?: ButtonStyles | TextStyles; + /** text that appears on the button (max 80 characters) */ + label?: string; + /** the dev-define value of the option, max 100 characters for select or 4000 for input. */ + value?: string; + /** Emoji object that includes fields of name, id, and animated supporting unicode and custom emojis. */ + emoji?: { + /** Emoji id */ + id?: string; + /** Emoji name */ + name?: string; + /** Whether this emoji is animated */ + animated?: boolean; + }; + /** optional url for link-style buttons that can navigate a user to the web. Only type 5 Link buttons can have a url */ + url?: string; + /** The choices! Maximum of 25 items. */ + options?: DiscordSelectOption[]; + /** A custom placeholder text if nothing is selected. Maximum 150 characters. */ + placeholder?: string; + /** The minimum number of items that must be selected. Default 1. Between 1-25. */ + min_values?: number; + /** The maximum number of items that can be selected. Default 1. Between 1-25. */ + max_values?: number; + /** a list of child components */ + components?: DiscordComponent[]; } /** https://discord.com/developers/docs/topics/gateway#channel-pins-update */ export interface DiscordChannelPinsUpdate { - /** The id of the guild */ - guild_id?: string; - /** The id of the channel */ - channel_id: string; - /** The time at which the most recent pinned message was pinned */ - last_pin_timestamp?: string | null; + /** The id of the guild */ + guild_id?: string; + /** The id of the channel */ + channel_id: string; + /** The time at which the most recent pinned message was pinned */ + last_pin_timestamp?: string | null; } /** https://discord.com/developers/docs/topics/gateway#guild-role-delete */ export interface DiscordGuildRoleDelete { - /** id of the guild */ - guild_id: string; - /** id of the role */ - role_id: string; + /** id of the guild */ + guild_id: string; + /** id of the role */ + role_id: string; } /** https://discord.com/developers/docs/topics/gateway#guild-ban-add */ export interface DiscordGuildBanAddRemove { - /** id of the guild */ - guild_id: string; - /** The banned user */ - user: DiscordUser; + /** id of the guild */ + guild_id: string; + /** The banned user */ + user: DiscordUser; } /** https://discord.com/developers/docs/topics/gateway#message-reaction-remove */ @@ -2088,78 +2088,78 @@ export interface DiscordMessageReactionRemove extends Omit; + /** The id of the user */ + user_id: string; + /** The id of the channel */ + channel_id: string; + /** The id of the message */ + message_id: string; + /** The id of the guild */ + guild_id?: string; + /** The member who reacted if this happened in a guild */ + member?: DiscordMemberWithUser; + /** The emoji used to react */ + emoji: Partial; } /** https://discord.com/developers/docs/topics/gateway#voice-server-update */ export interface DiscordVoiceServerUpdate { - /** Voice connection token */ - token: string; - /** The guild this voice server update is for */ - guild_id: string; - /** The voice server host */ - endpoint: string | null; + /** Voice connection token */ + token: string; + /** The guild this voice server update is for */ + guild_id: string; + /** The voice server host */ + endpoint: string | null; } /** https://discord.com/developers/docs/topics/gateway#invite-create */ export interface DiscordInviteCreate { - /** The channel the invite is for */ - channel_id: string; - /** The unique invite code */ - code: string; - /** The time at which the invite was created */ - created_at: string; - /** The guild of the invite */ - guild_id?: string; - /** The user that created the invite */ - inviter?: DiscordUser; - /** How long the invite is valid for (in seconds) */ - max_age: number; - /** The maximum number of times the invite can be used */ - max_uses: number; - /** The type of target for this voice channel invite */ - target_type: TargetTypes; - /** The target user for this invite */ - target_user?: DiscordUser; - /** The embedded application to open for this voice channel embedded application invite */ - target_application?: Partial; - /** Whether or not the invite is temporary (invited users will be kicked on disconnect unless they're assigned a role) */ - temporary: boolean; - /** How many times the invite has been used (always will be 0) */ - uses: number; + /** The channel the invite is for */ + channel_id: string; + /** The unique invite code */ + code: string; + /** The time at which the invite was created */ + created_at: string; + /** The guild of the invite */ + guild_id?: string; + /** The user that created the invite */ + inviter?: DiscordUser; + /** How long the invite is valid for (in seconds) */ + max_age: number; + /** The maximum number of times the invite can be used */ + max_uses: number; + /** The type of target for this voice channel invite */ + target_type: TargetTypes; + /** The target user for this invite */ + target_user?: DiscordUser; + /** The embedded application to open for this voice channel embedded application invite */ + target_application?: Partial; + /** Whether or not the invite is temporary (invited users will be kicked on disconnect unless they're assigned a role) */ + temporary: boolean; + /** How many times the invite has been used (always will be 0) */ + uses: number; } /** https://discord.com/developers/docs/topics/gateway#hello */ export interface DiscordHello { - /** The interval (in milliseconds) the client should heartbeat with */ - heartbeat_interval: number; + /** The interval (in milliseconds) the client should heartbeat with */ + heartbeat_interval: number; } /** https://discord.com/developers/docs/topics/gateway#ready */ export interface DiscordReady { - /** Gateway version */ - v: number; - /** Information about the user including email */ - user: DiscordUser; - /** The guilds the user is in */ - guilds: DiscordUnavailableGuild[]; - /** Used for resuming connections */ - session_id: string; - /** The shard information associated with this session, if sent when identifying */ - shard?: [number, number]; - /** Contains id and flags */ - application: Partial & Pick; + /** Gateway version */ + v: number; + /** Information about the user including email */ + user: DiscordUser; + /** The guilds the user is in */ + guilds: DiscordUnavailableGuild[]; + /** Used for resuming connections */ + session_id: string; + /** The shard information associated with this session, if sent when identifying */ + shard?: [number, number]; + /** Contains id and flags */ + application: Partial & Pick; } /** https://discord.com/developers/docs/resources/guild#unavailable-guild-object */ @@ -2167,260 +2167,260 @@ export interface DiscordUnavailableGuild extends Pick, - "roles" - > - & { - roles: ( + /** The template code (unique Id) */ + code: string; + /** Template name */ + name: string; + /** The description for the template */ + description: string | null; + /** Number of times this template has been used */ + usage_count: number; + /** The Id of the user who created the template */ + creator_id: string; + /** The user who created the template */ + creator: DiscordUser; + /** When this template was created */ + created_at: string; + /** When this template was last synced to the source guild */ + updated_at: string; + /** The Id of the guild this template is based on */ + source_guild_id: string; + /** The guild snapshot this template contains */ + serialized_source_guild: & Omit< - PickPartial< - DiscordRole, - "name" | "color" | "hoist" | "mentionable" | "permissions" | "icon" | "unicode_emoji" - >, - "id" + PickPartial< + DiscordGuild, + | "name" + | "description" + | "verification_level" + | "default_message_notifications" + | "explicit_content_filter" + | "preferred_locale" + | "afk_timeout" + | "channels" + | "afk_channel_id" + | "system_channel_id" + | "system_channel_flags" + >, + "roles" > - & { id: number } - )[]; - }; - /** Whether the template has un-synced changes */ - is_dirty: boolean | null; + & { + roles: ( + & Omit< + PickPartial< + DiscordRole, + "name" | "color" | "hoist" | "mentionable" | "permissions" | "icon" | "unicode_emoji" + >, + "id" + > + & { id: number } + )[]; + }; + /** Whether the template has un-synced changes */ + is_dirty: boolean | null; } /** https://discord.com/developers/docs/topics/gateway#guild-member-add */ export interface DiscordGuildMemberAdd extends DiscordMemberWithUser { - /** id of the guild */ - guild_id: string; + /** id of the guild */ + guild_id: string; } /** https://discord.com/developers/docs/topics/gateway#message-delete */ export interface DiscordMessageDelete { - /** The id of the message */ - id: string; - /** The id of the channel */ - channel_id: string; - /** The id of the guild */ - guild_id?: string; + /** The id of the message */ + id: string; + /** The id of the channel */ + channel_id: string; + /** The id of the guild */ + guild_id?: string; } /** https://discord.com/developers/docs/topics/gateway#thread-members-update-thread-members-update-event-fields */ export interface DiscordThreadMembersUpdate { - /** The id of the thread */ - id: string; - /** The id of the guild */ - guild_id: string; - /** The users who were added to the thread */ - added_members?: DiscordThreadMember[]; - /** The id of the users who were removed from the thread */ - removed_member_ids?: string[]; - /** the approximate number of members in the thread, capped at 50 */ - member_count: number; + /** The id of the thread */ + id: string; + /** The id of the guild */ + guild_id: string; + /** The users who were added to the thread */ + added_members?: DiscordThreadMember[]; + /** The id of the users who were removed from the thread */ + removed_member_ids?: string[]; + /** the approximate number of members in the thread, capped at 50 */ + member_count: number; } /** https://discord.com/developers/docs/topics/gateway#thread-member-update */ export interface DiscordThreadMemberUpdate { - /** The id of the thread */ - id: string; - /** The id of the guild */ - guild_id: string; - /** The timestamp when the bot joined this thread. */ - joined_at: string; - /** The flags this user has for this thread. Not useful for bots. */ - flags: number; + /** The id of the thread */ + id: string; + /** The id of the guild */ + guild_id: string; + /** The timestamp when the bot joined this thread. */ + joined_at: string; + /** The flags this user has for this thread. Not useful for bots. */ + flags: number; } /** https://discord.com/developers/docs/topics/gateway#guild-role-create */ export interface DiscordGuildRoleCreate { - /** The id of the guild */ - guild_id: string; - /** The role created */ - role: DiscordRole; + /** The id of the guild */ + guild_id: string; + /** The role created */ + role: DiscordRole; } /** https://discord.com/developers/docs/topics/gateway#guild-emojis-update */ export interface DiscordGuildEmojisUpdate { - /** id of the guild */ - guild_id: string; - /** Array of emojis */ - emojis: DiscordEmoji[]; + /** id of the guild */ + guild_id: string; + /** Array of emojis */ + emojis: DiscordEmoji[]; } export interface DiscordAddGuildDiscoverySubcategory { - /** The guild Id of the subcategory was added to */ - guild_id: string; - /** The Id of the subcategory added */ - category_id: number; + /** The guild Id of the subcategory was added to */ + guild_id: string; + /** The Id of the subcategory added */ + category_id: number; } /** https://discord.com/developers/docs/topics/gateway#guild-ban-add */ export interface DiscordGuildBanAddRemove { - /** id of the guild */ - guild_id: string; - /** The banned user */ - user: DiscordUser; + /** id of the guild */ + guild_id: string; + /** The banned user */ + user: DiscordUser; } /** https://discord.com/developers/docs/topics/gateway#guild-member-update */ export interface DiscordGuildMemberUpdate { - /** The id of the guild */ - guild_id: string; - /** User role ids */ - roles: string[]; - /** The user */ - user: DiscordUser; - /** Nickname of the user in the guild */ - nick?: string | null; - /** the member's [guild avatar hash](https://discord.com/developers/docs/reference#image-formatting) */ - avatar: string; - /** When the user joined the guild */ - joined_at: string; - /** When the user starting boosting the guild */ - premium_since?: string | null; - /** whether the user is deafened in voice channels */ - deaf?: boolean; - /** whether the user is muted in voice channels */ - mute?: boolean; - /** Whether the user has not yet passed the guild's Membership Screening requirements */ - pending?: boolean; - /** when the user's [timeout](https://support.discord.com/hc/en-us/articles/4413305239191-Time-Out-FAQ) will expire and the user will be able to communicate in the guild again, null or a time in the past if the user is not timed out. Will throw a 403 error if the user has the ADMINISTRATOR permission or is the owner of the guild */ - communication_disabled_until?: string; + /** The id of the guild */ + guild_id: string; + /** User role ids */ + roles: string[]; + /** The user */ + user: DiscordUser; + /** Nickname of the user in the guild */ + nick?: string | null; + /** the member's [guild avatar hash](https://discord.com/developers/docs/reference#image-formatting) */ + avatar: string; + /** When the user joined the guild */ + joined_at: string; + /** When the user starting boosting the guild */ + premium_since?: string | null; + /** whether the user is deafened in voice channels */ + deaf?: boolean; + /** whether the user is muted in voice channels */ + mute?: boolean; + /** Whether the user has not yet passed the guild's Membership Screening requirements */ + pending?: boolean; + /** when the user's [timeout](https://support.discord.com/hc/en-us/articles/4413305239191-Time-Out-FAQ) will expire and the user will be able to communicate in the guild again, null or a time in the past if the user is not timed out. Will throw a 403 error if the user has the ADMINISTRATOR permission or is the owner of the guild */ + communication_disabled_until?: string; } /** https://discord.com/developers/docs/topics/gateway#message-reaction-remove-all */ export interface DiscordMessageReactionRemoveAll - extends Pick {} + extends Pick {} // TODO: add docs link export interface DiscordValidateDiscoverySearchTerm { - /** Whether the provided term is valid */ - valid: boolean; + /** Whether the provided term is valid */ + valid: boolean; } /** https://discord.com/developers/docs/topics/gateway#guild-role-update */ export interface DiscordGuildRoleUpdate { - /** The id of the guild */ - guild_id: string; - /** The role updated */ - role: DiscordRole; + /** The id of the guild */ + guild_id: string; + /** The role updated */ + role: DiscordRole; } export interface DiscordScheduledEventUserAdd { - /** id of the guild scheduled event */ - guild_scheduled_event_id: string; - /** id of the user */ - user_id: string; - /** id of the guild */ - guild_id: string; + /** id of the guild scheduled event */ + guild_scheduled_event_id: string; + /** id of the user */ + user_id: string; + /** id of the guild */ + guild_id: string; } /** https://discord.com/developers/docs/topics/gateway#message-reaction-remove-emoji */ export type DiscordMessageReactionRemoveEmoji = Pick< - DiscordMessageReactionAdd, - "channel_id" | "guild_id" | "message_id" | "emoji" + DiscordMessageReactionAdd, + "channel_id" | "guild_id" | "message_id" | "emoji" >; /** https://discord.com/developers/docs/topics/gateway#guild-member-remove */ export interface DiscordGuildMemberRemove { - /** The id of the guild */ - guild_id: string; - /** The user who was removed */ - user: DiscordUser; + /** The id of the guild */ + guild_id: string; + /** The user who was removed */ + user: DiscordUser; } /** https://discord.com/developers/docs/resources/guild#ban-object */ export interface DiscordBan { - /** The reason for the ban */ - reason: string | null; - /** The banned user */ - user: DiscordUser; + /** The reason for the ban */ + reason: string | null; + /** The banned user */ + user: DiscordUser; } export interface DiscordScheduledEventUserRemove { - /** id of the guild scheduled event */ - guild_scheduled_event_id: string; - /** id of the user */ - user_id: string; - /** id of the guild */ - guild_id: string; + /** id of the guild scheduled event */ + guild_scheduled_event_id: string; + /** id of the user */ + user_id: string; + /** id of the guild */ + guild_id: string; } /** https://discord.com/developers/docs/topics/gateway#invite-delete */ export interface DiscordInviteDelete { - /** The channel of the invite */ - channel_id: string; - /** The guild of the invite */ - guild_id?: string; - /** The unique invite code */ - code: string; + /** The channel of the invite */ + channel_id: string; + /** The guild of the invite */ + guild_id?: string; + /** The unique invite code */ + code: string; } /** https://discord.com/developers/docs/resources/voice#voice-region-object-voice-region-structure */ export interface DiscordVoiceRegion { - /** Unique Id for the region */ - id: string; - /** Name of the region */ - name: string; - /** true for a single server that is closest to the current user's client */ - optimal: boolean; - /** Whether this is a deprecated voice region (avoid switching to these) */ - deprecated: boolean; - /** Whether this is a custom voice region (used for events/etc) */ - custom: boolean; + /** Unique Id for the region */ + id: string; + /** Name of the region */ + name: string; + /** true for a single server that is closest to the current user's client */ + optimal: boolean; + /** Whether this is a deprecated voice region (avoid switching to these) */ + deprecated: boolean; + /** Whether this is a custom voice region (used for events/etc) */ + custom: boolean; } export interface DiscordGuildWidgetSettings { - /** whether the widget is enabled */ - enabled: boolean; - /** the widget channel id */ - channel_id: string | null; + /** whether the widget is enabled */ + enabled: boolean; + /** the widget channel id */ + channel_id: string | null; } export interface DiscordInstallParams { - /** he scopes to add the application to the server with */ - scopes: string[]; - /** the permissions to request for the bot role */ - permissions: string; + /** he scopes to add the application to the server with */ + scopes: string[]; + /** the permissions to request for the bot role */ + permissions: string; } diff --git a/vendor/types/shared.ts b/vendor/types/shared.ts index 5001188..6c90cdf 100644 --- a/vendor/types/shared.ts +++ b/vendor/types/shared.ts @@ -1,125 +1,125 @@ /** https://discord.com/developers/docs/resources/user#user-object-premium-types */ export enum PremiumTypes { - None, - NitroClassic, - Nitro, + None, + NitroClassic, + Nitro, } /** https://discord.com/developers/docs/resources/user#user-object-user-flags */ export enum UserFlags { - DiscordEmployee = 1 << 0, - PartneredServerOwner = 1 << 1, - HypeSquadEventsMember = 1 << 2, - BugHunterLevel1 = 1 << 3, - HouseBravery = 1 << 6, - HouseBrilliance = 1 << 7, - HouseBalance = 1 << 8, - EarlySupporter = 1 << 9, - TeamUser = 1 << 10, - BugHunterLevel2 = 1 << 14, - VerifiedBot = 1 << 16, - EarlyVerifiedBotDeveloper = 1 << 17, - DiscordCertifiedModerator = 1 << 18, - BotHttpInteractions = 1 << 19, + DiscordEmployee = 1 << 0, + PartneredServerOwner = 1 << 1, + HypeSquadEventsMember = 1 << 2, + BugHunterLevel1 = 1 << 3, + HouseBravery = 1 << 6, + HouseBrilliance = 1 << 7, + HouseBalance = 1 << 8, + EarlySupporter = 1 << 9, + TeamUser = 1 << 10, + BugHunterLevel2 = 1 << 14, + VerifiedBot = 1 << 16, + EarlyVerifiedBotDeveloper = 1 << 17, + DiscordCertifiedModerator = 1 << 18, + BotHttpInteractions = 1 << 19, } /** https://discord.com/developers/docs/resources/channel#channels-resource */ export enum ChannelFlags { - None, - Pinned = 1 << 1, + None, + Pinned = 1 << 1, } /** https://discord.com/developers/docs/resources/guild#integration-object-integration-expire-behaviors */ export enum IntegrationExpireBehaviors { - RemoveRole, - Kick, + RemoveRole, + Kick, } /** https://discord.com/developers/docs/resources/user#connection-object-visibility-types */ export enum VisibilityTypes { - /** Invisible to everyone except the user themselves */ - None, - /** Visible to everyone */ - Everyone, + /** Invisible to everyone except the user themselves */ + None, + /** Visible to everyone */ + Everyone, } /** https://discord.com/developers/docs/topics/teams#data-models-membership-state-enum */ export enum TeamMembershipStates { - Invited = 1, - Accepted, + Invited = 1, + Accepted, } /** https://discord.com/developers/docs/topics/oauth2#application-application-flags */ export enum ApplicationFlags { - /** Intent required for bots in **100 or more servers** to receive [`presence_update` events](#DOCS_TOPICS_GATEWAY/presence-update) */ - GatewayPresence = 1 << 12, - /** Intent required for bots in under 100 servers to receive [`presence_update` events](#DOCS_TOPICS_GATEWAY/presence-update), found in Bot Settings */ - GatewayPresenceLimited = 1 << 13, - /** Intent required for bots in **100 or more servers** to receive member-related events like `guild_member_add`. See list of member-related events [under `GUILD_MEMBERS`](#DOCS_TOPICS_GATEWAY/list-of-intents) */ - GatewayGuildMembers = 1 << 14, - /** Intent required for bots in under 100 servers to receive member-related events like `guild_member_add`, found in Bot Settings. See list of member-related events [under `GUILD_MEMBERS`](#DOCS_TOPICS_GATEWAY/list-of-intents) */ - GatewayGuildMembersLimited = 1 << 15, - /** Indicates unusual growth of an app that prevents verification */ - VerificationPendingGuildLimit = 1 << 16, - /** Indicates if an app is embedded within the Discord client (currently unavailable publicly) */ - Embedded = 1 << 17, - /** Intent required for bots in **100 or more servers** to receive [message content](https://support-dev.discord.com/hc/en-us/articles/4404772028055) */ - GatewayMessageCount = 1 << 18, - /** Intent required for bots in under 100 servers to receive [message content](https://support-dev.discord.com/hc/en-us/articles/4404772028055), found in Bot Settings */ - GatewayMessageContentLimited = 1 << 19, + /** Intent required for bots in **100 or more servers** to receive [`presence_update` events](#DOCS_TOPICS_GATEWAY/presence-update) */ + GatewayPresence = 1 << 12, + /** Intent required for bots in under 100 servers to receive [`presence_update` events](#DOCS_TOPICS_GATEWAY/presence-update), found in Bot Settings */ + GatewayPresenceLimited = 1 << 13, + /** Intent required for bots in **100 or more servers** to receive member-related events like `guild_member_add`. See list of member-related events [under `GUILD_MEMBERS`](#DOCS_TOPICS_GATEWAY/list-of-intents) */ + GatewayGuildMembers = 1 << 14, + /** Intent required for bots in under 100 servers to receive member-related events like `guild_member_add`, found in Bot Settings. See list of member-related events [under `GUILD_MEMBERS`](#DOCS_TOPICS_GATEWAY/list-of-intents) */ + GatewayGuildMembersLimited = 1 << 15, + /** Indicates unusual growth of an app that prevents verification */ + VerificationPendingGuildLimit = 1 << 16, + /** Indicates if an app is embedded within the Discord client (currently unavailable publicly) */ + Embedded = 1 << 17, + /** Intent required for bots in **100 or more servers** to receive [message content](https://support-dev.discord.com/hc/en-us/articles/4404772028055) */ + GatewayMessageCount = 1 << 18, + /** Intent required for bots in under 100 servers to receive [message content](https://support-dev.discord.com/hc/en-us/articles/4404772028055), found in Bot Settings */ + GatewayMessageContentLimited = 1 << 19, } /** https://discord.com/developers/docs/interactions/message-components#component-types */ export enum MessageComponentTypes { - /** A container for other components */ - ActionRow = 1, - /** A button object */ - Button = 2, - /** A select menu for picking from choices */ - SelectMenu = 3, - /** A text input object */ - InputText = 4, + /** A container for other components */ + ActionRow = 1, + /** A button object */ + Button = 2, + /** A select menu for picking from choices */ + SelectMenu = 3, + /** A text input object */ + InputText = 4, } export enum TextStyles { - /** Intended for short single-line text */ - Short = 1, - /** Intended for much longer inputs */ - Paragraph = 2, + /** Intended for short single-line text */ + Short = 1, + /** Intended for much longer inputs */ + Paragraph = 2, } /** https://discord.com/developers/docs/interactions/message-components#buttons-button-styles */ export enum ButtonStyles { - /** A blurple button */ - Primary = 1, - /** A grey button */ - Secondary, - /** A green button */ - Success, - /** A red button */ - Danger, - /** A button that navigates to a URL */ - Link, + /** A blurple button */ + Primary = 1, + /** A grey button */ + Secondary, + /** A green button */ + Success, + /** A red button */ + Danger, + /** A button that navigates to a URL */ + Link, } /** https://discord.com/developers/docs/resources/channel#allowed-mentions-object-allowed-mention-types */ export enum AllowedMentionsTypes { - /** Controls role mentions */ - RoleMentions = "roles", - /** Controls user mentions */ - UserMentions = "users", - /** Controls @everyone and @here mentions */ - EveryoneMentions = "everyone", + /** Controls role mentions */ + RoleMentions = "roles", + /** Controls user mentions */ + UserMentions = "users", + /** Controls @everyone and @here mentions */ + EveryoneMentions = "everyone", } /** https://discord.com/developers/docs/resources/webhook#webhook-object-webhook-types */ export enum WebhookTypes { - /** Incoming Webhooks can post messages to channels with a generated token */ - Incoming = 1, - /** Channel Follower Webhooks are internal webhooks used with Channel Following to post new messages into channels */ - ChannelFollower, - /** Application webhooks are webhooks used with Interactions */ - Application, + /** Incoming Webhooks can post messages to channels with a generated token */ + Incoming = 1, + /** Channel Follower Webhooks are internal webhooks used with Channel Following to post new messages into channels */ + ChannelFollower, + /** Application webhooks are webhooks used with Interactions */ + Application, } /** https://discord.com/developers/docs/resources/channel#embed-object-embed-types */ @@ -127,895 +127,895 @@ export type EmbedTypes = "rich" | "image" | "video" | "gifv" | "article" | "link /** https://discord.com/developers/docs/resources/guild#guild-object-default-message-notification-level */ export enum DefaultMessageNotificationLevels { - /** Members will receive notifications for all messages by default */ - AllMessages, - /** Members will receive notifications only for messages that @mention them by default */ - OnlyMentions, + /** Members will receive notifications for all messages by default */ + AllMessages, + /** Members will receive notifications only for messages that @mention them by default */ + OnlyMentions, } /** https://discord.com/developers/docs/resources/guild#guild-object-explicit-content-filter-level */ export enum ExplicitContentFilterLevels { - /** Media content will not be scanned */ - Disabled, - /** Media content sent by members without roles will be scanned */ - MembersWithoutRoles, - /** Media content sent by all members will be scanned */ - AllMembers, + /** Media content will not be scanned */ + Disabled, + /** Media content sent by members without roles will be scanned */ + MembersWithoutRoles, + /** Media content sent by all members will be scanned */ + AllMembers, } /** https://discord.com/developers/docs/resources/guild#guild-object-verification-level */ export enum VerificationLevels { - /** Unrestricted */ - None, - /** Must have verified email on account */ - Low, - /** Must be registered on Discord for longer than 5 minutes */ - Medium, - /** Must be a member of the server for longer than 10 minutes */ - High, - /** Must have a verified phone number */ - VeryHigh, + /** Unrestricted */ + None, + /** Must have verified email on account */ + Low, + /** Must be registered on Discord for longer than 5 minutes */ + Medium, + /** Must be a member of the server for longer than 10 minutes */ + High, + /** Must have a verified phone number */ + VeryHigh, } /** https://discord.com/developers/docs/topics/permissions#role-object-role-structure */ export interface BaseRole { - /** Role name */ - name: string; - /** Integer representation of hexadecimal color code */ - color: number; - /** Position of this role */ - position: number; - /** role unicode emoji */ - unicodeEmoji?: string; + /** Role name */ + name: string; + /** Integer representation of hexadecimal color code */ + color: number; + /** Position of this role */ + position: number; + /** role unicode emoji */ + unicodeEmoji?: string; } /** https://discord.com/developers/docs/resources/guild#guild-object-guild-features */ export enum GuildFeatures { - /** Guild has access to set an invite splash background */ - InviteSplash = "INVITE_SPLASH", - /** Guild has access to set 384 kbps bitrate in voice (previously VIP voice servers) */ - VipRegions = "VIP_REGIONS", - /** Guild has access to set a vanity URL */ - VanityUrl = "VANITY_URL", - /** Guild is verified */ - Verified = "VERIFIED", - /** Guild is partnered */ - Partnered = "PARTNERED", - /** Guild can enable welcome screen, Membership Screening, stage channels and discovery, and receives community updates */ - Community = "COMMUNITY", - /** Guild has access to use commerce features (i.e. create store channels) */ - Commerce = "COMMERCE", - /** Guild has access to create news channels */ - News = "NEWS", - /** Guild is able to be discovered in the directory */ - Discoverable = "DISCOVERABLE", - /** guild cannot be discoverable */ - DiscoverableDisabled = "DISCOVERABLE_DISABLED", - /** Guild is able to be featured in the directory */ - Feature = "FEATURABLE", - /** Guild has access to set an animated guild icon */ - AnimatedIcon = "ANIMATED_ICON", - /** Guild has access to set a guild banner image */ - Banner = "BANNER", - /** Guild has enabled the welcome screen */ - WelcomeScreenEnabled = "WELCOME_SCREEN_ENABLED", - /** Guild has enabled [Membership Screening](https://discord.com/developers/docs/resources/guild#membership-screening-object) */ - MemberVerificationGateEnabled = "MEMBER_VERIFICATION_GATE_ENABLED", - /** Guild can be previewed before joining via Membership Screening or the directory */ - PreviewEnabled = "PREVIEW_ENABLED", - /** Guild has enabled ticketed events */ - TicketedEventsEnabled = "TICKETED_EVENTS_ENABLED", - /** Guild has enabled monetization */ - MonetizationEnabled = "MONETIZATION_ENABLED", - /** Guild has increased custom sticker slots */ - MoreStickers = "MORE_STICKERS", - /** Guild has access to create private threads */ - PrivateThreads = "PRIVATE_THREADS", - /** Guild is able to set role icons */ - RoleIcons = "ROLE_ICONS", - /** Guild has set up auto moderation rules */ - AutoModeration = "AUTO_MODERATION", + /** Guild has access to set an invite splash background */ + InviteSplash = "INVITE_SPLASH", + /** Guild has access to set 384 kbps bitrate in voice (previously VIP voice servers) */ + VipRegions = "VIP_REGIONS", + /** Guild has access to set a vanity URL */ + VanityUrl = "VANITY_URL", + /** Guild is verified */ + Verified = "VERIFIED", + /** Guild is partnered */ + Partnered = "PARTNERED", + /** Guild can enable welcome screen, Membership Screening, stage channels and discovery, and receives community updates */ + Community = "COMMUNITY", + /** Guild has access to use commerce features (i.e. create store channels) */ + Commerce = "COMMERCE", + /** Guild has access to create news channels */ + News = "NEWS", + /** Guild is able to be discovered in the directory */ + Discoverable = "DISCOVERABLE", + /** guild cannot be discoverable */ + DiscoverableDisabled = "DISCOVERABLE_DISABLED", + /** Guild is able to be featured in the directory */ + Feature = "FEATURABLE", + /** Guild has access to set an animated guild icon */ + AnimatedIcon = "ANIMATED_ICON", + /** Guild has access to set a guild banner image */ + Banner = "BANNER", + /** Guild has enabled the welcome screen */ + WelcomeScreenEnabled = "WELCOME_SCREEN_ENABLED", + /** Guild has enabled [Membership Screening](https://discord.com/developers/docs/resources/guild#membership-screening-object) */ + MemberVerificationGateEnabled = "MEMBER_VERIFICATION_GATE_ENABLED", + /** Guild can be previewed before joining via Membership Screening or the directory */ + PreviewEnabled = "PREVIEW_ENABLED", + /** Guild has enabled ticketed events */ + TicketedEventsEnabled = "TICKETED_EVENTS_ENABLED", + /** Guild has enabled monetization */ + MonetizationEnabled = "MONETIZATION_ENABLED", + /** Guild has increased custom sticker slots */ + MoreStickers = "MORE_STICKERS", + /** Guild has access to create private threads */ + PrivateThreads = "PRIVATE_THREADS", + /** Guild is able to set role icons */ + RoleIcons = "ROLE_ICONS", + /** Guild has set up auto moderation rules */ + AutoModeration = "AUTO_MODERATION", } /** https://discord.com/developers/docs/resources/guild#guild-object-mfa-level */ export enum MfaLevels { - /** Guild has no MFA/2FA requirement for moderation actions */ - None, - /** Guild has a 2FA requirement for moderation actions */ - Elevated, + /** Guild has no MFA/2FA requirement for moderation actions */ + None, + /** Guild has a 2FA requirement for moderation actions */ + Elevated, } /** https://discord.com/developers/docs/resources/guild#guild-object-system-channel-flags */ export enum SystemChannelFlags { - /** Suppress member join notifications */ - SuppressJoinNotifications = 1 << 0, - /** Suppress server boost notifications */ - SuppressPremiumSubscriptions = 1 << 1, - /** Suppress server setup tips */ - SuppressGuildReminderNotifications = 1 << 2, - /** Hide member join sticker reply buttons */ - SuppressJoinNotificationReplies = 1 << 3, + /** Suppress member join notifications */ + SuppressJoinNotifications = 1 << 0, + /** Suppress server boost notifications */ + SuppressPremiumSubscriptions = 1 << 1, + /** Suppress server setup tips */ + SuppressGuildReminderNotifications = 1 << 2, + /** Hide member join sticker reply buttons */ + SuppressJoinNotificationReplies = 1 << 3, } /** https://discord.com/developers/docs/resources/guild#guild-object-premium-tier */ export enum PremiumTiers { - /** Guild has not unlocked any Server Boost perks */ - None, - /** Guild has unlocked Server Boost level 1 perks */ - Tier1, - /** Guild has unlocked Server Boost level 2 perks */ - Tier2, - /** Guild has unlocked Server Boost level 3 perks */ - Tier3, + /** Guild has not unlocked any Server Boost perks */ + None, + /** Guild has unlocked Server Boost level 1 perks */ + Tier1, + /** Guild has unlocked Server Boost level 2 perks */ + Tier2, + /** Guild has unlocked Server Boost level 3 perks */ + Tier3, } /** https://discord.com/developers/docs/resources/guild#guild-object-guild-nsfw-level */ export enum GuildNsfwLevel { - Default, - Explicit, - Safe, - AgeRestricted, + Default, + Explicit, + Safe, + AgeRestricted, } /** https://discord.com/developers/docs/resources/channel#channel-object-channel-types */ export enum ChannelTypes { - /** A text channel within a server */ - GuildText, - /** A direct message between users */ - DM, - /** A voice channel within a server */ - GuildVoice, - /** A direct message between multiple users */ - GroupDm, - /** An organizational category that contains up to 50 channels */ - GuildCategory, - /** A channel that users can follow and crosspost into their own server */ - GuildNews, - /** A temporary sub-channel within a GUILD_NEWS channel */ - GuildNewsThread = 10, - /** A temporary sub-channel within a GUILD_TEXT channel */ - GuildPublicThread, - /** A temporary sub-channel within a GUILD_TEXT channel that is only viewable by those invited and those with the MANAGE_THREADS permission */ - GuildPrivateThread, - /** A voice channel for hosting events with an audience */ - GuildStageVoice, - /** A channel in a hub containing the listed servers */ - GuildDirectory, - /** A channel which can only contains threads */ - GuildForum, + /** A text channel within a server */ + GuildText, + /** A direct message between users */ + DM, + /** A voice channel within a server */ + GuildVoice, + /** A direct message between multiple users */ + GroupDm, + /** An organizational category that contains up to 50 channels */ + GuildCategory, + /** A channel that users can follow and crosspost into their own server */ + GuildNews, + /** A temporary sub-channel within a GUILD_NEWS channel */ + GuildNewsThread = 10, + /** A temporary sub-channel within a GUILD_TEXT channel */ + GuildPublicThread, + /** A temporary sub-channel within a GUILD_TEXT channel that is only viewable by those invited and those with the MANAGE_THREADS permission */ + GuildPrivateThread, + /** A voice channel for hosting events with an audience */ + GuildStageVoice, + /** A channel in a hub containing the listed servers */ + GuildDirectory, + /** A channel which can only contains threads */ + GuildForum, } export enum OverwriteTypes { - Role, - Member, + Role, + Member, } export enum VideoQualityModes { - /** Discord chooses the quality for optimal performance */ - Auto = 1, - /** 720p */ - Full, + /** Discord chooses the quality for optimal performance */ + Auto = 1, + /** 720p */ + Full, } /** https://discord.com/developers/docs/topics/gateway#activity-object-activity-types */ export enum ActivityTypes { - Game, - Streaming, - Listening, - Watching, - Custom = 4, - Competing, + Game, + Streaming, + Listening, + Watching, + Custom = 4, + Competing, } /** https://discord.com/developers/docs/resources/channel#message-object-message-types */ export enum MessageTypes { - Default, - RecipientAdd, - RecipientRemove, - Call, - ChannelNameChange, - ChannelIconChange, - ChannelPinnedMessage, - GuildMemberJoin, - UserPremiumGuildSubscription, - UserPremiumGuildSubscriptionTier1, - UserPremiumGuildSubscriptionTier2, - UserPremiumGuildSubscriptionTier3, - ChannelFollowAdd, - GuildDiscoveryDisqualified = 14, - GuildDiscoveryRequalified, - GuildDiscoveryGracePeriodInitialWarning, - GuildDiscoveryGracePeriodFinalWarning, - ThreadCreated, - Reply, - ChatInputCommand, - ThreadStarterMessage, - GuildInviteReminder, - ContextMenuCommand, - AutoModerationAction, + Default, + RecipientAdd, + RecipientRemove, + Call, + ChannelNameChange, + ChannelIconChange, + ChannelPinnedMessage, + GuildMemberJoin, + UserPremiumGuildSubscription, + UserPremiumGuildSubscriptionTier1, + UserPremiumGuildSubscriptionTier2, + UserPremiumGuildSubscriptionTier3, + ChannelFollowAdd, + GuildDiscoveryDisqualified = 14, + GuildDiscoveryRequalified, + GuildDiscoveryGracePeriodInitialWarning, + GuildDiscoveryGracePeriodFinalWarning, + ThreadCreated, + Reply, + ChatInputCommand, + ThreadStarterMessage, + GuildInviteReminder, + ContextMenuCommand, + AutoModerationAction, } /** https://discord.com/developers/docs/resources/channel#message-object-message-activity-types */ export enum MessageActivityTypes { - Join = 1, - Spectate, - Listen, - JoinRequest, + Join = 1, + Spectate, + Listen, + JoinRequest, } /** https://discord.com/developers/docs/resources/sticker#sticker-object-sticker-types */ export enum StickerTypes { - /** an official sticker in a pack, part of Nitro or in a removed purchasable pack */ - Standard = 1, - /** a sticker uploaded to a Boosted guild for the guild's members */ - Guild, + /** an official sticker in a pack, part of Nitro or in a removed purchasable pack */ + Standard = 1, + /** a sticker uploaded to a Boosted guild for the guild's members */ + Guild, } /** https://discord.com/developers/docs/resources/sticker#sticker-object-sticker-format-types */ export enum StickerFormatTypes { - Png = 1, - APng, - Lottie, + Png = 1, + APng, + Lottie, } /** https://discord.com/developers/docs/interactions/slash-commands#interaction-interactiontype */ export enum InteractionTypes { - Ping = 1, - ApplicationCommand = 2, - MessageComponent = 3, - ApplicationCommandAutocomplete = 4, - ModalSubmit = 5, + Ping = 1, + ApplicationCommand = 2, + MessageComponent = 3, + ApplicationCommandAutocomplete = 4, + ModalSubmit = 5, } /** https://discord.com/developers/docs/interactions/slash-commands#applicationcommandoptiontype */ export enum ApplicationCommandOptionTypes { - SubCommand = 1, - SubCommandGroup, - String, - Integer, - Boolean, - User, - Channel, - Role, - Mentionable, - Number, - Attachment, + SubCommand = 1, + SubCommandGroup, + String, + Integer, + Boolean, + User, + Channel, + Role, + Mentionable, + Number, + Attachment, } /** https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events */ export enum AuditLogEvents { - /** Server settings were updated */ - GuildUpdate = 1, - /** Channel was created */ - ChannelCreate = 10, - /** Channel settings were updated */ - ChannelUpdate, - /** Channel was deleted */ - ChannelDelete, - /** Permission overwrite was added to a channel */ - ChannelOverwriteCreate, - /** Permission overwrite was updated for a channel */ - ChannelOverwriteUpdate, - /** Permission overwrite was deleted from a channel */ - ChannelOverwriteDelete, - /** Member was removed from server */ - MemberKick = 20, - /** Members were pruned from server */ - MemberPrune, - /** Member was banned from server */ - MemberBanAdd, - /** Server ban was lifted for a member */ - MemberBanRemove, - /** Member was updated in server */ - MemberUpdate, - /** Member was added or removed from a role */ - MemberRoleUpdate, - /** Member was moved to a different voice channel */ - MemberMove, - /** Member was disconnected from a voice channel */ - MemberDisconnect, - /** Bot user was added to server */ - BotAdd, - /** Role was created */ - RoleCreate = 30, - /** Role was edited */ - RoleUpdate, - /** Role was deleted */ - RoleDelete, - /** Server invite was created */ - InviteCreate = 40, - /** Server invite was updated */ - InviteUpdate, - /** Server invite was deleted */ - InviteDelete, - /** Webhook was created */ - WebhookCreate = 50, - /** Webhook properties or channel were updated */ - WebhookUpdate, - /** Webhook was deleted */ - WebhookDelete, - /** Emoji was created */ - EmojiCreate = 60, - /** Emoji name was updated */ - EmojiUpdate, - /** Emoji was deleted */ - EmojiDelete, - /** Single message was deleted */ - MessageDelete = 72, - /** Multiple messages were deleted */ - MessageBulkDelete, - /** Messaged was pinned to a channel */ - MessagePin, - /** Message was unpinned from a channel */ - MessageUnpin, - /** App was added to server */ - IntegrationCreate = 80, - /** App was updated (as an example, its scopes were updated) */ - IntegrationUpdate, - /** App was removed from server */ - IntegrationDelete, - /** Stage instance was created (stage channel becomes live) */ - StageInstanceCreate, - /** Stage instace details were updated */ - StageInstanceUpdate, - /** Stage instance was deleted (stage channel no longer live) */ - StageInstanceDelete, - /** Sticker was created */ - StickerCreate = 90, - /** Sticker details were updated */ - StickerUpdate, - /** Sticker was deleted */ - StickerDelete, - /** Event was created */ - GuildScheduledEventCreate = 100, - /** Event was updated */ - GuildScheduledEventUpdate, - /** Event was cancelled */ - GuildScheduledEventDelete, - /** Thread was created in a channel */ - ThreadCreate = 110, - /** Thread was updated */ - ThreadUpdate, - /** Thread was deleted */ - ThreadDelete, - /** Permissions were updated for a command */ - ApplicationCommandPermissionUpdate = 121, - /** Auto moderation rule was created */ - AutoModerationRuleCreate = 140, - /** Auto moderation rule was updated */ - AutoModerationRuleUpdate, - /** Auto moderation rule was deleted */ - AutoModerationRuleDelete, - /** Message was blocked by AutoMod according to a rule. */ - AutoModerationBlockMessage, + /** Server settings were updated */ + GuildUpdate = 1, + /** Channel was created */ + ChannelCreate = 10, + /** Channel settings were updated */ + ChannelUpdate, + /** Channel was deleted */ + ChannelDelete, + /** Permission overwrite was added to a channel */ + ChannelOverwriteCreate, + /** Permission overwrite was updated for a channel */ + ChannelOverwriteUpdate, + /** Permission overwrite was deleted from a channel */ + ChannelOverwriteDelete, + /** Member was removed from server */ + MemberKick = 20, + /** Members were pruned from server */ + MemberPrune, + /** Member was banned from server */ + MemberBanAdd, + /** Server ban was lifted for a member */ + MemberBanRemove, + /** Member was updated in server */ + MemberUpdate, + /** Member was added or removed from a role */ + MemberRoleUpdate, + /** Member was moved to a different voice channel */ + MemberMove, + /** Member was disconnected from a voice channel */ + MemberDisconnect, + /** Bot user was added to server */ + BotAdd, + /** Role was created */ + RoleCreate = 30, + /** Role was edited */ + RoleUpdate, + /** Role was deleted */ + RoleDelete, + /** Server invite was created */ + InviteCreate = 40, + /** Server invite was updated */ + InviteUpdate, + /** Server invite was deleted */ + InviteDelete, + /** Webhook was created */ + WebhookCreate = 50, + /** Webhook properties or channel were updated */ + WebhookUpdate, + /** Webhook was deleted */ + WebhookDelete, + /** Emoji was created */ + EmojiCreate = 60, + /** Emoji name was updated */ + EmojiUpdate, + /** Emoji was deleted */ + EmojiDelete, + /** Single message was deleted */ + MessageDelete = 72, + /** Multiple messages were deleted */ + MessageBulkDelete, + /** Messaged was pinned to a channel */ + MessagePin, + /** Message was unpinned from a channel */ + MessageUnpin, + /** App was added to server */ + IntegrationCreate = 80, + /** App was updated (as an example, its scopes were updated) */ + IntegrationUpdate, + /** App was removed from server */ + IntegrationDelete, + /** Stage instance was created (stage channel becomes live) */ + StageInstanceCreate, + /** Stage instace details were updated */ + StageInstanceUpdate, + /** Stage instance was deleted (stage channel no longer live) */ + StageInstanceDelete, + /** Sticker was created */ + StickerCreate = 90, + /** Sticker details were updated */ + StickerUpdate, + /** Sticker was deleted */ + StickerDelete, + /** Event was created */ + GuildScheduledEventCreate = 100, + /** Event was updated */ + GuildScheduledEventUpdate, + /** Event was cancelled */ + GuildScheduledEventDelete, + /** Thread was created in a channel */ + ThreadCreate = 110, + /** Thread was updated */ + ThreadUpdate, + /** Thread was deleted */ + ThreadDelete, + /** Permissions were updated for a command */ + ApplicationCommandPermissionUpdate = 121, + /** Auto moderation rule was created */ + AutoModerationRuleCreate = 140, + /** Auto moderation rule was updated */ + AutoModerationRuleUpdate, + /** Auto moderation rule was deleted */ + AutoModerationRuleDelete, + /** Message was blocked by AutoMod according to a rule. */ + AutoModerationBlockMessage, } export enum ScheduledEventPrivacyLevel { - /** the scheduled event is public and available in discovery. DISCORD DEVS DISABLED THIS! WILL ERROR IF USED! */ - // Public = 1, - /** the scheduled event is only accessible to guild members */ - GuildOnly = 2, + /** the scheduled event is public and available in discovery. DISCORD DEVS DISABLED THIS! WILL ERROR IF USED! */ + // Public = 1, + /** the scheduled event is only accessible to guild members */ + GuildOnly = 2, } export enum ScheduledEventEntityType { - StageInstance = 1, - Voice, - External, + StageInstance = 1, + Voice, + External, } export enum ScheduledEventStatus { - Scheduled = 1, - Active, - Completed, - Canceled, + Scheduled = 1, + Active, + Completed, + Canceled, } /** https://discord.com/developers/docs/resources/invite#invite-object-target-user-types */ export enum TargetTypes { - Stream = 1, - EmbeddedApplication, + Stream = 1, + EmbeddedApplication, } export enum ApplicationCommandTypes { - /** A text-based command that shows up when a user types `/` */ - ChatInput = 1, - /** A UI-based command that shows up when you right click or tap on a user */ - User, - /** A UI-based command that shows up when you right click or tap on a message */ - Message, + /** A text-based command that shows up when a user types `/` */ + ChatInput = 1, + /** A UI-based command that shows up when you right click or tap on a user */ + User, + /** A UI-based command that shows up when you right click or tap on a message */ + Message, } export enum ApplicationCommandPermissionTypes { - Role = 1, - User, - Channel, + Role = 1, + User, + Channel, } /** https://discord.com/developers/docs/topics/gateway#activity-object-activity-flags */ export enum ActivityFlags { - Instance = 1 << 0, - Join = 1 << 1, - Spectate = 1 << 2, - JoinRequest = 1 << 3, - Sync = 1 << 4, - Play = 1 << 5, - PartyPrivacyFriends = 1 << 6, - PartyPrivacyVoiceChannel = 1 << 7, - Embedded = 1 << 8, + Instance = 1 << 0, + Join = 1 << 1, + Spectate = 1 << 2, + JoinRequest = 1 << 3, + Sync = 1 << 4, + Play = 1 << 5, + PartyPrivacyFriends = 1 << 6, + PartyPrivacyVoiceChannel = 1 << 7, + Embedded = 1 << 8, } /** https://discord.com/developers/docs/topics/permissions#permissions-bitwise-permission-flags */ export enum BitwisePermissionFlags { - /** Allows creation of instant invites */ - CREATE_INSTANT_INVITE = 0x0000000000000001, - /** Allows kicking members */ - KICK_MEMBERS = 0x0000000000000002, - /** Allows banning members */ - BAN_MEMBERS = 0x0000000000000004, - /** Allows all permissions and bypasses channel permission overwrites */ - ADMINISTRATOR = 0x0000000000000008, - /** Allows management and editing of channels */ - MANAGE_CHANNELS = 0x0000000000000010, - /** Allows management and editing of the guild */ - MANAGE_GUILD = 0x0000000000000020, - /** Allows for the addition of reactions to messages */ - ADD_REACTIONS = 0x0000000000000040, - /** Allows for viewing of audit logs */ - VIEW_AUDIT_LOG = 0x0000000000000080, - /** Allows for using priority speaker in a voice channel */ - PRIORITY_SPEAKER = 0x0000000000000100, - /** Allows the user to go live */ - STREAM = 0x0000000000000200, - /** Allows guild members to view a channel, which includes reading messages in text channels and joining voice channels */ - VIEW_CHANNEL = 0x0000000000000400, - /** Allows for sending messages in a channel. (does not allow sending messages in threads) */ - SEND_MESSAGES = 0x0000000000000800, - /** Allows for sending of /tts messages */ - SEND_TTS_MESSAGES = 0x0000000000001000, - /** Allows for deletion of other users messages */ - MANAGE_MESSAGES = 0x0000000000002000, - /** Links sent by users with this permission will be auto-embedded */ - EMBED_LINKS = 0x0000000000004000, - /** Allows for uploading images and files */ - ATTACH_FILES = 0x0000000000008000, - /** Allows for reading of message history */ - READ_MESSAGE_HISTORY = 0x0000000000010000, - /** Allows for using the @everyone tag to notify all users in a channel, and the @here tag to notify all online users in a channel */ - MENTION_EVERYONE = 0x0000000000020000, - /** Allows the usage of custom emojis from other servers */ - USE_EXTERNAL_EMOJIS = 0x0000000000040000, - /** Allows for viewing guild insights */ - VIEW_GUILD_INSIGHTS = 0x0000000000080000, - /** Allows for joining of a voice channel */ - CONNECT = 0x0000000000100000, - /** Allows for speaking in a voice channel */ - SPEAK = 0x0000000000200000, - /** Allows for muting members in a voice channel */ - MUTE_MEMBERS = 0x0000000000400000, - /** Allows for deafening of members in a voice channel */ - DEAFEN_MEMBERS = 0x0000000000800000, - /** Allows for moving of members between voice channels */ - MOVE_MEMBERS = 0x0000000001000000, - /** Allows for using voice-activity-detection in a voice channel */ - USE_VAD = 0x0000000002000000, - /** Allows for modification of own nickname */ - CHANGE_NICKNAME = 0x0000000004000000, - /** Allows for modification of other users nicknames */ - MANAGE_NICKNAMES = 0x0000000008000000, - /** Allows management and editing of roles */ - MANAGE_ROLES = 0x0000000010000000, - /** Allows management and editing of webhooks */ - MANAGE_WEBHOOKS = 0x0000000020000000, - /** Allows management and editing of emojis */ - MANAGE_EMOJIS = 0x0000000040000000, - /** Allows members to use application commands in text channels */ - USE_SLASH_COMMANDS = 0x0000000080000000, - /** Allows for requesting to speak in stage channels. */ - REQUEST_TO_SPEAK = 0x0000000100000000, - /** Allows for creating, editing, and deleting scheduled events */ - MANAGE_EVENTS = 0x0000000200000000, - /** Allows for deleting and archiving threads, and viewing all private threads */ - MANAGE_THREADS = 0x0000000400000000, - /** Allows for creating public and announcement threads */ - CREATE_PUBLIC_THREADS = 0x0000000800000000, - /** Allows for creating private threads */ - CREATE_PRIVATE_THREADS = 0x0000001000000000, - /** Allows the usage of custom stickers from other servers */ - USE_EXTERNAL_STICKERS = 0x0000002000000000, - /** Allows for sending messages in threads */ - SEND_MESSAGES_IN_THREADS = 0x0000004000000000, - /** Allows for launching activities (applications with the `EMBEDDED` flag) in a voice channel. */ - USE_EMBEDDED_ACTIVITIES = 0x0000008000000000, - /** Allows for timing out users to prevent them from sending or reacting to messages in chat and threads, and from speaking in voice and stage channels */ - MODERATE_MEMBERS = 0x0000010000000000, + /** Allows creation of instant invites */ + CREATE_INSTANT_INVITE = 0x0000000000000001, + /** Allows kicking members */ + KICK_MEMBERS = 0x0000000000000002, + /** Allows banning members */ + BAN_MEMBERS = 0x0000000000000004, + /** Allows all permissions and bypasses channel permission overwrites */ + ADMINISTRATOR = 0x0000000000000008, + /** Allows management and editing of channels */ + MANAGE_CHANNELS = 0x0000000000000010, + /** Allows management and editing of the guild */ + MANAGE_GUILD = 0x0000000000000020, + /** Allows for the addition of reactions to messages */ + ADD_REACTIONS = 0x0000000000000040, + /** Allows for viewing of audit logs */ + VIEW_AUDIT_LOG = 0x0000000000000080, + /** Allows for using priority speaker in a voice channel */ + PRIORITY_SPEAKER = 0x0000000000000100, + /** Allows the user to go live */ + STREAM = 0x0000000000000200, + /** Allows guild members to view a channel, which includes reading messages in text channels and joining voice channels */ + VIEW_CHANNEL = 0x0000000000000400, + /** Allows for sending messages in a channel. (does not allow sending messages in threads) */ + SEND_MESSAGES = 0x0000000000000800, + /** Allows for sending of /tts messages */ + SEND_TTS_MESSAGES = 0x0000000000001000, + /** Allows for deletion of other users messages */ + MANAGE_MESSAGES = 0x0000000000002000, + /** Links sent by users with this permission will be auto-embedded */ + EMBED_LINKS = 0x0000000000004000, + /** Allows for uploading images and files */ + ATTACH_FILES = 0x0000000000008000, + /** Allows for reading of message history */ + READ_MESSAGE_HISTORY = 0x0000000000010000, + /** Allows for using the @everyone tag to notify all users in a channel, and the @here tag to notify all online users in a channel */ + MENTION_EVERYONE = 0x0000000000020000, + /** Allows the usage of custom emojis from other servers */ + USE_EXTERNAL_EMOJIS = 0x0000000000040000, + /** Allows for viewing guild insights */ + VIEW_GUILD_INSIGHTS = 0x0000000000080000, + /** Allows for joining of a voice channel */ + CONNECT = 0x0000000000100000, + /** Allows for speaking in a voice channel */ + SPEAK = 0x0000000000200000, + /** Allows for muting members in a voice channel */ + MUTE_MEMBERS = 0x0000000000400000, + /** Allows for deafening of members in a voice channel */ + DEAFEN_MEMBERS = 0x0000000000800000, + /** Allows for moving of members between voice channels */ + MOVE_MEMBERS = 0x0000000001000000, + /** Allows for using voice-activity-detection in a voice channel */ + USE_VAD = 0x0000000002000000, + /** Allows for modification of own nickname */ + CHANGE_NICKNAME = 0x0000000004000000, + /** Allows for modification of other users nicknames */ + MANAGE_NICKNAMES = 0x0000000008000000, + /** Allows management and editing of roles */ + MANAGE_ROLES = 0x0000000010000000, + /** Allows management and editing of webhooks */ + MANAGE_WEBHOOKS = 0x0000000020000000, + /** Allows management and editing of emojis */ + MANAGE_EMOJIS = 0x0000000040000000, + /** Allows members to use application commands in text channels */ + USE_SLASH_COMMANDS = 0x0000000080000000, + /** Allows for requesting to speak in stage channels. */ + REQUEST_TO_SPEAK = 0x0000000100000000, + /** Allows for creating, editing, and deleting scheduled events */ + MANAGE_EVENTS = 0x0000000200000000, + /** Allows for deleting and archiving threads, and viewing all private threads */ + MANAGE_THREADS = 0x0000000400000000, + /** Allows for creating public and announcement threads */ + CREATE_PUBLIC_THREADS = 0x0000000800000000, + /** Allows for creating private threads */ + CREATE_PRIVATE_THREADS = 0x0000001000000000, + /** Allows the usage of custom stickers from other servers */ + USE_EXTERNAL_STICKERS = 0x0000002000000000, + /** Allows for sending messages in threads */ + SEND_MESSAGES_IN_THREADS = 0x0000004000000000, + /** Allows for launching activities (applications with the `EMBEDDED` flag) in a voice channel. */ + USE_EMBEDDED_ACTIVITIES = 0x0000008000000000, + /** Allows for timing out users to prevent them from sending or reacting to messages in chat and threads, and from speaking in voice and stage channels */ + MODERATE_MEMBERS = 0x0000010000000000, } export type PermissionStrings = keyof typeof BitwisePermissionFlags; /** https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice */ export enum VoiceOpcodes { - /** Begin a voice websocket connection. */ - Identify, - /** Select the voice protocol. */ - SelectProtocol, - /** Complete the websocket handshake. */ - Ready, - /** Keep the websocket connection alive. */ - Heartbeat, - /** Describe the session. */ - SessionDescription, - /** Indicate which users are speaking. */ - Speaking, - /** Sent to acknowledge a received client heartbeat. */ - HeartbeatACK, - /** Resume a connection. */ - Resume, - /** Time to wait between sending heartbeats in milliseconds. */ - Hello, - /** Acknowledge a successful session resume. */ - Resumed, - /** A client has disconnected from the voice channel */ - ClientDisconnect = 13, + /** Begin a voice websocket connection. */ + Identify, + /** Select the voice protocol. */ + SelectProtocol, + /** Complete the websocket handshake. */ + Ready, + /** Keep the websocket connection alive. */ + Heartbeat, + /** Describe the session. */ + SessionDescription, + /** Indicate which users are speaking. */ + Speaking, + /** Sent to acknowledge a received client heartbeat. */ + HeartbeatACK, + /** Resume a connection. */ + Resume, + /** Time to wait between sending heartbeats in milliseconds. */ + Hello, + /** Acknowledge a successful session resume. */ + Resumed, + /** A client has disconnected from the voice channel */ + ClientDisconnect = 13, } /** https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice */ export enum VoiceCloseEventCodes { - /** You sent an invalid [opcode](https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice-voice-opcodes). */ - UnknownOpcode = 4001, - /** You sent a invalid payload in your [identifying](https://discord.com/developers/docs/topics/gateway#identify) to the Gateway. */ - FailedToDecodePayload, - /** You sent a payload before [identifying](https://discord.com/developers/docs/topics/gateway#identify) with the Gateway. */ - NotAuthenticated, - /** The token you sent in your [identify](https://discord.com/developers/docs/topics/gateway#identify) payload is incorrect. */ - AuthenticationFailed, - /** You sent more than one [identify](https://discord.com/developers/docs/topics/gateway#identify) payload. Stahp. */ - AlreadyAuthenticated, - /** Your session is no longer valid. */ - SessionNoLongerValid, - /** Your session has timed out. */ - SessionTimedOut = 4009, - /** We can't find the server you're trying to connect to. */ - ServerNotFound = 4011, - /** We didn't recognize the [protocol](https://discord.com/developers/docs/topics/voice-connections#establishing-a-voice-udp-connection-example-select-protocol-payload) you sent. */ - UnknownProtocol, - /** Channel was deleted, you were kicked, voice server changed, or the main gateway session was dropped. Should not reconnect. */ - Disconnect = 4014, - /** The server crashed. Our bad! Try [resuming](https://discord.com/developers/docs/topics/voice-connections#resuming-voice-connection). */ - VoiceServerCrashed, - /** We didn't recognize your [encryption](https://discord.com/developers/docs/topics/voice-connections#encrypting-and-sending-voice). */ - UnknownEncryptionMode, + /** You sent an invalid [opcode](https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice-voice-opcodes). */ + UnknownOpcode = 4001, + /** You sent a invalid payload in your [identifying](https://discord.com/developers/docs/topics/gateway#identify) to the Gateway. */ + FailedToDecodePayload, + /** You sent a payload before [identifying](https://discord.com/developers/docs/topics/gateway#identify) with the Gateway. */ + NotAuthenticated, + /** The token you sent in your [identify](https://discord.com/developers/docs/topics/gateway#identify) payload is incorrect. */ + AuthenticationFailed, + /** You sent more than one [identify](https://discord.com/developers/docs/topics/gateway#identify) payload. Stahp. */ + AlreadyAuthenticated, + /** Your session is no longer valid. */ + SessionNoLongerValid, + /** Your session has timed out. */ + SessionTimedOut = 4009, + /** We can't find the server you're trying to connect to. */ + ServerNotFound = 4011, + /** We didn't recognize the [protocol](https://discord.com/developers/docs/topics/voice-connections#establishing-a-voice-udp-connection-example-select-protocol-payload) you sent. */ + UnknownProtocol, + /** Channel was deleted, you were kicked, voice server changed, or the main gateway session was dropped. Should not reconnect. */ + Disconnect = 4014, + /** The server crashed. Our bad! Try [resuming](https://discord.com/developers/docs/topics/voice-connections#resuming-voice-connection). */ + VoiceServerCrashed, + /** We didn't recognize your [encryption](https://discord.com/developers/docs/topics/voice-connections#encrypting-and-sending-voice). */ + UnknownEncryptionMode, } /** https://discord.com/developers/docs/topics/opcodes-and-status-codes#rpc */ export enum RpcErrorCodes { - /** An unknown error occurred. */ - UnknownError = 1000, - /** You sent an invalid payload. */ - InvalidPayload = 4000, - /** Invalid command name specified. */ - InvalidCommand = 4002, - /** Invalid guild ID specified. */ - InvalidGuild, - /** Invalid event name specified. */ - InvalidEvent, - /** Invalid channel ID specified. */ - InvalidChannel, - /** You lack permissions to access the given resource. */ - InvalidPermissions, - /** An invalid OAuth2 application ID was used to authorize or authenticate with. */ - InvalidClientId, - /** An invalid OAuth2 application origin was used to authorize or authenticate with. */ - InvalidOrigin, - /** An invalid OAuth2 token was used to authorize or authenticate with. */ - InvalidToken, - /** The specified user ID was invalid. */ - InvalidUser, - /** A standard OAuth2 error occurred; check the data object for the OAuth2 error details. */ - OAuth2Error = 5000, - /** An asynchronous `SELECT_TEXT_CHANNEL`/`SELECT_VOICE_CHANNEL` command timed out. */ - SelectChannelTimedOut, - /** An asynchronous `GET_GUILD` command timed out. */ - GetGuildTimedOut, - /** You tried to join a user to a voice channel but the user was already in one. */ - SelectVoiceForceRequired, - /** You tried to capture more than one shortcut key at once. */ - CaptureShortcutAlreadyListening, + /** An unknown error occurred. */ + UnknownError = 1000, + /** You sent an invalid payload. */ + InvalidPayload = 4000, + /** Invalid command name specified. */ + InvalidCommand = 4002, + /** Invalid guild ID specified. */ + InvalidGuild, + /** Invalid event name specified. */ + InvalidEvent, + /** Invalid channel ID specified. */ + InvalidChannel, + /** You lack permissions to access the given resource. */ + InvalidPermissions, + /** An invalid OAuth2 application ID was used to authorize or authenticate with. */ + InvalidClientId, + /** An invalid OAuth2 application origin was used to authorize or authenticate with. */ + InvalidOrigin, + /** An invalid OAuth2 token was used to authorize or authenticate with. */ + InvalidToken, + /** The specified user ID was invalid. */ + InvalidUser, + /** A standard OAuth2 error occurred; check the data object for the OAuth2 error details. */ + OAuth2Error = 5000, + /** An asynchronous `SELECT_TEXT_CHANNEL`/`SELECT_VOICE_CHANNEL` command timed out. */ + SelectChannelTimedOut, + /** An asynchronous `GET_GUILD` command timed out. */ + GetGuildTimedOut, + /** You tried to join a user to a voice channel but the user was already in one. */ + SelectVoiceForceRequired, + /** You tried to capture more than one shortcut key at once. */ + CaptureShortcutAlreadyListening, } /** https://discord.com/developers/docs/topics/opcodes-and-status-codes#rpc */ export enum RpcCloseEventCodes { - /** You connected to the RPC server with an invalid client ID. */ - InvalidClientId = 4000, - /** You connected to the RPC server with an invalid origin. */ - InvalidOrigin, - /** You are being rate limited. */ - RateLimited, - /** The OAuth2 token associated with a connection was revoked, get a new one! */ - TokenRevoked, - /** The RPC Server version specified in the connection string was not valid. */ - InvalidVersion, - /** The encoding specified in the connection string was not valid. */ - InvalidEncoding, + /** You connected to the RPC server with an invalid client ID. */ + InvalidClientId = 4000, + /** You connected to the RPC server with an invalid origin. */ + InvalidOrigin, + /** You are being rate limited. */ + RateLimited, + /** The OAuth2 token associated with a connection was revoked, get a new one! */ + TokenRevoked, + /** The RPC Server version specified in the connection string was not valid. */ + InvalidVersion, + /** The encoding specified in the connection string was not valid. */ + InvalidEncoding, } /** https://discord.com/developers/docs/topics/opcodes-and-status-codes#http */ export enum HTTPResponseCodes { - /** The request completed successfully. */ - Ok = 200, - /** The entity was created successfully. */ - Created, - /** The request completed successfully but returned no content. */ - NoContent = 204, - /** The entity was not modified (no action was taken). */ - NotModified = 304, - /** The request was improperly formatted, or the server couldn't understand it. */ - BadRequest = 400, - /** The `Authorization` header was missing or invalid. */ - Unauthorized, - /** The `Authorization` token you passed did not have permission to the resource. */ - Forbidden = 403, - /** The resource at the location specified doesn't exist. */ - NotFound, - /** The HTTP method used is not valid for the location specified. */ - MethodNotAllowed, - /** You are being rate limited, see [Rate Limits](https://discord.com/developers/docs/topics/rate-limits). */ - TooManyRequests = 429, - /** There was not a gateway available to process your request. Wait a bit and retry. */ - GatewayUnavailable = 502, + /** The request completed successfully. */ + Ok = 200, + /** The entity was created successfully. */ + Created, + /** The request completed successfully but returned no content. */ + NoContent = 204, + /** The entity was not modified (no action was taken). */ + NotModified = 304, + /** The request was improperly formatted, or the server couldn't understand it. */ + BadRequest = 400, + /** The `Authorization` header was missing or invalid. */ + Unauthorized, + /** The `Authorization` token you passed did not have permission to the resource. */ + Forbidden = 403, + /** The resource at the location specified doesn't exist. */ + NotFound, + /** The HTTP method used is not valid for the location specified. */ + MethodNotAllowed, + /** You are being rate limited, see [Rate Limits](https://discord.com/developers/docs/topics/rate-limits). */ + TooManyRequests = 429, + /** There was not a gateway available to process your request. Wait a bit and retry. */ + GatewayUnavailable = 502, } /** https://discord.com/developers/docs/topics/opcodes-and-status-codes#opcodes-and-status-codes */ export enum GatewayCloseEventCodes { - /** A normal closure of the gateway. - * You may attempt to reconnect. - */ - NormalClosure = 1000, - /** We're not sure what went wrong. Try reconnecting? */ - UnknownError = 4000, - /** You sent an invalid [Gateway opcode](https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes) or an invalid payload for an opcode. Don't do that! */ - UnknownOpcode, - /** You sent an invalid [payload](https://discord.com/developers/docs/topics/gateway#sending-payloads) to us. Don't do that! */ - DecodeError, - /** You sent us a payload prior to [identifying](https://discord.com/developers/docs/topics/gateway#identify). */ - NotAuthenticated, - /** The account token sent with your [identify payload](https://discord.com/developers/docs/topics/gateway#identify) is incorrect. */ - AuthenticationFailed, - /** You sent more than one identify payload. Don't do that! */ - AlreadyAuthenticated, - /** The sequence sent when [resuming](https://discord.com/developers/docs/topics/gateway#resume) the session was invalid. Reconnect and start a new session. */ - InvalidSeq = 4007, - /** Woah nelly! You're sending payloads to us too quickly. Slow it down! You will be disconnected on receiving this. */ - RateLimited, - /** Your session timed out. Reconnect and start a new one. */ - SessionTimedOut, - /** You sent us an invalid [shard when identifying](https://discord.com/developers/docs/topics/gateway#sharding). */ - InvalidShard, - /** The session would have handled too many guilds - you are required to [shard](https://discord.com/developers/docs/topics/gateway#sharding) your connection in order to connect. */ - ShardingRequired, - /** You sent an invalid version for the gateway. */ - InvalidApiVersion, - /** You sent an invalid intent for a [Gateway Intent](https://discord.com/developers/docs/topics/gateway#gateway-intents). You may have incorrectly calculated the bitwise value. */ - InvalidIntents, - /** You sent a disallowed intent for a [Gateway Intent](https://discord.com/developers/docs/topics/gateway#gateway-intents). You may have tried to specify an intent that you [have not enabled or are not approved for](https://discord.com/developers/docs/topics/gateway#privileged-intents). */ - DisallowedIntents, + /** A normal closure of the gateway. + * You may attempt to reconnect. + */ + NormalClosure = 1000, + /** We're not sure what went wrong. Try reconnecting? */ + UnknownError = 4000, + /** You sent an invalid [Gateway opcode](https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes) or an invalid payload for an opcode. Don't do that! */ + UnknownOpcode, + /** You sent an invalid [payload](https://discord.com/developers/docs/topics/gateway#sending-payloads) to us. Don't do that! */ + DecodeError, + /** You sent us a payload prior to [identifying](https://discord.com/developers/docs/topics/gateway#identify). */ + NotAuthenticated, + /** The account token sent with your [identify payload](https://discord.com/developers/docs/topics/gateway#identify) is incorrect. */ + AuthenticationFailed, + /** You sent more than one identify payload. Don't do that! */ + AlreadyAuthenticated, + /** The sequence sent when [resuming](https://discord.com/developers/docs/topics/gateway#resume) the session was invalid. Reconnect and start a new session. */ + InvalidSeq = 4007, + /** Woah nelly! You're sending payloads to us too quickly. Slow it down! You will be disconnected on receiving this. */ + RateLimited, + /** Your session timed out. Reconnect and start a new one. */ + SessionTimedOut, + /** You sent us an invalid [shard when identifying](https://discord.com/developers/docs/topics/gateway#sharding). */ + InvalidShard, + /** The session would have handled too many guilds - you are required to [shard](https://discord.com/developers/docs/topics/gateway#sharding) your connection in order to connect. */ + ShardingRequired, + /** You sent an invalid version for the gateway. */ + InvalidApiVersion, + /** You sent an invalid intent for a [Gateway Intent](https://discord.com/developers/docs/topics/gateway#gateway-intents). You may have incorrectly calculated the bitwise value. */ + InvalidIntents, + /** You sent a disallowed intent for a [Gateway Intent](https://discord.com/developers/docs/topics/gateway#gateway-intents). You may have tried to specify an intent that you [have not enabled or are not approved for](https://discord.com/developers/docs/topics/gateway#privileged-intents). */ + DisallowedIntents, } /** https://discord.com/developers/docs/resources/invite#invite-object-invite-target-types */ export enum InviteTargetTypes { - Stream = 1, - EmbeddedApplication, + Stream = 1, + EmbeddedApplication, } /** https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes */ export enum GatewayOpcodes { - /** An event was dispatched. */ - Dispatch, - /** Fired periodically by the client to keep the connection alive. */ - Heartbeat, - /** Starts a new session during the initial handshake. */ - Identify, - /** Update the client's presence. */ - PresenceUpdate, - /** Used to join/leave or move between voice channels. */ + /** An event was dispatched. */ + Dispatch, + /** Fired periodically by the client to keep the connection alive. */ + Heartbeat, + /** Starts a new session during the initial handshake. */ + Identify, + /** Update the client's presence. */ + PresenceUpdate, + /** Used to join/leave or move between voice channels. */ - VoiceStateUpdate, - /** Resume a previous session that was disconnected. */ - Resume = 6, - /** You should attempt to reconnect and resume immediately. */ - Reconnect, - /** Request information about offline guild members in a large guild. */ - RequestGuildMembers, - /** The session has been invalidated. You should reconnect and identify/resume accordingly. */ - InvalidSession, - /** Sent immediately after connecting, contains the `heartbeat_interval` to use. */ - Hello, - /** Sent in response to receiving a heartbeat to acknowledge that it has been received. */ - HeartbeatACK, + VoiceStateUpdate, + /** Resume a previous session that was disconnected. */ + Resume = 6, + /** You should attempt to reconnect and resume immediately. */ + Reconnect, + /** Request information about offline guild members in a large guild. */ + RequestGuildMembers, + /** The session has been invalidated. You should reconnect and identify/resume accordingly. */ + InvalidSession, + /** Sent immediately after connecting, contains the `heartbeat_interval` to use. */ + Hello, + /** Sent in response to receiving a heartbeat to acknowledge that it has been received. */ + HeartbeatACK, } export type GatewayDispatchEventNames = - | "READY" - | "CHANNEL_CREATE" - | "CHANNEL_DELETE" - | "CHANNEL_PINS_UPDATE" - | "CHANNEL_UPDATE" - | "GUILD_BAN_ADD" - | "GUILD_BAN_REMOVE" - | "GUILD_CREATE" - | "GUILD_DELETE" - | "GUILD_EMOJIS_UPDATE" - | "GUILD_INTEGRATIONS_UPDATE" - | "GUILD_MEMBER_ADD" - | "GUILD_MEMBER_REMOVE" - | "GUILD_MEMBER_UPDATE" - | "GUILD_MEMBERS_CHUNK" - | "GUILD_ROLE_CREATE" - | "GUILD_ROLE_DELETE" - | "GUILD_ROLE_UPDATE" - | "GUILD_UPDATE" - | "GUILD_SCHEDULED_EVENT_CREATE" - | "GUILD_SCHEDULED_EVENT_DELETE" - | "GUILD_SCHEDULED_EVENT_UPDATE" - | "GUILD_SCHEDULED_EVENT_USER_ADD" - | "GUILD_SCHEDULED_EVENT_USER_REMOVE" - | "INTERACTION_CREATE" - | "INVITE_CREATE" - | "INVITE_DELETE" - | "MESSAGE_CREATE" - | "MESSAGE_DELETE_BULK" - | "MESSAGE_DELETE" - | "MESSAGE_REACTION_ADD" - | "MESSAGE_REACTION_REMOVE_ALL" - | "MESSAGE_REACTION_REMOVE_EMOJI" - | "MESSAGE_REACTION_REMOVE" - | "MESSAGE_UPDATE" - | "PRESENCE_UPDATE" - | "TYPING_START" - | "USER_UPDATE" - | "VOICE_SERVER_UPDATE" - | "VOICE_STATE_UPDATE" - | "WEBHOOKS_UPDATE" - | "INTEGRATION_CREATE" - | "INTEGRATION_UPDATE" - | "INTEGRATION_DELETE" - | "STAGE_INSTANCE_CREATE" - | "STAGE_INSTANCE_UPDATE" - | "STAGE_INSTANCE_DELETE" - | "THREAD_CREATE" - | "THREAD_UPDATE" - | "THREAD_DELETE" - | "THREAD_LIST_SYNC" - | "THREAD_MEMBERS_UPDATE"; + | "READY" + | "CHANNEL_CREATE" + | "CHANNEL_DELETE" + | "CHANNEL_PINS_UPDATE" + | "CHANNEL_UPDATE" + | "GUILD_BAN_ADD" + | "GUILD_BAN_REMOVE" + | "GUILD_CREATE" + | "GUILD_DELETE" + | "GUILD_EMOJIS_UPDATE" + | "GUILD_INTEGRATIONS_UPDATE" + | "GUILD_MEMBER_ADD" + | "GUILD_MEMBER_REMOVE" + | "GUILD_MEMBER_UPDATE" + | "GUILD_MEMBERS_CHUNK" + | "GUILD_ROLE_CREATE" + | "GUILD_ROLE_DELETE" + | "GUILD_ROLE_UPDATE" + | "GUILD_UPDATE" + | "GUILD_SCHEDULED_EVENT_CREATE" + | "GUILD_SCHEDULED_EVENT_DELETE" + | "GUILD_SCHEDULED_EVENT_UPDATE" + | "GUILD_SCHEDULED_EVENT_USER_ADD" + | "GUILD_SCHEDULED_EVENT_USER_REMOVE" + | "INTERACTION_CREATE" + | "INVITE_CREATE" + | "INVITE_DELETE" + | "MESSAGE_CREATE" + | "MESSAGE_DELETE_BULK" + | "MESSAGE_DELETE" + | "MESSAGE_REACTION_ADD" + | "MESSAGE_REACTION_REMOVE_ALL" + | "MESSAGE_REACTION_REMOVE_EMOJI" + | "MESSAGE_REACTION_REMOVE" + | "MESSAGE_UPDATE" + | "PRESENCE_UPDATE" + | "TYPING_START" + | "USER_UPDATE" + | "VOICE_SERVER_UPDATE" + | "VOICE_STATE_UPDATE" + | "WEBHOOKS_UPDATE" + | "INTEGRATION_CREATE" + | "INTEGRATION_UPDATE" + | "INTEGRATION_DELETE" + | "STAGE_INSTANCE_CREATE" + | "STAGE_INSTANCE_UPDATE" + | "STAGE_INSTANCE_DELETE" + | "THREAD_CREATE" + | "THREAD_UPDATE" + | "THREAD_DELETE" + | "THREAD_LIST_SYNC" + | "THREAD_MEMBERS_UPDATE"; export type GatewayEventNames = - | GatewayDispatchEventNames - | "READY" - | "RESUMED" - // THIS IS A CUSTOM DD EVENT NOT A DISCORD EVENT - | "GUILD_LOADED_DD"; + | GatewayDispatchEventNames + | "READY" + | "RESUMED" + // THIS IS A CUSTOM DD EVENT NOT A DISCORD EVENT + | "GUILD_LOADED_DD"; /** https://discord.com/developers/docs/topics/gateway#list-of-intents */ export enum GatewayIntents { - /** - * - GUILD_CREATE - * - GUILD_DELETE - * - GUILD_ROLE_CREATE - * - GUILD_ROLE_UPDATE - * - GUILD_ROLE_DELETE - * - CHANNEL_CREATE - * - CHANNEL_UPDATE - * - CHANNEL_DELETE - * - CHANNEL_PINS_UPDATE - * - THREAD_CREATE - * - THREAD_UPDATE - * - THREAD_DELETE - * - THREAD_LIST_SYNC - * - THREAD_MEMBER_UPDATE - * - THREAD_MEMBERS_UPDATE - * - STAGE_INSTANCE_CREATE - * - STAGE_INSTANCE_UPDATE - * - STAGE_INSTANCE_DELETE - */ - Guilds = 1 << 0, - /** - * - GUILD_MEMBER_ADD - * - GUILD_MEMBER_UPDATE - * - GUILD_MEMBER_REMOVE - */ - GuildMembers = 1 << 1, - /** - * - GUILD_BAN_ADD - * - GUILD_BAN_REMOVE - */ - GuildBans = 1 << 2, - /** - * - GUILD_EMOJIS_UPDATE - */ - GuildEmojis = 1 << 3, - /** - * - GUILD_INTEGRATIONS_UPDATE - * - INTEGRATION_CREATE - * - INTEGRATION_UPDATE - * - INTEGRATION_DELETE - */ - GuildIntegrations = 1 << 4, - /** Enables the following events: - * - WEBHOOKS_UPDATE - */ - GuildWebhooks = 1 << 5, - /** - * - INVITE_CREATE - * - INVITE_DELETE - */ - GuildInvites = 1 << 6, - /** - * - VOICE_STATE_UPDATE - */ - GuildVoiceStates = 1 << 7, - /** - * - PRESENCE_UPDATE - */ - GuildPresences = 1 << 8, - /** - * - MESSAGE_CREATE - * - MESSAGE_UPDATE - * - MESSAGE_DELETE - */ - GuildMessages = 1 << 9, - /** - * - MESSAGE_REACTION_ADD - * - MESSAGE_REACTION_REMOVE - * - MESSAGE_REACTION_REMOVE_ALL - * - MESSAGE_REACTION_REMOVE_EMOJI - */ - GuildMessageReactions = 1 << 10, - /** - * - TYPING_START - */ - GuildMessageTyping = 1 << 11, - /** - * - CHANNEL_CREATE - * - MESSAGE_CREATE - * - MESSAGE_UPDATE - * - MESSAGE_DELETE - * - CHANNEL_PINS_UPDATE - */ - DirectMessages = 1 << 12, - /** - * - MESSAGE_REACTION_ADD - * - MESSAGE_REACTION_REMOVE - * - MESSAGE_REACTION_REMOVE_ALL - * - MESSAGE_REACTION_REMOVE_EMOJI - */ - DirectMessageReactions = 1 << 13, - /** - * - TYPING_START - */ - DirectMessageTyping = 1 << 14, + /** + * - GUILD_CREATE + * - GUILD_DELETE + * - GUILD_ROLE_CREATE + * - GUILD_ROLE_UPDATE + * - GUILD_ROLE_DELETE + * - CHANNEL_CREATE + * - CHANNEL_UPDATE + * - CHANNEL_DELETE + * - CHANNEL_PINS_UPDATE + * - THREAD_CREATE + * - THREAD_UPDATE + * - THREAD_DELETE + * - THREAD_LIST_SYNC + * - THREAD_MEMBER_UPDATE + * - THREAD_MEMBERS_UPDATE + * - STAGE_INSTANCE_CREATE + * - STAGE_INSTANCE_UPDATE + * - STAGE_INSTANCE_DELETE + */ + Guilds = 1 << 0, + /** + * - GUILD_MEMBER_ADD + * - GUILD_MEMBER_UPDATE + * - GUILD_MEMBER_REMOVE + */ + GuildMembers = 1 << 1, + /** + * - GUILD_BAN_ADD + * - GUILD_BAN_REMOVE + */ + GuildBans = 1 << 2, + /** + * - GUILD_EMOJIS_UPDATE + */ + GuildEmojis = 1 << 3, + /** + * - GUILD_INTEGRATIONS_UPDATE + * - INTEGRATION_CREATE + * - INTEGRATION_UPDATE + * - INTEGRATION_DELETE + */ + GuildIntegrations = 1 << 4, + /** Enables the following events: + * - WEBHOOKS_UPDATE + */ + GuildWebhooks = 1 << 5, + /** + * - INVITE_CREATE + * - INVITE_DELETE + */ + GuildInvites = 1 << 6, + /** + * - VOICE_STATE_UPDATE + */ + GuildVoiceStates = 1 << 7, + /** + * - PRESENCE_UPDATE + */ + GuildPresences = 1 << 8, + /** + * - MESSAGE_CREATE + * - MESSAGE_UPDATE + * - MESSAGE_DELETE + */ + GuildMessages = 1 << 9, + /** + * - MESSAGE_REACTION_ADD + * - MESSAGE_REACTION_REMOVE + * - MESSAGE_REACTION_REMOVE_ALL + * - MESSAGE_REACTION_REMOVE_EMOJI + */ + GuildMessageReactions = 1 << 10, + /** + * - TYPING_START + */ + GuildMessageTyping = 1 << 11, + /** + * - CHANNEL_CREATE + * - MESSAGE_CREATE + * - MESSAGE_UPDATE + * - MESSAGE_DELETE + * - CHANNEL_PINS_UPDATE + */ + DirectMessages = 1 << 12, + /** + * - MESSAGE_REACTION_ADD + * - MESSAGE_REACTION_REMOVE + * - MESSAGE_REACTION_REMOVE_ALL + * - MESSAGE_REACTION_REMOVE_EMOJI + */ + DirectMessageReactions = 1 << 13, + /** + * - TYPING_START + */ + DirectMessageTyping = 1 << 14, - /** - * This intent will add `content` values to all message objects. - */ - MessageContent = 1 << 15, - /** - * - GUILD_SCHEDULED_EVENT_CREATE - * - GUILD_SCHEDULED_EVENT_UPDATE - * - GUILD_SCHEDULED_EVENT_DELETE - * - GUILD_SCHEDULED_EVENT_USER_ADD this is experimental and unstable. - * - GUILD_SCHEDULED_EVENT_USER_REMOVE this is experimental and unstable. - */ - GuildScheduledEvents = (1 << 16), + /** + * This intent will add `content` values to all message objects. + */ + MessageContent = 1 << 15, + /** + * - GUILD_SCHEDULED_EVENT_CREATE + * - GUILD_SCHEDULED_EVENT_UPDATE + * - GUILD_SCHEDULED_EVENT_DELETE + * - GUILD_SCHEDULED_EVENT_USER_ADD this is experimental and unstable. + * - GUILD_SCHEDULED_EVENT_USER_REMOVE this is experimental and unstable. + */ + GuildScheduledEvents = (1 << 16), - /** - * - AUTO_MODERATION_RULE_CREATE - * - AUTO_MODERATION_RULE_UPDATE - * - AUTO_MODERATION_RULE_DELETE - */ - AutoModerationConfiguration = (1 << 20), - /** - * - AUTO_MODERATION_ACTION_EXECUTION - */ - AutoModerationExecution = (1 << 21), + /** + * - AUTO_MODERATION_RULE_CREATE + * - AUTO_MODERATION_RULE_UPDATE + * - AUTO_MODERATION_RULE_DELETE + */ + AutoModerationConfiguration = (1 << 20), + /** + * - AUTO_MODERATION_ACTION_EXECUTION + */ + AutoModerationExecution = (1 << 21), } // ALIASES JUST FOR BETTER UX IN THIS CASE @@ -1026,204 +1026,204 @@ export type Intents = GatewayIntents; /** https://discord.com/developers/docs/interactions/slash-commands#interaction-response-interactionresponsetype */ export enum InteractionResponseTypes { - /** ACK a `Ping` */ - Pong = 1, - /** Respond to an interaction with a message */ - ChannelMessageWithSource = 4, - /** ACK an interaction and edit a response later, the user sees a loading state */ - DeferredChannelMessageWithSource = 5, - /** For components, ACK an interaction and edit the original message later; the user does not see a loading state */ - DeferredUpdateMessage = 6, - /** For components, edit the message the component was attached to */ - UpdateMessage = 7, - /** For Application Command Options, send an autocomplete result */ - ApplicationCommandAutocompleteResult = 8, - /** For Command or Component interactions, send a Modal response */ - Modal = 9, + /** ACK a `Ping` */ + Pong = 1, + /** Respond to an interaction with a message */ + ChannelMessageWithSource = 4, + /** ACK an interaction and edit a response later, the user sees a loading state */ + DeferredChannelMessageWithSource = 5, + /** For components, ACK an interaction and edit the original message later; the user does not see a loading state */ + DeferredUpdateMessage = 6, + /** For components, edit the message the component was attached to */ + UpdateMessage = 7, + /** For Application Command Options, send an autocomplete result */ + ApplicationCommandAutocompleteResult = 8, + /** For Command or Component interactions, send a Modal response */ + Modal = 9, } export enum Errors { - // Bot Role errors - BOTS_HIGHEST_ROLE_TOO_LOW = "BOTS_HIGHEST_ROLE_TOO_LOW", - // Channel Errors - CHANNEL_NOT_FOUND = "CHANNEL_NOT_FOUND", - CHANNEL_NOT_IN_GUILD = "CHANNEL_NOT_IN_GUILD", - CHANNEL_NOT_TEXT_BASED = "CHANNEL_NOT_TEXT_BASED", - CHANNEL_NOT_STAGE_VOICE = "CHANNEL_NOT_STAGE_VOICE", - MESSAGE_MAX_LENGTH = "MESSAGE_MAX_LENGTH", - RULES_CHANNEL_CANNOT_BE_DELETED = "RULES_CHANNEL_CANNOT_BE_DELETED", - UPDATES_CHANNEL_CANNOT_BE_DELETED = "UPDATES_CHANNEL_CANNOT_BE_DELETED", - INVALID_TOPIC_LENGTH = "INVALID_TOPIC_LENGTH", - // Guild Errors - GUILD_NOT_DISCOVERABLE = "GUILD_NOT_DISCOVERABLE", - GUILD_WIDGET_NOT_ENABLED = "GUILD_WIDGET_NOT_ENABLED", - GUILD_NOT_FOUND = "GUILD_NOT_FOUND", - MEMBER_NOT_FOUND = "MEMBER_NOT_FOUND", - MEMBER_NOT_IN_VOICE_CHANNEL = "MEMBER_NOT_IN_VOICE_CHANNEL", - MEMBER_SEARCH_LIMIT_TOO_HIGH = "MEMBER_SEARCH_LIMIT_TOO_HIGH", - MEMBER_SEARCH_LIMIT_TOO_LOW = "MEMBER_SEARCH_LIMIT_TOO_LOW", - PRUNE_MAX_DAYS = "PRUNE_MAX_DAYS", - ROLE_NOT_FOUND = "ROLE_NOT_FOUND", - // Thread errors - INVALID_THREAD_PARENT_CHANNEL_TYPE = "INVALID_THREAD_PARENT_CHANNEL_TYPE", - GUILD_NEWS_CHANNEL_ONLY_SUPPORT_PUBLIC_THREADS = "GUILD_NEWS_CHANNEL_ONLY_SUPPORT_PUBLIC_THREADS", - NOT_A_THREAD_CHANNEL = "NOT_A_THREAD_CHANNEL", - MISSING_MANAGE_THREADS_AND_NOT_MEMBER = "MISSING_MANAGE_THREADS_AND_NOT_MEMBER", - CANNOT_GET_MEMBERS_OF_AN_UNJOINED_PRIVATE_THREAD = "CANNOT_GET_MEMBERS_OF_AN_UNJOINED_PRIVATE_THREAD", - HAVE_TO_BE_THE_CREATOR_OF_THE_THREAD_OR_HAVE_MANAGE_THREADS_TO_REMOVE_MEMBERS = - "HAVE_TO_BE_THE_CREATOR_OF_THE_THREAD_OR_HAVE_MANAGE_THREADS_TO_REMOVE_MEMBERS", - // Message Get Errors - INVALID_GET_MESSAGES_LIMIT = "INVALID_GET_MESSAGES_LIMIT", - // Message Delete Errors - DELETE_MESSAGES_MIN = "DELETE_MESSAGES_MIN", - PRUNE_MIN_DAYS = "PRUNE_MIN_DAYS", - // Interaction Errors - INVALID_SLASH_DESCRIPTION = "INVALID_SLASH_DESCRIPTION", - INVALID_SLASH_NAME = "INVALID_SLASH_NAME", - INVALID_SLASH_OPTIONS = "INVALID_SLASH_OPTIONS", - INVALID_SLASH_OPTIONS_CHOICES = "INVALID_SLASH_OPTIONS_CHOICES", - TOO_MANY_SLASH_OPTIONS = "TOO_MANY_SLASH_OPTIONS", - INVALID_SLASH_OPTION_CHOICE_NAME = "INVALID_SLASH_OPTION_CHOICE_NAME", - INVALID_SLASH_OPTIONS_CHOICE_VALUE_TYPE = "INVALID_SLASH_OPTIONS_CHOICE_VALUE_TYPE", - TOO_MANY_SLASH_OPTION_CHOICES = "TOO_MANY_SLASH_OPTION_CHOICES", - ONLY_STRING_OR_INTEGER_OPTIONS_CAN_HAVE_CHOICES = "ONLY_STRING_OR_INTEGER_OPTIONS_CAN_HAVE_CHOICES", - INVALID_SLASH_OPTION_NAME = "INVALID_SLASH_OPTION_NAME", - INVALID_SLASH_OPTION_DESCRIPTION = "INVALID_SLASH_OPTION_DESCRIPTION", - INVALID_CONTEXT_MENU_COMMAND_NAME = "INVALID_CONTEXT_MENU_COMMAND_NAME", - INVALID_CONTEXT_MENU_COMMAND_DESCRIPTION = "INVALID_CONTEXT_MENU_COMMAND_DESCRIPTION", - // Webhook Errors - INVALID_WEBHOOK_NAME = "INVALID_WEBHOOK_NAME", - INVALID_WEBHOOK_OPTIONS = "INVALID_WEBHOOK_OPTIONS", - // Permission Errors - MISSING_ADD_REACTIONS = "MISSING_ADD_REACTIONS", - MISSING_ADMINISTRATOR = "MISSING_ADMINISTRATOR", - MISSING_ATTACH_FILES = "MISSING_ATTACH_FILES", - MISSING_BAN_MEMBERS = "MISSING_BAN_MEMBERS", - MISSING_CHANGE_NICKNAME = "MISSING_CHANGE_NICKNAME", - MISSING_CONNECT = "MISSING_CONNECT", - MISSING_CREATE_INSTANT_INVITE = "MISSING_CREATE_INSTANT_INVITE", - MISSING_DEAFEN_MEMBERS = "MISSING_DEAFEN_MEMBERS", - MISSING_EMBED_LINKS = "MISSING_EMBED_LINKS", - MISSING_INTENT_GUILD_MEMBERS = "MISSING_INTENT_GUILD_MEMBERS", - MISSING_KICK_MEMBERS = "MISSING_KICK_MEMBERS", - MISSING_MANAGE_CHANNELS = "MISSING_MANAGE_CHANNELS", - MISSING_MANAGE_EMOJIS = "MISSING_MANAGE_EMOJIS", - MISSING_MANAGE_GUILD = "MISSING_MANAGE_GUILD", - MISSING_MANAGE_MESSAGES = "MISSING_MANAGE_MESSAGES", - MISSING_MANAGE_NICKNAMES = "MISSING_MANAGE_NICKNAMES", - MISSING_MANAGE_ROLES = "MISSING_MANAGE_ROLES", - MISSING_MANAGE_WEBHOOKS = "MISSING_MANAGE_WEBHOOKS", - MISSING_MENTION_EVERYONE = "MISSING_MENTION_EVERYONE", - MISSING_MOVE_MEMBERS = "MISSING_MOVE_MEMBERS", - MISSING_MUTE_MEMBERS = "MISSING_MUTE_MEMBERS", - MISSING_PRIORITY_SPEAKER = "MISSING_PRIORITY_SPEAKER", - MISSING_READ_MESSAGE_HISTORY = "MISSING_READ_MESSAGE_HISTORY", - MISSING_SEND_MESSAGES = "MISSING_SEND_MESSAGES", - MISSING_SEND_TTS_MESSAGES = "MISSING_SEND_TTS_MESSAGES", - MISSING_SPEAK = "MISSING_SPEAK", - MISSING_STREAM = "MISSING_STREAM", - MISSING_USE_VAD = "MISSING_USE_VAD", - MISSING_USE_EXTERNAL_EMOJIS = "MISSING_USE_EXTERNAL_EMOJIS", - MISSING_VIEW_AUDIT_LOG = "MISSING_VIEW_AUDIT_LOG", - MISSING_VIEW_CHANNEL = "MISSING_VIEW_CHANNEL", - MISSING_VIEW_GUILD_INSIGHTS = "MISSING_VIEW_GUILD_INSIGHTS", - // User Errors - NICKNAMES_MAX_LENGTH = "NICKNAMES_MAX_LENGTH", - USERNAME_INVALID_CHARACTER = "USERNAME_INVALID_CHARACTER", - USERNAME_INVALID_USERNAME = "USERNAME_INVALID_USERNAME", - USERNAME_MAX_LENGTH = "USERNAME_MAX_LENGTH", - USERNAME_MIN_LENGTH = "USERNAME_MIN_LENGTH", - NONCE_TOO_LONG = "NONCE_TOO_LONG", - INVITE_MAX_AGE_INVALID = "INVITE_MAX_AGE_INVALID", - INVITE_MAX_USES_INVALID = "INVITE_MAX_USES_INVALID", - // API Errors - RATE_LIMIT_RETRY_MAXED = "RATE_LIMIT_RETRY_MAXED", - REQUEST_CLIENT_ERROR = "REQUEST_CLIENT_ERROR", - REQUEST_SERVER_ERROR = "REQUEST_SERVER_ERROR", - REQUEST_UNKNOWN_ERROR = "REQUEST_UNKNOWN_ERROR", - // Component Errors - TOO_MANY_COMPONENTS = "TOO_MANY_COMPONENTS", - TOO_MANY_ACTION_ROWS = "TOO_MANY_ACTION_ROWS", - LINK_BUTTON_CANNOT_HAVE_CUSTOM_ID = "LINK_BUTTON_CANNOT_HAVE_CUSTOM_ID", - COMPONENT_LABEL_TOO_BIG = "COMPONENT_LABEL_TOO_BIG", - COMPONENT_CUSTOM_ID_TOO_BIG = "COMPONENT_CUSTOM_ID_TOO_BIG", - BUTTON_REQUIRES_CUSTOM_ID = "BUTTON_REQUIRES_CUSTOM_ID", - COMPONENT_SELECT_MUST_BE_ALONE = "COMPONENT_SELECT_MUST_BE_ALONE", - COMPONENT_PLACEHOLDER_TOO_BIG = "COMPONENT_PLACEHOLDER_TOO_BIG", - COMPONENT_SELECT_MIN_VALUE_TOO_LOW = "COMPONENT_SELECT_MIN_VALUE_TOO_LOW", - COMPONENT_SELECT_MIN_VALUE_TOO_MANY = "COMPONENT_SELECT_MIN_VALUE_TOO_MANY", - COMPONENT_SELECT_MAX_VALUE_TOO_LOW = "COMPONENT_SELECT_MAX_VALUE_TOO_LOW", - COMPONENT_SELECT_MAX_VALUE_TOO_MANY = "COMPONENT_SELECT_MAX_VALUE_TOO_MANY", - COMPONENT_SELECT_OPTIONS_TOO_LOW = "COMPONENT_SELECT_OPTIONS_TOO_LOW", - COMPONENT_SELECT_OPTIONS_TOO_MANY = "COMPONENT_SELECT_OPTIONS_TOO_MANY", - SELECT_OPTION_LABEL_TOO_BIG = "SELECT_OPTION_LABEL_TOO_BIG", - SELECT_OPTION_VALUE_TOO_BIG = "SELECT_OPTION_VALUE_TOO_BIG", - SELECT_OPTION_TOO_MANY_DEFAULTS = "SELECT_OPTION_TOO_MANY_DEFAULTS", - COMPONENT_SELECT_MIN_HIGHER_THAN_MAX = "COMPONENT_SELECT_MIN_HIGHER_THAN_MAX", - CANNOT_ADD_USER_TO_ARCHIVED_THREADS = "CANNOT_ADD_USER_TO_ARCHIVED_THREADS", - CANNOT_LEAVE_ARCHIVED_THREAD = "CANNOT_LEAVE_ARCHIVED_THREAD", - CANNOT_REMOVE_FROM_ARCHIVED_THREAD = "CANNOT_REMOVE_FROM_ARCHIVED_THREAD", - YOU_CAN_NOT_DM_THE_BOT_ITSELF = "YOU_CAN_NOT_DM_THE_BOT_ITSELF", + // Bot Role errors + BOTS_HIGHEST_ROLE_TOO_LOW = "BOTS_HIGHEST_ROLE_TOO_LOW", + // Channel Errors + CHANNEL_NOT_FOUND = "CHANNEL_NOT_FOUND", + CHANNEL_NOT_IN_GUILD = "CHANNEL_NOT_IN_GUILD", + CHANNEL_NOT_TEXT_BASED = "CHANNEL_NOT_TEXT_BASED", + CHANNEL_NOT_STAGE_VOICE = "CHANNEL_NOT_STAGE_VOICE", + MESSAGE_MAX_LENGTH = "MESSAGE_MAX_LENGTH", + RULES_CHANNEL_CANNOT_BE_DELETED = "RULES_CHANNEL_CANNOT_BE_DELETED", + UPDATES_CHANNEL_CANNOT_BE_DELETED = "UPDATES_CHANNEL_CANNOT_BE_DELETED", + INVALID_TOPIC_LENGTH = "INVALID_TOPIC_LENGTH", + // Guild Errors + GUILD_NOT_DISCOVERABLE = "GUILD_NOT_DISCOVERABLE", + GUILD_WIDGET_NOT_ENABLED = "GUILD_WIDGET_NOT_ENABLED", + GUILD_NOT_FOUND = "GUILD_NOT_FOUND", + MEMBER_NOT_FOUND = "MEMBER_NOT_FOUND", + MEMBER_NOT_IN_VOICE_CHANNEL = "MEMBER_NOT_IN_VOICE_CHANNEL", + MEMBER_SEARCH_LIMIT_TOO_HIGH = "MEMBER_SEARCH_LIMIT_TOO_HIGH", + MEMBER_SEARCH_LIMIT_TOO_LOW = "MEMBER_SEARCH_LIMIT_TOO_LOW", + PRUNE_MAX_DAYS = "PRUNE_MAX_DAYS", + ROLE_NOT_FOUND = "ROLE_NOT_FOUND", + // Thread errors + INVALID_THREAD_PARENT_CHANNEL_TYPE = "INVALID_THREAD_PARENT_CHANNEL_TYPE", + GUILD_NEWS_CHANNEL_ONLY_SUPPORT_PUBLIC_THREADS = "GUILD_NEWS_CHANNEL_ONLY_SUPPORT_PUBLIC_THREADS", + NOT_A_THREAD_CHANNEL = "NOT_A_THREAD_CHANNEL", + MISSING_MANAGE_THREADS_AND_NOT_MEMBER = "MISSING_MANAGE_THREADS_AND_NOT_MEMBER", + CANNOT_GET_MEMBERS_OF_AN_UNJOINED_PRIVATE_THREAD = "CANNOT_GET_MEMBERS_OF_AN_UNJOINED_PRIVATE_THREAD", + HAVE_TO_BE_THE_CREATOR_OF_THE_THREAD_OR_HAVE_MANAGE_THREADS_TO_REMOVE_MEMBERS = + "HAVE_TO_BE_THE_CREATOR_OF_THE_THREAD_OR_HAVE_MANAGE_THREADS_TO_REMOVE_MEMBERS", + // Message Get Errors + INVALID_GET_MESSAGES_LIMIT = "INVALID_GET_MESSAGES_LIMIT", + // Message Delete Errors + DELETE_MESSAGES_MIN = "DELETE_MESSAGES_MIN", + PRUNE_MIN_DAYS = "PRUNE_MIN_DAYS", + // Interaction Errors + INVALID_SLASH_DESCRIPTION = "INVALID_SLASH_DESCRIPTION", + INVALID_SLASH_NAME = "INVALID_SLASH_NAME", + INVALID_SLASH_OPTIONS = "INVALID_SLASH_OPTIONS", + INVALID_SLASH_OPTIONS_CHOICES = "INVALID_SLASH_OPTIONS_CHOICES", + TOO_MANY_SLASH_OPTIONS = "TOO_MANY_SLASH_OPTIONS", + INVALID_SLASH_OPTION_CHOICE_NAME = "INVALID_SLASH_OPTION_CHOICE_NAME", + INVALID_SLASH_OPTIONS_CHOICE_VALUE_TYPE = "INVALID_SLASH_OPTIONS_CHOICE_VALUE_TYPE", + TOO_MANY_SLASH_OPTION_CHOICES = "TOO_MANY_SLASH_OPTION_CHOICES", + ONLY_STRING_OR_INTEGER_OPTIONS_CAN_HAVE_CHOICES = "ONLY_STRING_OR_INTEGER_OPTIONS_CAN_HAVE_CHOICES", + INVALID_SLASH_OPTION_NAME = "INVALID_SLASH_OPTION_NAME", + INVALID_SLASH_OPTION_DESCRIPTION = "INVALID_SLASH_OPTION_DESCRIPTION", + INVALID_CONTEXT_MENU_COMMAND_NAME = "INVALID_CONTEXT_MENU_COMMAND_NAME", + INVALID_CONTEXT_MENU_COMMAND_DESCRIPTION = "INVALID_CONTEXT_MENU_COMMAND_DESCRIPTION", + // Webhook Errors + INVALID_WEBHOOK_NAME = "INVALID_WEBHOOK_NAME", + INVALID_WEBHOOK_OPTIONS = "INVALID_WEBHOOK_OPTIONS", + // Permission Errors + MISSING_ADD_REACTIONS = "MISSING_ADD_REACTIONS", + MISSING_ADMINISTRATOR = "MISSING_ADMINISTRATOR", + MISSING_ATTACH_FILES = "MISSING_ATTACH_FILES", + MISSING_BAN_MEMBERS = "MISSING_BAN_MEMBERS", + MISSING_CHANGE_NICKNAME = "MISSING_CHANGE_NICKNAME", + MISSING_CONNECT = "MISSING_CONNECT", + MISSING_CREATE_INSTANT_INVITE = "MISSING_CREATE_INSTANT_INVITE", + MISSING_DEAFEN_MEMBERS = "MISSING_DEAFEN_MEMBERS", + MISSING_EMBED_LINKS = "MISSING_EMBED_LINKS", + MISSING_INTENT_GUILD_MEMBERS = "MISSING_INTENT_GUILD_MEMBERS", + MISSING_KICK_MEMBERS = "MISSING_KICK_MEMBERS", + MISSING_MANAGE_CHANNELS = "MISSING_MANAGE_CHANNELS", + MISSING_MANAGE_EMOJIS = "MISSING_MANAGE_EMOJIS", + MISSING_MANAGE_GUILD = "MISSING_MANAGE_GUILD", + MISSING_MANAGE_MESSAGES = "MISSING_MANAGE_MESSAGES", + MISSING_MANAGE_NICKNAMES = "MISSING_MANAGE_NICKNAMES", + MISSING_MANAGE_ROLES = "MISSING_MANAGE_ROLES", + MISSING_MANAGE_WEBHOOKS = "MISSING_MANAGE_WEBHOOKS", + MISSING_MENTION_EVERYONE = "MISSING_MENTION_EVERYONE", + MISSING_MOVE_MEMBERS = "MISSING_MOVE_MEMBERS", + MISSING_MUTE_MEMBERS = "MISSING_MUTE_MEMBERS", + MISSING_PRIORITY_SPEAKER = "MISSING_PRIORITY_SPEAKER", + MISSING_READ_MESSAGE_HISTORY = "MISSING_READ_MESSAGE_HISTORY", + MISSING_SEND_MESSAGES = "MISSING_SEND_MESSAGES", + MISSING_SEND_TTS_MESSAGES = "MISSING_SEND_TTS_MESSAGES", + MISSING_SPEAK = "MISSING_SPEAK", + MISSING_STREAM = "MISSING_STREAM", + MISSING_USE_VAD = "MISSING_USE_VAD", + MISSING_USE_EXTERNAL_EMOJIS = "MISSING_USE_EXTERNAL_EMOJIS", + MISSING_VIEW_AUDIT_LOG = "MISSING_VIEW_AUDIT_LOG", + MISSING_VIEW_CHANNEL = "MISSING_VIEW_CHANNEL", + MISSING_VIEW_GUILD_INSIGHTS = "MISSING_VIEW_GUILD_INSIGHTS", + // User Errors + NICKNAMES_MAX_LENGTH = "NICKNAMES_MAX_LENGTH", + USERNAME_INVALID_CHARACTER = "USERNAME_INVALID_CHARACTER", + USERNAME_INVALID_USERNAME = "USERNAME_INVALID_USERNAME", + USERNAME_MAX_LENGTH = "USERNAME_MAX_LENGTH", + USERNAME_MIN_LENGTH = "USERNAME_MIN_LENGTH", + NONCE_TOO_LONG = "NONCE_TOO_LONG", + INVITE_MAX_AGE_INVALID = "INVITE_MAX_AGE_INVALID", + INVITE_MAX_USES_INVALID = "INVITE_MAX_USES_INVALID", + // API Errors + RATE_LIMIT_RETRY_MAXED = "RATE_LIMIT_RETRY_MAXED", + REQUEST_CLIENT_ERROR = "REQUEST_CLIENT_ERROR", + REQUEST_SERVER_ERROR = "REQUEST_SERVER_ERROR", + REQUEST_UNKNOWN_ERROR = "REQUEST_UNKNOWN_ERROR", + // Component Errors + TOO_MANY_COMPONENTS = "TOO_MANY_COMPONENTS", + TOO_MANY_ACTION_ROWS = "TOO_MANY_ACTION_ROWS", + LINK_BUTTON_CANNOT_HAVE_CUSTOM_ID = "LINK_BUTTON_CANNOT_HAVE_CUSTOM_ID", + COMPONENT_LABEL_TOO_BIG = "COMPONENT_LABEL_TOO_BIG", + COMPONENT_CUSTOM_ID_TOO_BIG = "COMPONENT_CUSTOM_ID_TOO_BIG", + BUTTON_REQUIRES_CUSTOM_ID = "BUTTON_REQUIRES_CUSTOM_ID", + COMPONENT_SELECT_MUST_BE_ALONE = "COMPONENT_SELECT_MUST_BE_ALONE", + COMPONENT_PLACEHOLDER_TOO_BIG = "COMPONENT_PLACEHOLDER_TOO_BIG", + COMPONENT_SELECT_MIN_VALUE_TOO_LOW = "COMPONENT_SELECT_MIN_VALUE_TOO_LOW", + COMPONENT_SELECT_MIN_VALUE_TOO_MANY = "COMPONENT_SELECT_MIN_VALUE_TOO_MANY", + COMPONENT_SELECT_MAX_VALUE_TOO_LOW = "COMPONENT_SELECT_MAX_VALUE_TOO_LOW", + COMPONENT_SELECT_MAX_VALUE_TOO_MANY = "COMPONENT_SELECT_MAX_VALUE_TOO_MANY", + COMPONENT_SELECT_OPTIONS_TOO_LOW = "COMPONENT_SELECT_OPTIONS_TOO_LOW", + COMPONENT_SELECT_OPTIONS_TOO_MANY = "COMPONENT_SELECT_OPTIONS_TOO_MANY", + SELECT_OPTION_LABEL_TOO_BIG = "SELECT_OPTION_LABEL_TOO_BIG", + SELECT_OPTION_VALUE_TOO_BIG = "SELECT_OPTION_VALUE_TOO_BIG", + SELECT_OPTION_TOO_MANY_DEFAULTS = "SELECT_OPTION_TOO_MANY_DEFAULTS", + COMPONENT_SELECT_MIN_HIGHER_THAN_MAX = "COMPONENT_SELECT_MIN_HIGHER_THAN_MAX", + CANNOT_ADD_USER_TO_ARCHIVED_THREADS = "CANNOT_ADD_USER_TO_ARCHIVED_THREADS", + CANNOT_LEAVE_ARCHIVED_THREAD = "CANNOT_LEAVE_ARCHIVED_THREAD", + CANNOT_REMOVE_FROM_ARCHIVED_THREAD = "CANNOT_REMOVE_FROM_ARCHIVED_THREAD", + YOU_CAN_NOT_DM_THE_BOT_ITSELF = "YOU_CAN_NOT_DM_THE_BOT_ITSELF", } export enum Locales { - Danish = "da", - German = "de", - EnglishUk = "en-GB", - EnglishUs = "en-US", - Spanish = "es-ES", - French = "fr", - Croatian = "hr", - Italian = "it", - Lithuanian = "lt", - Hungarian = "hu", - Dutch = "nl", - Norwegian = "no", - Polish = "pl", - PortugueseBrazilian = "pt-BR", - RomanianRomania = "ro", - Finnish = "fi", - Swedish = "sv-SE", - Vietnamese = "vi", - Turkish = "tr", - Czech = "cs", - Greek = "el", - Bulgarian = "bg", - Russian = "ru", - Ukrainian = "uk", - Hindi = "hi", - Thai = "th", - ChineseChina = "zh-CN", - Japanese = "ja", - ChineseTaiwan = "zh-TW", - Korean = "ko", + Danish = "da", + German = "de", + EnglishUk = "en-GB", + EnglishUs = "en-US", + Spanish = "es-ES", + French = "fr", + Croatian = "hr", + Italian = "it", + Lithuanian = "lt", + Hungarian = "hu", + Dutch = "nl", + Norwegian = "no", + Polish = "pl", + PortugueseBrazilian = "pt-BR", + RomanianRomania = "ro", + Finnish = "fi", + Swedish = "sv-SE", + Vietnamese = "vi", + Turkish = "tr", + Czech = "cs", + Greek = "el", + Bulgarian = "bg", + Russian = "ru", + Ukrainian = "uk", + Hindi = "hi", + Thai = "th", + ChineseChina = "zh-CN", + Japanese = "ja", + ChineseTaiwan = "zh-TW", + Korean = "ko", } export type Localization = Partial>; export interface FileContent { - /** The file blob */ - blob: Blob; - /** The name of the file */ - name: string; + /** The file blob */ + blob: Blob; + /** The name of the file */ + name: string; } export interface GatewayBot { - /** The WSS URL that can be used for connecting to the gateway */ - url: string; - /** The recommended number of shards to use when connecting */ - shards: number; - /** Information on the current session start limit */ - sessionStartLimit: { - /** The total number of session starts the current user is allowed */ - total: number; - /** The remaining number of session starts the current user is allowed */ - remaining: number; - /** The number of milliseconds after which the limit resets */ - resetAfter: number; - /** The number of identify requests allowed per 5 seconds */ - maxConcurrency: number; - }; + /** The WSS URL that can be used for connecting to the gateway */ + url: string; + /** The recommended number of shards to use when connecting */ + shards: number; + /** Information on the current session start limit */ + sessionStartLimit: { + /** The total number of session starts the current user is allowed */ + total: number; + /** The remaining number of session starts the current user is allowed */ + remaining: number; + /** The number of milliseconds after which the limit resets */ + resetAfter: number; + /** The number of identify requests allowed per 5 seconds */ + maxConcurrency: number; + }; } // UTILS @@ -1234,24 +1234,24 @@ export type MakeRequired = T & { [P in K]-?: T[P] }; // THANK YOU YUI FOR SHARING THIS! export type CamelCase = S extends `${infer P1}_${infer P2}${infer P3}` - ? `${Lowercase}${Uppercase}${CamelCase}` - : Lowercase; + ? `${Lowercase}${Uppercase}${CamelCase}` + : Lowercase; export type Camelize = { - [K in keyof T as CamelCase]: T[K] extends Array ? U extends {} ? Array> - : T[K] - : T[K] extends {} ? Camelize - : never; + [K in keyof T as CamelCase]: T[K] extends Array ? U extends {} ? Array> + : T[K] + : T[K] extends {} ? Camelize + : never; }; /** Non object primitives */ export type Primitive = - | string - | number - | symbol - | bigint - | boolean - | undefined - | null; + | string + | number + | symbol + | bigint + | boolean + | undefined + | null; // | object <- don't make object a primitive /** @@ -1261,12 +1261,12 @@ export type Primitive = * export const o: object = []; // no error */ export type ObjectLiteral = { - [K in PropertyKey]: T; + [K in PropertyKey]: T; }; /** Array with no utilty methods, aka Object.create(null) */ export type ArrayWithNoPrototype = { - [index: number]: T | ArrayWithNoPrototype; + [index: number]: T | ArrayWithNoPrototype; }; /** @@ -1276,40 +1276,40 @@ export type ArrayWithNoPrototype = { * export type RequestData = Record>; */ export type AnythingBut = Exclude< - | Primitive - | { - [K in PropertyKey]: AnythingBut; - } - | ArrayWithNoPrototype< | Primitive | { - [K in PropertyKey]: AnythingBut; + [K in PropertyKey]: AnythingBut; } - >, - T + | ArrayWithNoPrototype< + | Primitive + | { + [K in PropertyKey]: AnythingBut; + } + >, + T >; /** * object identity type */ export type Id = T extends infer U ? { - [K in keyof U]: U[K]; + [K in keyof U]: U[K]; } - : never; + : never; export type KeysWithUndefined = { - [K in keyof T]-?: undefined extends T[K] ? K - : null extends T[K] ? K - : never; + [K in keyof T]-?: undefined extends T[K] ? K + : null extends T[K] ? K + : never; }[keyof T]; type OptionalizeAux = Id< - & { - [K in KeysWithUndefined]?: Optionalize; - } - & { - [K in Exclude>]: T[K] extends ObjectLiteral ? Optionalize : T[K]; - } + & { + [K in KeysWithUndefined]?: Optionalize; + } + & { + [K in Exclude>]: T[K] extends ObjectLiteral ? Optionalize : T[K]; + } >; /** @@ -1317,16 +1317,17 @@ type OptionalizeAux = Id< * it is recursive */ export type Optionalize = T extends object - ? T extends Array ? number extends T["length"] ? T[number] extends object ? Array> - : T - : Partial - : OptionalizeAux - : T; + ? T extends Array + ? number extends T["length"] ? T[number] extends object ? Array> + : T + : Partial + : OptionalizeAux + : T; export type PickPartial = - & { - [P in keyof T]?: T[P] | undefined; - } - & { [P in K]: T[P] }; + & { + [P in keyof T]?: T[P] | undefined; + } + & { [P in K]: T[P] }; export type OmitFirstFnArg = F extends (x: any, ...args: infer P) => infer R ? (...args: P) => R : never; diff --git a/vendor/util/bucket.ts b/vendor/util/bucket.ts index d968352..00eb60b 100644 --- a/vendor/util/bucket.ts +++ b/vendor/util/bucket.ts @@ -8,168 +8,168 @@ import { delay } from "./delay.ts"; * NOTE: This bucket is lazy, means it only updates when a related method is called. */ export interface LeakyBucket { - // ---------- - // PROPERTIES - // ---------- + // ---------- + // PROPERTIES + // ---------- - /** How many tokens this bucket can hold. */ - max: number; - /** Amount of tokens gained per interval. - * If bigger than `max` it will be pressed to `max`. - */ - refillAmount: number; - /** Interval at which the bucket gains tokens. */ - refillInterval: number; + /** How many tokens this bucket can hold. */ + max: number; + /** Amount of tokens gained per interval. + * If bigger than `max` it will be pressed to `max`. + */ + refillAmount: number; + /** Interval at which the bucket gains tokens. */ + refillInterval: number; - // ---------- - // METHODS - // ---------- + // ---------- + // METHODS + // ---------- - /** Acquire tokens from the bucket. - * Resolves when the tokens are acquired and available. - * @param {boolean} [highPriority=false] Whether this acquire is should be done asap. - */ - acquire(amount: number, highPriority?: boolean): Promise; + /** Acquire tokens from the bucket. + * Resolves when the tokens are acquired and available. + * @param {boolean} [highPriority=false] Whether this acquire is should be done asap. + */ + acquire(amount: number, highPriority?: boolean): Promise; - /** Returns the number of milliseconds until the next refill. */ - nextRefill(): number; + /** Returns the number of milliseconds until the next refill. */ + nextRefill(): number; - /** Current tokens in the bucket. */ - tokens(): number; + /** Current tokens in the bucket. */ + tokens(): number; - // ---------- - // INTERNAL STATES - // ---------- + // ---------- + // INTERNAL STATES + // ---------- - /** @private Internal track of when the last refill of tokens was. - * DO NOT TOUCH THIS! Unless you know what you are doing ofc :P - */ - lastRefill: number; + /** @private Internal track of when the last refill of tokens was. + * DO NOT TOUCH THIS! Unless you know what you are doing ofc :P + */ + lastRefill: number; - /** @private Internal state of whether currently it is allowed to acquire tokens. - * DO NOT TOUCH THIS! Unless you know what you are doing ofc :P - */ - allowAcquire: boolean; + /** @private Internal state of whether currently it is allowed to acquire tokens. + * DO NOT TOUCH THIS! Unless you know what you are doing ofc :P + */ + allowAcquire: boolean; - /** @private Internal number of currently available tokens. - * DO NOT TOUCH THIS! Unless you know what you are doing ofc :P - */ - tokensState: number; + /** @private Internal number of currently available tokens. + * DO NOT TOUCH THIS! Unless you know what you are doing ofc :P + */ + tokensState: number; - /** @private Internal array of promises necessary to guarantee no race conditions. - * DO NOT TOUCH THIS! Unless you know what you are doing ofc :P - */ - waiting: ((_?: unknown) => void)[]; + /** @private Internal array of promises necessary to guarantee no race conditions. + * DO NOT TOUCH THIS! Unless you know what you are doing ofc :P + */ + waiting: ((_?: unknown) => void)[]; } export function createLeakyBucket( - { max, refillInterval, refillAmount, tokens, waiting, ...rest }: - & Omit< - PickPartial< - LeakyBucket, - "max" | "refillInterval" | "refillAmount" - >, - "tokens" - > - & { - /** Current tokens in the bucket. - * @default max - */ - tokens?: number; - }, + { max, refillInterval, refillAmount, tokens, waiting, ...rest }: + & Omit< + PickPartial< + LeakyBucket, + "max" | "refillInterval" | "refillAmount" + >, + "tokens" + > + & { + /** Current tokens in the bucket. + * @default max + */ + tokens?: number; + }, ): LeakyBucket { - return { - max, - refillInterval, - refillAmount: refillAmount > max ? max : refillAmount, - lastRefill: performance.now(), - allowAcquire: true, + return { + max, + refillInterval, + refillAmount: refillAmount > max ? max : refillAmount, + lastRefill: performance.now(), + allowAcquire: true, - nextRefill: function () { - return nextRefill(this); - }, + nextRefill: function () { + return nextRefill(this); + }, - tokens: function () { - return updateTokens(this); - }, + tokens: function () { + return updateTokens(this); + }, - acquire: async function (amount, highPriority) { - return await acquire(this, amount, highPriority); - }, + acquire: async function (amount, highPriority) { + return await acquire(this, amount, highPriority); + }, - tokensState: tokens ?? max, - waiting: waiting ?? [], + tokensState: tokens ?? max, + waiting: waiting ?? [], - ...rest, - }; + ...rest, + }; } /** Update the tokens of that bucket. * @returns {number} The amount of current available tokens. */ function updateTokens(bucket: LeakyBucket): number { - const timePassed = performance.now() - bucket.lastRefill; - const missedRefills = Math.floor(timePassed / bucket.refillInterval); + const timePassed = performance.now() - bucket.lastRefill; + const missedRefills = Math.floor(timePassed / bucket.refillInterval); - // The refill shall not exceed the max amount of tokens. - bucket.tokensState = Math.min(bucket.tokensState + (bucket.refillAmount * missedRefills), bucket.max); - bucket.lastRefill += bucket.refillInterval * missedRefills; + // The refill shall not exceed the max amount of tokens. + bucket.tokensState = Math.min(bucket.tokensState + (bucket.refillAmount * missedRefills), bucket.max); + bucket.lastRefill += bucket.refillInterval * missedRefills; - return bucket.tokensState; + return bucket.tokensState; } function nextRefill(bucket: LeakyBucket): number { - // Since this bucket is lazy update the tokens before calculating the next refill. - updateTokens(bucket); + // Since this bucket is lazy update the tokens before calculating the next refill. + updateTokens(bucket); - return (performance.now() - bucket.lastRefill) + bucket.refillInterval; + return (performance.now() - bucket.lastRefill) + bucket.refillInterval; } async function acquire(bucket: LeakyBucket, amount: number, highPriority = false): Promise { - // To prevent the race condition of 2 acquires happening at once, - // check whether its currently allowed to acquire. - if (!bucket.allowAcquire) { - // create, push, and wait until the current running acquiring is finished. - await new Promise((resolve) => { - if (highPriority) { - bucket.waiting.unshift(resolve); - } else { - bucket.waiting.push(resolve); - } - }); - - // Somehow another acquire has started, - // so need to wait again. + // To prevent the race condition of 2 acquires happening at once, + // check whether its currently allowed to acquire. if (!bucket.allowAcquire) { - return await acquire(bucket, amount); + // create, push, and wait until the current running acquiring is finished. + await new Promise((resolve) => { + if (highPriority) { + bucket.waiting.unshift(resolve); + } else { + bucket.waiting.push(resolve); + } + }); + + // Somehow another acquire has started, + // so need to wait again. + if (!bucket.allowAcquire) { + return await acquire(bucket, amount); + } } - } - bucket.allowAcquire = false; - // Since the bucket is lazy update the tokens now, - // and also get the current amount of available tokens - let currentTokens = updateTokens(bucket); + bucket.allowAcquire = false; + // Since the bucket is lazy update the tokens now, + // and also get the current amount of available tokens + let currentTokens = updateTokens(bucket); - // It's possible that more than available tokens have been acquired, - // so calculate the amount of milliseconds to wait until this acquire is good to go. - if (currentTokens < amount) { - const tokensNeeded = amount - currentTokens; - let refillsNeeded = Math.ceil(tokensNeeded / bucket.refillAmount); + // It's possible that more than available tokens have been acquired, + // so calculate the amount of milliseconds to wait until this acquire is good to go. + if (currentTokens < amount) { + const tokensNeeded = amount - currentTokens; + let refillsNeeded = Math.ceil(tokensNeeded / bucket.refillAmount); - const waitTime = bucket.refillInterval * refillsNeeded; - await delay(waitTime); + const waitTime = bucket.refillInterval * refillsNeeded; + await delay(waitTime); - // Update the tokens again to ensure nothing has been missed. - updateTokens(bucket); - } + // Update the tokens again to ensure nothing has been missed. + updateTokens(bucket); + } - // In order to not subtract too much from the tokens, - // calculate what is actually needed to subtract. - const toSubtract = (amount % bucket.refillAmount) || amount; - bucket.tokensState -= toSubtract; + // In order to not subtract too much from the tokens, + // calculate what is actually needed to subtract. + const toSubtract = (amount % bucket.refillAmount) || amount; + bucket.tokensState -= toSubtract; - // Allow the next acquire to happen. - bucket.allowAcquire = true; - // If there is an acquire waiting, let it continue. - bucket.waiting.shift()?.(); + // Allow the next acquire to happen. + bucket.allowAcquire = true; + // If there is an acquire waiting, let it continue. + bucket.waiting.shift()?.(); } diff --git a/vendor/util/collection.ts b/vendor/util/collection.ts index 3d161c6..16193b8 100644 --- a/vendor/util/collection.ts +++ b/vendor/util/collection.ts @@ -1,100 +1,100 @@ export class Collection extends Map { - maxSize: number | undefined; + maxSize: number | undefined; - constructor(entries?: (readonly (readonly [K, V])[] | null) | Map, options?: CollectionOptions) { - super(entries ?? []); + constructor(entries?: (readonly (readonly [K, V])[] | null) | Map, options?: CollectionOptions) { + super(entries ?? []); - this.maxSize = options?.maxSize; - } - - set(key: K, value: V) { - // When this collection is maxSized make sure we can add first - if ((this.maxSize || this.maxSize === 0) && this.size >= this.maxSize) { - return this; + this.maxSize = options?.maxSize; } - return super.set(key, value); - } + set(key: K, value: V) { + // When this collection is maxSized make sure we can add first + if ((this.maxSize || this.maxSize === 0) && this.size >= this.maxSize) { + return this; + } - forceSet(key: K, value: V) { - return super.set(key, value); - } - - array() { - return [...this.values()]; - } - - /** Retrieve the value of the first element in this collection */ - first(): V | undefined { - return this.values().next().value; - } - - last(): V | undefined { - return [...this.values()][this.size - 1]; - } - - random(): V | undefined { - const array = [...this.values()]; - return array[Math.floor(Math.random() * array.length)]; - } - - find(callback: (value: V, key: K) => boolean) { - for (const key of this.keys()) { - const value = this.get(key)!; - if (callback(value, key)) return value; - } - // If nothing matched - return; - } - - filter(callback: (value: V, key: K) => boolean) { - const relevant = new Collection(); - this.forEach((value, key) => { - if (callback(value, key)) relevant.set(key, value); - }); - - return relevant; - } - - map(callback: (value: V, key: K) => T) { - const results = []; - for (const key of this.keys()) { - const value = this.get(key)!; - results.push(callback(value, key)); - } - return results; - } - - some(callback: (value: V, key: K) => boolean) { - for (const key of this.keys()) { - const value = this.get(key)!; - if (callback(value, key)) return true; + return super.set(key, value); } - return false; - } - - every(callback: (value: V, key: K) => boolean) { - for (const key of this.keys()) { - const value = this.get(key)!; - if (!callback(value, key)) return false; + forceSet(key: K, value: V) { + return super.set(key, value); } - return true; - } - - reduce(callback: (accumulator: T, value: V, key: K) => T, initialValue?: T): T { - let accumulator: T = initialValue!; - - for (const key of this.keys()) { - const value = this.get(key)!; - accumulator = callback(accumulator, value, key); + array() { + return [...this.values()]; } - return accumulator; - } + /** Retrieve the value of the first element in this collection */ + first(): V | undefined { + return this.values().next().value; + } + + last(): V | undefined { + return [...this.values()][this.size - 1]; + } + + random(): V | undefined { + const array = [...this.values()]; + return array[Math.floor(Math.random() * array.length)]; + } + + find(callback: (value: V, key: K) => boolean) { + for (const key of this.keys()) { + const value = this.get(key)!; + if (callback(value, key)) return value; + } + // If nothing matched + return; + } + + filter(callback: (value: V, key: K) => boolean) { + const relevant = new Collection(); + this.forEach((value, key) => { + if (callback(value, key)) relevant.set(key, value); + }); + + return relevant; + } + + map(callback: (value: V, key: K) => T) { + const results = []; + for (const key of this.keys()) { + const value = this.get(key)!; + results.push(callback(value, key)); + } + return results; + } + + some(callback: (value: V, key: K) => boolean) { + for (const key of this.keys()) { + const value = this.get(key)!; + if (callback(value, key)) return true; + } + + return false; + } + + every(callback: (value: V, key: K) => boolean) { + for (const key of this.keys()) { + const value = this.get(key)!; + if (!callback(value, key)) return false; + } + + return true; + } + + reduce(callback: (accumulator: T, value: V, key: K) => T, initialValue?: T): T { + let accumulator: T = initialValue!; + + for (const key of this.keys()) { + const value = this.get(key)!; + accumulator = callback(accumulator, value, key); + } + + return accumulator; + } } export interface CollectionOptions { - maxSize?: number; + maxSize?: number; } diff --git a/vendor/util/constants.ts b/vendor/util/constants.ts index 2bece21..6516158 100644 --- a/vendor/util/constants.ts +++ b/vendor/util/constants.ts @@ -16,8 +16,8 @@ export const IMAGE_BASE_URL = "https://cdn.discordapp.com"; // This can be modified by big brain bots and use a proxy export const baseEndpoints = { - BASE_URL: `${BASE_URL}/v${API_VERSION}`, - CDN_URL: IMAGE_BASE_URL, + BASE_URL: `${BASE_URL}/v${API_VERSION}`, + CDN_URL: IMAGE_BASE_URL, }; export const SLASH_COMMANDS_NAME_REGEX = /^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$/u; diff --git a/vendor/util/delay.ts b/vendor/util/delay.ts index 19f2882..5eb17c8 100644 --- a/vendor/util/delay.ts +++ b/vendor/util/delay.ts @@ -1,8 +1,8 @@ /** Pause the execution for a given amount of milliseconds. */ export function delay(ms: number): Promise { - return new Promise((res): number => - setTimeout((): void => { - res(); - }, ms) - ); + return new Promise((res): number => + setTimeout((): void => { + res(); + }, ms) + ); } diff --git a/vendor/util/token.ts b/vendor/util/token.ts index 795455e..5b758fc 100644 --- a/vendor/util/token.ts +++ b/vendor/util/token.ts @@ -1,14 +1,14 @@ /** Removes the Bot before the token. */ export function removeTokenPrefix(token?: string, type: "GATEWAY" | "REST" = "REST"): string { - // If no token is provided, throw an error - if (!token) throw new Error(`The ${type} was not given a token. Please provide a token and try again.`); - // If the token does not have a prefix just return token - if (!token.startsWith("Bot ")) return token; - // Remove the prefix and return only the token. - return token.substring(token.indexOf(" ") + 1); + // If no token is provided, throw an error + if (!token) throw new Error(`The ${type} was not given a token. Please provide a token and try again.`); + // If the token does not have a prefix just return token + if (!token.startsWith("Bot ")) return token; + // Remove the prefix and return only the token. + return token.substring(token.indexOf(" ") + 1); } /** Get the bot id from the bot token. WARNING: Discord staff has mentioned this may not be stable forever. Use at your own risk. However, note for over 5 years this has never broken. */ export function getBotIdFromToken(token: string) { - return BigInt(atob(token.split(".")[0])); + return BigInt(atob(token.split(".")[0])); }