fix: ID > Id (#754)

* Update bot.ts

* id id
This commit is contained in:
ITOH
2021-04-04 19:51:01 +02:00
committed by GitHub
parent 57540567a5
commit 2e397325f5
20 changed files with 148 additions and 148 deletions
+8 -8
View File
@@ -5,8 +5,8 @@ import { baseEndpoints, GATEWAY_VERSION } from "./util/constants.ts";
export let authorization = ""; export let authorization = "";
export let secretKey = ""; export let secretKey = "";
export let botID = ""; export let botId = "";
export let applicationID = ""; export let applicationId = "";
export let eventHandlers: EventHandlers = {}; export let eventHandlers: EventHandlers = {};
@@ -109,10 +109,10 @@ export async function startBigBrainBot(data: BigBrainBotConfig) {
await spawnShards( await spawnShards(
botGatewayData, botGatewayData,
identifyPayload, identifyPayload,
data.firstShardID, data.firstShardId,
data.lastShardID || data.lastShardId ||
(botGatewayData.shards >= 25 (botGatewayData.shards >= 25
? (data.firstShardID + 25) ? (data.firstShardId + 25)
: botGatewayData.shards), : botGatewayData.shards),
); );
} }
@@ -127,9 +127,9 @@ export interface BotConfig {
export interface BigBrainBotConfig extends BotConfig { export interface BigBrainBotConfig extends BotConfig {
/** The first shard to start at for this worker. Use this to control which shards to run in each worker. */ /** The first shard to start at for this worker. Use this to control which shards to run in each worker. */
firstShardID: number; firstShardId: number;
/** The last shard to start for this worker. By default it will be 25 + the firstShardID. */ /** The last shard to start for this worker. By default it will be 25 + the firstShardId. */
lastShardID?: number; lastShardId?: number;
/** This can be used to forward the ws handling to a proxy. It will disable the sharding done by the bot side. */ /** This can be used to forward the ws handling to a proxy. It will disable the sharding done by the bot side. */
wsPort?: number; wsPort?: number;
/** This can be used to forward the REST handling to a proxy. */ /** This can be used to forward the REST handling to a proxy. */
+3 -3
View File
@@ -240,13 +240,13 @@ export interface GuildStruct extends
/** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */ /** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */
auditLogs(options: GetGuildAuditLog): ReturnType<typeof getAuditLogs>; auditLogs(options: GetGuildAuditLog): ReturnType<typeof getAuditLogs>;
/** Returns a ban object for the given user or a 404 not found if the ban cannot be found. Requires the BAN_MEMBERS permission. */ /** Returns a ban object for the given user or a 404 not found if the ban cannot be found. Requires the BAN_MEMBERS permission. */
getBan(memberID: string): ReturnType<typeof getBan>; getBan(memberId: string): ReturnType<typeof getBan>;
/** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */ /** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */
bans(): ReturnType<typeof getBans>; bans(): ReturnType<typeof getBans>;
/** Ban a user from the guild and optionally delete previous messages sent by the user. Requires the BAN_MEMBERS permission. */ /** Ban a user from the guild and optionally delete previous messages sent by the user. Requires the BAN_MEMBERS permission. */
ban(memberID: string, options: CreateGuildBan): ReturnType<typeof banMember>; ban(memberId: string, options: CreateGuildBan): ReturnType<typeof banMember>;
/** Remove the ban for a user. Requires BAN_MEMBERS permission */ /** Remove the ban for a user. Requires BAN_MEMBERS permission */
unban(memberID: string): ReturnType<typeof unbanMember>; unban(memberId: string): ReturnType<typeof unbanMember>;
/** Get all the invites for this guild. Requires MANAGE_GUILD permission */ /** Get all the invites for this guild. Requires MANAGE_GUILD permission */
invites(): ReturnType<typeof getInvites>; invites(): ReturnType<typeof getInvites>;
} }
+1 -1
View File
@@ -1,6 +1,6 @@
/** https://discord.com/developers/docs/topics/opcodes-and-status-codes#rpc */ /** https://discord.com/developers/docs/topics/opcodes-and-status-codes#rpc */
export enum DiscordRpcCloseEventCodes { export enum DiscordRpcCloseEventCodes {
InvalidClientID = 4000, InvalidClientId = 4000,
InvalidOrigin, InvalidOrigin,
RateLimited, RateLimited,
TokenRevoked, TokenRevoked,
+1 -1
View File
@@ -7,7 +7,7 @@ export enum DiscordRpcErrorCodes {
InvalidEvent, InvalidEvent,
InvalidChannel, InvalidChannel,
InvalidPermissions, InvalidPermissions,
InvalidClientID, InvalidClientId,
InvalidOrigin, InvalidOrigin,
InvalidToken, InvalidToken,
InvalidUser, InvalidUser,
@@ -2,7 +2,7 @@ import { SnakeCaseProps } from "../util.ts";
import { ApplicationCommandInteractionDataOption } from "./application_command_interaction_data_option.ts"; import { ApplicationCommandInteractionDataOption } from "./application_command_interaction_data_option.ts";
export interface ApplicationCommandInteractionData { export interface ApplicationCommandInteractionData {
/** The ID of the invoked command */ /** The Id of the invoked command */
id: string; id: string;
/** The name of the invoked command */ /** The name of the invoked command */
name: string; name: string;
+12 -12
View File
@@ -67,10 +67,10 @@ startGateway({
intents: ["GUILDS", "GUILD_MESSAGES"], intents: ["GUILDS", "GUILD_MESSAGES"],
/** The max amount of shards used for identifying. This can be useful for zero-downtime updates or resharding. */ /** The max amount of shards used for identifying. This can be useful for zero-downtime updates or resharding. */
maxShards: 885, maxShards: 885,
/** The first shard ID for this group of shards. */ /** The first shard Id for this group of shards. */
firstShardID: 100, firstShardId: 100,
/** The last shard ID for this group. If none is provided, it will default to loading all shards. */ /** The last shard Id for this group. If none is provided, it will default to loading all shards. */
lastShardID: 124, lastShardId: 124,
/** The url to forward all payloads to. */ /** The url to forward all payloads to. */
url: "http://urlToYourServerHere", url: "http://urlToYourServerHere",
/** The amount of shards per cluster. By default this is 25. Use this to spread the load from shards to different CPU cores. */ /** The amount of shards per cluster. By default this is 25. Use this to spread the load from shards to different CPU cores. */
@@ -93,16 +93,16 @@ export const ws = {
reshard: true, reshard: true,
/** The percentage at which resharding should occur. */ /** The percentage at which resharding should occur. */
reshardPercentage: 80, reshardPercentage: 80,
/** The maximum shard ID number. Useful for zero-downtime updates or resharding. */ /** The maximum shard Id number. Useful for zero-downtime updates or resharding. */
maxShards: 1, maxShards: 1,
/** The amount of shards to load per cluster */ /** The amount of shards to load per cluster */
shardsPerCluster: 25, shardsPerCluster: 25,
/** The maximum amount of clusters to use for your bot. */ /** The maximum amount of clusters to use for your bot. */
maxClusters: 4, maxClusters: 4,
/** The first shard ID to start spawning. */ /** The first shard Id to start spawning. */
firstShardID: 0, firstShardId: 0,
/** The last shard ID for this cluster. */ /** The last shard Id for this cluster. */
lastShardID: 1, lastShardId: 1,
/** This prop decides whether Discord allows our next shard to be started. When 1 starts, this is set to false until it is ready for the next one. */ /** This prop decides whether Discord allows our next shard to be started. When 1 starts, this is set to false until it is ready for the next one. */
createNextShard: true, createNextShard: true,
/** The identify payload holds the necessary data to connect and stay connected with Discords WSS. */ /** The identify payload holds the necessary data to connect and stay connected with Discords WSS. */
@@ -141,7 +141,7 @@ export const ws = {
loadingShards: new Collection< loadingShards: new Collection<
number, number,
{ {
shardID: number; shardId: number;
resolve: (value: unknown) => void; resolve: (value: unknown) => void;
reject: (reason?: unknown) => void; reject: (reason?: unknown) => void;
startedAt: number; startedAt: number;
@@ -181,7 +181,7 @@ export interface DiscordenoShard {
/** The amount of milliseconds to wait between heartbeats */ /** The amount of milliseconds to wait between heartbeats */
resumeInterval: number; resumeInterval: number;
/** The session id important for resuming connections. */ /** The session id important for resuming connections. */
sessionID: string; sessionId: string;
/** The previous sequence number, important for resuming connections. */ /** The previous sequence number, important for resuming connections. */
previousSequenceNumber: number | null; previousSequenceNumber: number | null;
/** Whether the shard is currently resuming. */ /** Whether the shard is currently resuming. */
@@ -198,7 +198,7 @@ export interface DiscordenoShard {
/** The interval between heartbeats requested by discord. */ /** The interval between heartbeats requested by discord. */
interval: number; interval: number;
/** The id of the interval, useful for stopping the interval if ws closed. */ /** The id of the interval, useful for stopping the interval if ws closed. */
intervalID: number; intervalId: number;
}; };
} }
``` ```
+1 -1
View File
@@ -15,7 +15,7 @@ export async function cleanupLoadingShards() {
if (now < loadingShard.startedAt + 60000) return; if (now < loadingShard.startedAt + 60000) return;
loadingShard.reject( loadingShard.reject(
`[Identify Failure] Shard ${loadingShard.shardID} has not received READY event in over a minute.`, `[Identify Failure] Shard ${loadingShard.shardId} has not received READY event in over a minute.`,
); );
}); });
+7 -7
View File
@@ -3,18 +3,18 @@ import { resume } from "./resume.ts";
import { ws } from "./ws.ts"; import { ws } from "./ws.ts";
// deno-lint-ignore require-await // deno-lint-ignore require-await
export async function createShard(shardID: number) { export async function createShard(shardId: number) {
const socket = new WebSocket(ws.botGatewayData.url); const socket = new WebSocket(ws.botGatewayData.url);
socket.binaryType = "arraybuffer"; socket.binaryType = "arraybuffer";
socket.onerror = (errorEvent) => { socket.onerror = (errorEvent) => {
ws.log("ERROR", { shardID, error: errorEvent }); ws.log("ERROR", { shardId, error: errorEvent });
}; };
socket.onmessage = ({ data: message }) => handleOnMessage(message, shardID); socket.onmessage = ({ data: message }) => handleOnMessage(message, shardId);
socket.onclose = (event) => { socket.onclose = (event) => {
ws.log("CLOSED", { shardID, payload: event }); ws.log("CLOSED", { shardId, payload: event });
// TODO: ENUM FOR THESE CODES? // TODO: ENUM FOR THESE CODES?
switch (event.code) { switch (event.code) {
@@ -35,11 +35,11 @@ export async function createShard(shardID: number) {
case 4007: case 4007:
case 4008: case 4008:
case 4009: case 4009:
ws.log("CLOSED_RECONNECT", { shardID, payload: event }); ws.log("CLOSED_RECONNECT", { shardId, payload: event });
identify(shardID, ws.maxShards); identify(shardId, ws.maxShards);
break; break;
default: default:
resume(shardID); resume(shardId);
break; break;
} }
}; };
+12 -12
View File
@@ -3,44 +3,44 @@ import { DiscordenoShard } from "./ws.ts";
/** The handler for logging different actions happening inside the ws. User can override and put custom handling per event. */ /** The handler for logging different actions happening inside the ws. User can override and put custom handling per event. */
export function log( export function log(
type: "CLOSED", type: "CLOSED",
data: { shardID: number; payload: CloseEvent }, data: { shardId: number; payload: CloseEvent },
): unknown; ): unknown;
export function log( export function log(
type: "CLOSED_RECONNECT", type: "CLOSED_RECONNECT",
data: { shardID: number; payload: CloseEvent }, data: { shardId: number; payload: CloseEvent },
): unknown; ): unknown;
export function log( export function log(
type: "ERROR", type: "ERROR",
data: Record<string, unknown> & { shardID: number }, data: Record<string, unknown> & { shardId: number },
): unknown; ): unknown;
export function log( export function log(
type: "HEARTBEATING", type: "HEARTBEATING",
data: { shardID: number; shard: DiscordenoShard }, data: { shardId: number; shard: DiscordenoShard },
): unknown; ): unknown;
export function log( export function log(
type: "HEARTBEATING_CLOSED", type: "HEARTBEATING_CLOSED",
data: { shardID: number; shard: DiscordenoShard }, data: { shardId: number; shard: DiscordenoShard },
): unknown; ): unknown;
export function log( export function log(
type: "HEARTBEATING_DETAILS", type: "HEARTBEATING_DETAILS",
data: { shardID: number; interval: number; shard: DiscordenoShard }, data: { shardId: number; interval: number; shard: DiscordenoShard },
): unknown; ): unknown;
export function log( export function log(
type: "HEARTBEATING_STARTED", type: "HEARTBEATING_STARTED",
data: { shardID: number; interval: number }, data: { shardId: number; interval: number },
): unknown; ): unknown;
export function log( export function log(
type: "IDENTIFYING", type: "IDENTIFYING",
data: { shardID: number; maxShards: number }, data: { shardId: number; maxShards: number },
): unknown; ): unknown;
export function log( export function log(
type: "INVALID_SESSION", type: "INVALID_SESSION",
data: { shardID: number; payload: DiscordPayload }, data: { shardId: number; payload: DiscordPayload },
): unknown; ): unknown;
export function log(type: "RAW", data: Record<string, unknown>): unknown; export function log(type: "RAW", data: Record<string, unknown>): unknown;
export function log(type: "RECONNECT", data: { shardID: number }): unknown; export function log(type: "RECONNECT", data: { shardId: number }): unknown;
export function log(type: "RESUMED", data: { shardID: number }): unknown; export function log(type: "RESUMED", data: { shardId: number }): unknown;
export function log(type: "RESUMING", data: { shardID: number }): unknown; export function log(type: "RESUMING", data: { shardId: number }): unknown;
export function log( export function log(
type: type:
| "CLOSED" | "CLOSED"
+2 -2
View File
@@ -3,7 +3,7 @@ import { ws } from "./ws.ts";
/** Handler for processing all dispatch payloads that should be sent/forwarded to another server/vps/process. */ /** Handler for processing all dispatch payloads that should be sent/forwarded to another server/vps/process. */
export async function handleDiscordPayload( export async function handleDiscordPayload(
data: DiscordPayload, data: DiscordPayload,
shardID: number, shardId: number,
) { ) {
await fetch(ws.url, { await fetch(ws.url, {
headers: { headers: {
@@ -11,7 +11,7 @@ export async function handleDiscordPayload(
}, },
method: "post", method: "post",
body: JSON.stringify({ body: JSON.stringify({
shardID, shardId,
data, data,
}), }),
}).catch(console.error); }).catch(console.error);
+25 -25
View File
@@ -1,13 +1,13 @@
import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts";
import { DiscordReady } from "../types/gateway/ready.ts";
import { decompressWith } from "./deps.ts";
import { identify } from "./identify.ts"; import { identify } from "./identify.ts";
import { resume } from "./resume.ts"; import { resume } from "./resume.ts";
import { ws } from "./ws.ts"; import { ws } from "./ws.ts";
import { decompressWith } from "./deps.ts";
import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts";
import { DiscordReady } from "../types/gateway/ready.ts";
/** Handler for handling every message event from websocket. */ /** Handler for handling every message event from websocket. */
// deno-lint-ignore no-explicit-any // deno-lint-ignore no-explicit-any
export function handleOnMessage(message: any, shardID: number) { export function handleOnMessage(message: any, shardId: number) {
if (message instanceof ArrayBuffer) { if (message instanceof ArrayBuffer) {
message = new Uint8Array(message); message = new Uint8Array(message);
} }
@@ -28,69 +28,69 @@ export function handleOnMessage(message: any, shardID: number) {
switch (messageData.op) { switch (messageData.op) {
case DiscordGatewayOpcodes.Hello: case DiscordGatewayOpcodes.Hello:
ws.heartbeat( ws.heartbeat(
shardID, shardId,
(messageData.d as DiscordHeartbeat).heartbeat_interval, (messageData.d as DiscordHeartbeat).heartbeat_interval,
); );
break; break;
case DiscordGatewayOpcodes.HeartbeatACK: case DiscordGatewayOpcodes.HeartbeatACK:
if (ws.shards.has(shardID)) { if (ws.shards.has(shardId)) {
ws.shards.get(shardID)!.heartbeat.acknowledged = true; ws.shards.get(shardId)!.heartbeat.acknowledged = true;
} }
break; break;
case DiscordGatewayOpcodes.Reconnect: case DiscordGatewayOpcodes.Reconnect:
ws.log("RECONNECT", { shardID }); ws.log("RECONNECT", { shardId });
if (ws.shards.has(shardID)) { if (ws.shards.has(shardId)) {
ws.shards.get(shardID)!.resuming = true; ws.shards.get(shardId)!.resuming = true;
} }
resume(shardID); resume(shardId);
break; break;
case DiscordGatewayOpcodes.InvalidSession: case DiscordGatewayOpcodes.InvalidSession:
ws.log("INVALID_SESSION", { shardID, payload: messageData }); ws.log("INVALID_SESSION", { shardId, payload: messageData });
// When d is false we need to reidentify // When d is false we need to reidentify
if (!messageData.d) { if (!messageData.d) {
identify(shardID, ws.maxShards); identify(shardId, ws.maxShards);
break; break;
} }
if (ws.shards.has(shardID)) { if (ws.shards.has(shardId)) {
ws.shards.get(shardID)!.resuming = true; ws.shards.get(shardId)!.resuming = true;
} }
resume(shardID); resume(shardId);
break; break;
default: default:
if (messageData.t === "RESUMED") { if (messageData.t === "RESUMED") {
ws.log("RESUMED", { shardID }); ws.log("RESUMED", { shardId });
if (ws.shards.has(shardID)) { if (ws.shards.has(shardId)) {
ws.shards.get(shardID)!.resuming = false; ws.shards.get(shardId)!.resuming = false;
} }
break; break;
} }
// Important for RESUME // Important for RESUME
if (messageData.t === "READY") { if (messageData.t === "READY") {
const shard = ws.shards.get(shardID); const shard = ws.shards.get(shardId);
if (shard) { if (shard) {
shard.sessionID = (messageData.d as DiscordReady).session_id; shard.sessionId = (messageData.d as DiscordReady).session_id;
} }
ws.loadingShards.get(shardID)?.resolve(true); ws.loadingShards.get(shardId)?.resolve(true);
ws.loadingShards.delete(shardID); ws.loadingShards.delete(shardId);
} }
// Update the sequence number if it is present // Update the sequence number if it is present
if (messageData.s) { if (messageData.s) {
const shard = ws.shards.get(shardID); const shard = ws.shards.get(shardId);
if (shard) { if (shard) {
shard.previousSequenceNumber = messageData.s; shard.previousSequenceNumber = messageData.s;
} }
} }
ws.handleDiscordPayload(messageData, shardID); ws.handleDiscordPayload(messageData, shardId);
break; break;
} }
} }
+10 -10
View File
@@ -1,33 +1,33 @@
import { ws } from "./ws.ts";
import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts"; import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts";
import { ws } from "./ws.ts";
export function heartbeat(shardID: number, interval: number) { export function heartbeat(shardId: number, interval: number) {
ws.log("HEARTBEATING_STARTED", { shardID, interval }); ws.log("HEARTBEATING_STARTED", { shardId, interval });
const shard = ws.shards.get(shardID); const shard = ws.shards.get(shardId);
if (!shard) return; if (!shard) return;
ws.log("HEARTBEATING_DETAILS", { shardID, interval, shard }); ws.log("HEARTBEATING_DETAILS", { shardId, interval, shard });
shard.heartbeat.keepAlive = true; shard.heartbeat.keepAlive = true;
shard.heartbeat.acknowledged = false; shard.heartbeat.acknowledged = false;
shard.heartbeat.lastSentAt = Date.now(); shard.heartbeat.lastSentAt = Date.now();
shard.heartbeat.interval = interval; shard.heartbeat.interval = interval;
shard.heartbeat.intervalID = setInterval(() => { shard.heartbeat.intervalId = setInterval(() => {
const currentShard = ws.shards.get(shardID); const currentShard = ws.shards.get(shardId);
if (!currentShard) return; if (!currentShard) return;
ws.log("HEARTBEATING", { shardID, shard: currentShard }); ws.log("HEARTBEATING", { shardId, shard: currentShard });
if ( if (
currentShard.ws.readyState === WebSocket.CLOSED || currentShard.ws.readyState === WebSocket.CLOSED ||
!currentShard.heartbeat.keepAlive !currentShard.heartbeat.keepAlive
) { ) {
ws.log("HEARTBEATING_CLOSED", { shardID, shard: currentShard }); ws.log("HEARTBEATING_CLOSED", { shardId, shard: currentShard });
// STOP THE HEARTBEAT // STOP THE HEARTBEAT
return clearInterval(currentShard.heartbeat.intervalID); return clearInterval(currentShard.heartbeat.intervalId);
} }
currentShard.ws.send( currentShard.ws.send(
+10 -10
View File
@@ -1,18 +1,18 @@
import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts"; import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts";
import { ws } from "./ws.ts"; import { ws } from "./ws.ts";
export async function identify(shardID: number, maxShards: number) { export async function identify(shardId: number, maxShards: number) {
ws.log("IDENTIFYING", { shardID, maxShards }); ws.log("IDENTIFYING", { shardId, maxShards });
// CREATE A SHARD // CREATE A SHARD
const socket = await ws.createShard(shardID); const socket = await ws.createShard(shardId);
// Identify can just set/reset the settings for the shard // Identify can just set/reset the settings for the shard
ws.shards.set(shardID, { ws.shards.set(shardId, {
id: shardID, id: shardId,
ws: socket, ws: socket,
resumeInterval: 0, resumeInterval: 0,
sessionID: "", sessionId: "",
previousSequenceNumber: 0, previousSequenceNumber: 0,
resuming: false, resuming: false,
heartbeat: { heartbeat: {
@@ -21,7 +21,7 @@ export async function identify(shardID: number, maxShards: number) {
acknowledged: false, acknowledged: false,
keepAlive: false, keepAlive: false,
interval: 0, interval: 0,
intervalID: 0, intervalId: 0,
}, },
}); });
@@ -29,14 +29,14 @@ export async function identify(shardID: number, maxShards: number) {
socket.send( socket.send(
JSON.stringify({ JSON.stringify({
op: DiscordGatewayOpcodes.Identify, op: DiscordGatewayOpcodes.Identify,
d: { ...ws.identifyPayload, shard: [shardID, maxShards] }, d: { ...ws.identifyPayload, shard: [shardId, maxShards] },
}), }),
); );
}; };
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
ws.loadingShards.set(shardID, { ws.loadingShards.set(shardId, {
shardID, shardId,
resolve, resolve,
reject, reject,
startedAt: Date.now(), startedAt: Date.now(),
+2 -2
View File
@@ -1,5 +1,5 @@
import { ws } from "./ws.ts";
import { getGatewayBot } from "../helpers/misc/get_gateway_bot.ts"; import { getGatewayBot } from "../helpers/misc/get_gateway_bot.ts";
import { ws } from "./ws.ts";
/** The handler to automatically reshard when necessary. */ /** The handler to automatically reshard when necessary. */
export async function resharder() { export async function resharder() {
@@ -26,5 +26,5 @@ export async function resharder() {
ws.botGatewayData.shards = data.shards; ws.botGatewayData.shards = data.shards;
ws.botGatewayData.url = data.url; ws.botGatewayData.url = data.url;
ws.spawnShards(ws.firstShardID); ws.spawnShards(ws.firstShardId);
} }
+11 -11
View File
@@ -1,31 +1,31 @@
import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts"; import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts";
import { ws } from "./ws.ts"; import { ws } from "./ws.ts";
export async function resume(shardID: number) { export async function resume(shardId: number) {
ws.log("RESUMING", { shardID }); ws.log("RESUMING", { shardId });
// CREATE A SHARD // CREATE A SHARD
const socket = await ws.createShard(shardID); const socket = await ws.createShard(shardId);
// NOW WE HANDLE RESUMING THIS SHARD // NOW WE HANDLE RESUMING THIS SHARD
// Get the old data for this shard necessary for resuming // Get the old data for this shard necessary for resuming
const oldShard = ws.shards.get(shardID); const oldShard = ws.shards.get(shardId);
if (oldShard) { if (oldShard) {
// HOW TO CLOSE OLD SHARD SOCKET!!! // HOW TO CLOSE OLD SHARD SOCKET!!!
oldShard.ws.close(4009, "Resuming the shard, closing old shard."); oldShard.ws.close(4009, "Resuming the shard, closing old shard.");
// STOP OLD HEARTBEAT // STOP OLD HEARTBEAT
clearInterval(oldShard.heartbeat.intervalID); clearInterval(oldShard.heartbeat.intervalId);
} }
const sessionID = oldShard?.sessionID || ""; const sessionId = oldShard?.sessionId || "";
const previousSequenceNumber = oldShard?.previousSequenceNumber || 0; const previousSequenceNumber = oldShard?.previousSequenceNumber || 0;
ws.shards.set(shardID, { ws.shards.set(shardId, {
id: shardID, id: shardId,
ws: socket, ws: socket,
resumeInterval: 0, resumeInterval: 0,
sessionID, sessionId,
previousSequenceNumber, previousSequenceNumber,
resuming: false, resuming: false,
heartbeat: { heartbeat: {
@@ -34,7 +34,7 @@ export async function resume(shardID: number) {
acknowledged: false, acknowledged: false,
keepAlive: false, keepAlive: false,
interval: 0, interval: 0,
intervalID: 0, intervalId: 0,
}, },
}); });
@@ -45,7 +45,7 @@ export async function resume(shardID: number) {
op: DiscordGatewayOpcodes.Resume, op: DiscordGatewayOpcodes.Resume,
d: { d: {
token: ws.identifyPayload.token, token: ws.identifyPayload.token,
session_id: sessionID, session_id: sessionId,
seq: previousSequenceNumber, seq: previousSequenceNumber,
}, },
}), }),
+12 -12
View File
@@ -2,25 +2,25 @@ import { Collection } from "../util/collection.ts";
import { ws } from "./ws.ts"; import { ws } from "./ws.ts";
/** Begin spawning shards. */ /** Begin spawning shards. */
export function spawnShards(firstShardID = 0) { export function spawnShards(firstShardId = 0) {
/** Stored as bucketID: [clusterID, [ShardIDs]] */ /** Stored as bucketId: [clusterId, [ShardIds]] */
const buckets = new Collection<number, number[][]>(); const buckets = new Collection<number, number[][]>();
const maxShards = ws.maxShards || ws.botGatewayData.shards; const maxShards = ws.maxShards || ws.botGatewayData.shards;
let cluster = 0; let cluster = 0;
for ( for (
let index = firstShardID; let index = firstShardId;
index < ws.botGatewayData.sessionStartLimit.maxConcurrency; index < ws.botGatewayData.sessionStartLimit.maxConcurrency;
index++ index++
) { ) {
// ORGANIZE ALL SHARDS INTO THEIR OWN BUCKETS // ORGANIZE ALL SHARDS INTO THEIR OWN BUCKETS
for (let i = 0; i < maxShards; i++) { for (let i = 0; i < maxShards; i++) {
const bucketID = i % ws.botGatewayData.sessionStartLimit.maxConcurrency; const bucketId = i % ws.botGatewayData.sessionStartLimit.maxConcurrency;
const bucket = buckets.get(bucketID); const bucket = buckets.get(bucketId);
if (!bucket) { if (!bucket) {
// Create the bucket since it doesnt exist // Create the bucket since it doesnt exist
buckets.set(bucketID, [[cluster, i]]); buckets.set(bucketId, [[cluster, i]]);
if (cluster + 1 <= ws.maxClusters) cluster++; if (cluster + 1 <= ws.maxClusters) cluster++;
} else { } else {
@@ -39,13 +39,13 @@ export function spawnShards(firstShardID = 0) {
} }
// SPREAD THIS OUT TO DIFFERENT CLUSTERS TO BEGIN STARTING UP // SPREAD THIS OUT TO DIFFERENT CLUSTERS TO BEGIN STARTING UP
buckets.forEach(async (bucket, bucketID) => { buckets.forEach(async (bucket, bucketId) => {
for (const [clusterID, ...queue] of bucket) { for (const [clusterId, ...queue] of bucket) {
let shardID = queue.shift(); let shardId = queue.shift();
while (shardID !== undefined) { while (shardId !== undefined) {
await ws.tellClusterToIdentify(clusterID as number, shardID, bucketID); await ws.tellClusterToIdentify(clusterId as number, shardId, bucketId);
shardID = queue.shift(); shardId = queue.shift();
} }
} }
}); });
+4 -4
View File
@@ -1,5 +1,5 @@
import { StartGatewayOptions } from "./start_gateway_options.ts";
import { DiscordGatewayIntents } from "../types/gateway/gateway_intents.ts"; import { DiscordGatewayIntents } from "../types/gateway/gateway_intents.ts";
import { StartGatewayOptions } from "./start_gateway_options.ts";
import { ws } from "./ws.ts"; import { ws } from "./ws.ts";
/** ADVANCED DEVS ONLY!!!!!! /** ADVANCED DEVS ONLY!!!!!!
@@ -9,7 +9,7 @@ import { ws } from "./ws.ts";
export async function startGateway(options: StartGatewayOptions) { export async function startGateway(options: StartGatewayOptions) {
ws.identifyPayload.token = `Bot ${options.token}`; ws.identifyPayload.token = `Bot ${options.token}`;
ws.secretKey = options.secretKey; ws.secretKey = options.secretKey;
ws.firstShardID = options.firstShardID; ws.firstShardId = options.firstShardId;
ws.url = options.url; ws.url = options.url;
if (options.shardsPerCluster) ws.shardsPerCluster = options.shardsPerCluster; if (options.shardsPerCluster) ws.shardsPerCluster = options.shardsPerCluster;
if (options.maxClusters) ws.maxClusters = options.maxClusters; if (options.maxClusters) ws.maxClusters = options.maxClusters;
@@ -36,7 +36,7 @@ export async function startGateway(options: StartGatewayOptions) {
}).then((res) => res.json())) as DiscordBotGatewayData; }).then((res) => res.json())) as DiscordBotGatewayData;
ws.maxShards = options.maxShards || data.shards; ws.maxShards = options.maxShards || data.shards;
ws.lastShardID = options.lastShardID || data.shards - 1; ws.lastShardId = options.lastShardId || data.shards - 1;
// TODO: ALL THE FOLLOWING CAN BE REPLACED BY THIS 1 LINE // TODO: ALL THE FOLLOWING CAN BE REPLACED BY THIS 1 LINE
// ws.botGatewayData = snakeToCamel(await getGatewayBot()) // ws.botGatewayData = snakeToCamel(await getGatewayBot())
@@ -50,6 +50,6 @@ export async function startGateway(options: StartGatewayOptions) {
ws.botGatewayData.shards = data.shards; ws.botGatewayData.shards = data.shards;
ws.botGatewayData.url = data.url; ws.botGatewayData.url = data.url;
ws.spawnShards(ws.firstShardID); ws.spawnShards(ws.firstShardId);
ws.cleanupLoadingShards(); ws.cleanupLoadingShards();
} }
+4 -4
View File
@@ -9,10 +9,10 @@ export interface StartGatewayOptions {
intents: (DiscordGatewayIntents | keyof typeof DiscordGatewayIntents)[]; intents: (DiscordGatewayIntents | keyof typeof DiscordGatewayIntents)[];
/** The max amount of shards used for identifying. This can be useful for zero-downtime updates or resharding. */ /** The max amount of shards used for identifying. This can be useful for zero-downtime updates or resharding. */
maxShards?: number; maxShards?: number;
/** The first shard ID for this group of shards. */ /** The first shard Id for this group of shards. */
firstShardID: number; firstShardId: number;
/** The last shard ID for this group. If none is provided, it will default to loading all shards. */ /** The last shard Id for this group. If none is provided, it will default to loading all shards. */
lastShardID?: number; lastShardId?: number;
/** The url to forward all payloads to. */ /** The url to forward all payloads to. */
url: string; url: string;
/** The amount of shards per cluster. By default this is 25. Use this to spread the load from shards to different CPU cores. */ /** The amount of shards per cluster. By default this is 25. Use this to spread the load from shards to different CPU cores. */
+5 -5
View File
@@ -2,15 +2,15 @@ import { ws } from "./ws.ts";
/** Allows users to hook in and change to communicate to different clusters across different servers or anything they like. For example using redis pubsub to talk to other servers. */ /** Allows users to hook in and change to communicate to different clusters across different servers or anything they like. For example using redis pubsub to talk to other servers. */
export async function tellClusterToIdentify( export async function tellClusterToIdentify(
workerID: number, workerId: number,
shardID: number, shardId: number,
bucketID: number, bucketId: number,
) { ) {
// When resharding this may exist already // When resharding this may exist already
const oldShard = ws.shards.get(shardID); const oldShard = ws.shards.get(shardId);
// TODO: Use workers // TODO: Use workers
await ws.identify(shardID, ws.maxShards); await ws.identify(shardId, ws.maxShards);
if (oldShard) { if (oldShard) {
oldShard.ws.close(4009, "Resharded!"); oldShard.ws.close(4009, "Resharded!");
+17 -17
View File
@@ -1,15 +1,15 @@
import { Collection } from "../util/collection.ts"; import { Collection } from "../util/collection.ts";
import { log } from "./events.ts";
import { resharder } from "./resharder.ts";
import { startGateway } from "./start_gateway.ts";
import { spawnShards } from "./spawn_shards.ts";
import { createShard } from "./create_shard.ts";
import { identify } from "./identify.ts";
import { heartbeat } from "./heartbeat.ts";
import { handleDiscordPayload } from "./handle_discord_payload.ts";
import { tellClusterToIdentify } from "./tell_cluster_to_identify.ts";
import { cleanupLoadingShards } from "./cleanup_loading_shards.ts"; import { cleanupLoadingShards } from "./cleanup_loading_shards.ts";
import { createShard } from "./create_shard.ts";
import { log } from "./events.ts";
import { handleDiscordPayload } from "./handle_discord_payload.ts";
import { handleOnMessage } from "./handle_on_message.ts"; import { handleOnMessage } from "./handle_on_message.ts";
import { heartbeat } from "./heartbeat.ts";
import { identify } from "./identify.ts";
import { resharder } from "./resharder.ts";
import { spawnShards } from "./spawn_shards.ts";
import { startGateway } from "./start_gateway.ts";
import { tellClusterToIdentify } from "./tell_cluster_to_identify.ts";
// CONTROLLER LIKE INTERFACE FOR WS HANDLING // CONTROLLER LIKE INTERFACE FOR WS HANDLING
export const ws = { export const ws = {
@@ -21,16 +21,16 @@ export const ws = {
reshard: true, reshard: true,
/** The percentage at which resharding should occur. */ /** The percentage at which resharding should occur. */
reshardPercentage: 80, reshardPercentage: 80,
/** The maximum shard ID number. Useful for zero-downtime updates or resharding. */ /** The maximum shard Id number. Useful for zero-downtime updates or resharding. */
maxShards: 1, maxShards: 1,
/** The amount of shards to load per cluster */ /** The amount of shards to load per cluster */
shardsPerCluster: 25, shardsPerCluster: 25,
/** The maximum amount of clusters to use for your bot. */ /** The maximum amount of clusters to use for your bot. */
maxClusters: 4, maxClusters: 4,
/** The first shard ID to start spawning. */ /** The first shard Id to start spawning. */
firstShardID: 0, firstShardId: 0,
/** The last shard ID for this cluster. */ /** The last shard Id for this cluster. */
lastShardID: 1, lastShardId: 1,
/** This prop decides whether Discord allows our next shard to be started. When 1 starts, this is set to false until it is ready for the next one. */ /** This prop decides whether Discord allows our next shard to be started. When 1 starts, this is set to false until it is ready for the next one. */
createNextShard: true, createNextShard: true,
/** The identify payload holds the necessary data to connect and stay connected with Discords WSS. */ /** The identify payload holds the necessary data to connect and stay connected with Discords WSS. */
@@ -69,7 +69,7 @@ export const ws = {
loadingShards: new Collection< loadingShards: new Collection<
number, number,
{ {
shardID: number; shardId: number;
resolve: (value: unknown) => void; resolve: (value: unknown) => void;
reject: (reason?: unknown) => void; reject: (reason?: unknown) => void;
startedAt: number; startedAt: number;
@@ -111,7 +111,7 @@ export interface DiscordenoShard {
/** The amount of milliseconds to wait between heartbeats */ /** The amount of milliseconds to wait between heartbeats */
resumeInterval: number; resumeInterval: number;
/** The session id important for resuming connections. */ /** The session id important for resuming connections. */
sessionID: string; sessionId: string;
/** The previous sequence number, important for resuming connections. */ /** The previous sequence number, important for resuming connections. */
previousSequenceNumber: number | null; previousSequenceNumber: number | null;
/** Whether the shard is currently resuming. */ /** Whether the shard is currently resuming. */
@@ -128,6 +128,6 @@ export interface DiscordenoShard {
/** The interval between heartbeats requested by discord. */ /** The interval between heartbeats requested by discord. */
interval: number; interval: number;
/** The id of the interval, useful for stopping the interval if ws closed. */ /** The id of the interval, useful for stopping the interval if ws closed. */
intervalID: number; intervalId: number;
}; };
} }