mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-16 11:28:15 +00:00
+8
-8
@@ -5,8 +5,8 @@ import { baseEndpoints, GATEWAY_VERSION } from "./util/constants.ts";
|
||||
|
||||
export let authorization = "";
|
||||
export let secretKey = "";
|
||||
export let botID = "";
|
||||
export let applicationID = "";
|
||||
export let botId = "";
|
||||
export let applicationId = "";
|
||||
|
||||
export let eventHandlers: EventHandlers = {};
|
||||
|
||||
@@ -109,10 +109,10 @@ export async function startBigBrainBot(data: BigBrainBotConfig) {
|
||||
await spawnShards(
|
||||
botGatewayData,
|
||||
identifyPayload,
|
||||
data.firstShardID,
|
||||
data.lastShardID ||
|
||||
data.firstShardId,
|
||||
data.lastShardId ||
|
||||
(botGatewayData.shards >= 25
|
||||
? (data.firstShardID + 25)
|
||||
? (data.firstShardId + 25)
|
||||
: botGatewayData.shards),
|
||||
);
|
||||
}
|
||||
@@ -127,9 +127,9 @@ export interface 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. */
|
||||
firstShardID: number;
|
||||
/** The last shard to start for this worker. By default it will be 25 + the firstShardID. */
|
||||
lastShardID?: number;
|
||||
firstShardId: number;
|
||||
/** The last shard to start for this worker. By default it will be 25 + the firstShardId. */
|
||||
lastShardId?: number;
|
||||
/** This can be used to forward the ws handling to a proxy. It will disable the sharding done by the bot side. */
|
||||
wsPort?: number;
|
||||
/** This can be used to forward the REST handling to a proxy. */
|
||||
|
||||
@@ -240,13 +240,13 @@ export interface GuildStruct extends
|
||||
/** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */
|
||||
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. */
|
||||
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. */
|
||||
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(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 */
|
||||
unban(memberID: string): ReturnType<typeof unbanMember>;
|
||||
unban(memberId: string): ReturnType<typeof unbanMember>;
|
||||
/** Get all the invites for this guild. Requires MANAGE_GUILD permission */
|
||||
invites(): ReturnType<typeof getInvites>;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** https://discord.com/developers/docs/topics/opcodes-and-status-codes#rpc */
|
||||
export enum DiscordRpcCloseEventCodes {
|
||||
InvalidClientID = 4000,
|
||||
InvalidClientId = 4000,
|
||||
InvalidOrigin,
|
||||
RateLimited,
|
||||
TokenRevoked,
|
||||
|
||||
@@ -7,7 +7,7 @@ export enum DiscordRpcErrorCodes {
|
||||
InvalidEvent,
|
||||
InvalidChannel,
|
||||
InvalidPermissions,
|
||||
InvalidClientID,
|
||||
InvalidClientId,
|
||||
InvalidOrigin,
|
||||
InvalidToken,
|
||||
InvalidUser,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { SnakeCaseProps } from "../util.ts";
|
||||
import { ApplicationCommandInteractionDataOption } from "./application_command_interaction_data_option.ts";
|
||||
|
||||
export interface ApplicationCommandInteractionData {
|
||||
/** The ID of the invoked command */
|
||||
/** The Id of the invoked command */
|
||||
id: string;
|
||||
/** The name of the invoked command */
|
||||
name: string;
|
||||
|
||||
+12
-12
@@ -67,10 +67,10 @@ startGateway({
|
||||
intents: ["GUILDS", "GUILD_MESSAGES"],
|
||||
/** The max amount of shards used for identifying. This can be useful for zero-downtime updates or resharding. */
|
||||
maxShards: 885,
|
||||
/** The first shard ID for this group of shards. */
|
||||
firstShardID: 100,
|
||||
/** The last shard ID for this group. If none is provided, it will default to loading all shards. */
|
||||
lastShardID: 124,
|
||||
/** The first shard Id for this group of shards. */
|
||||
firstShardId: 100,
|
||||
/** The last shard Id for this group. If none is provided, it will default to loading all shards. */
|
||||
lastShardId: 124,
|
||||
/** The url to forward all payloads to. */
|
||||
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. */
|
||||
@@ -93,16 +93,16 @@ export const ws = {
|
||||
reshard: true,
|
||||
/** The percentage at which resharding should occur. */
|
||||
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,
|
||||
/** The amount of shards to load per cluster */
|
||||
shardsPerCluster: 25,
|
||||
/** The maximum amount of clusters to use for your bot. */
|
||||
maxClusters: 4,
|
||||
/** The first shard ID to start spawning. */
|
||||
firstShardID: 0,
|
||||
/** The last shard ID for this cluster. */
|
||||
lastShardID: 1,
|
||||
/** The first shard Id to start spawning. */
|
||||
firstShardId: 0,
|
||||
/** The last shard Id for this cluster. */
|
||||
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. */
|
||||
createNextShard: true,
|
||||
/** 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<
|
||||
number,
|
||||
{
|
||||
shardID: number;
|
||||
shardId: number;
|
||||
resolve: (value: unknown) => void;
|
||||
reject: (reason?: unknown) => void;
|
||||
startedAt: number;
|
||||
@@ -181,7 +181,7 @@ export interface DiscordenoShard {
|
||||
/** The amount of milliseconds to wait between heartbeats */
|
||||
resumeInterval: number;
|
||||
/** The session id important for resuming connections. */
|
||||
sessionID: string;
|
||||
sessionId: string;
|
||||
/** The previous sequence number, important for resuming connections. */
|
||||
previousSequenceNumber: number | null;
|
||||
/** Whether the shard is currently resuming. */
|
||||
@@ -198,7 +198,7 @@ export interface DiscordenoShard {
|
||||
/** The interval between heartbeats requested by discord. */
|
||||
interval: number;
|
||||
/** The id of the interval, useful for stopping the interval if ws closed. */
|
||||
intervalID: number;
|
||||
intervalId: number;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
@@ -15,7 +15,7 @@ export async function cleanupLoadingShards() {
|
||||
if (now < loadingShard.startedAt + 60000) return;
|
||||
|
||||
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.`,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -3,18 +3,18 @@ import { resume } from "./resume.ts";
|
||||
import { ws } from "./ws.ts";
|
||||
|
||||
// deno-lint-ignore require-await
|
||||
export async function createShard(shardID: number) {
|
||||
export async function createShard(shardId: number) {
|
||||
const socket = new WebSocket(ws.botGatewayData.url);
|
||||
socket.binaryType = "arraybuffer";
|
||||
|
||||
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) => {
|
||||
ws.log("CLOSED", { shardID, payload: event });
|
||||
ws.log("CLOSED", { shardId, payload: event });
|
||||
|
||||
// TODO: ENUM FOR THESE CODES?
|
||||
switch (event.code) {
|
||||
@@ -35,11 +35,11 @@ export async function createShard(shardID: number) {
|
||||
case 4007:
|
||||
case 4008:
|
||||
case 4009:
|
||||
ws.log("CLOSED_RECONNECT", { shardID, payload: event });
|
||||
identify(shardID, ws.maxShards);
|
||||
ws.log("CLOSED_RECONNECT", { shardId, payload: event });
|
||||
identify(shardId, ws.maxShards);
|
||||
break;
|
||||
default:
|
||||
resume(shardID);
|
||||
resume(shardId);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
+12
-12
@@ -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. */
|
||||
export function log(
|
||||
type: "CLOSED",
|
||||
data: { shardID: number; payload: CloseEvent },
|
||||
data: { shardId: number; payload: CloseEvent },
|
||||
): unknown;
|
||||
export function log(
|
||||
type: "CLOSED_RECONNECT",
|
||||
data: { shardID: number; payload: CloseEvent },
|
||||
data: { shardId: number; payload: CloseEvent },
|
||||
): unknown;
|
||||
export function log(
|
||||
type: "ERROR",
|
||||
data: Record<string, unknown> & { shardID: number },
|
||||
data: Record<string, unknown> & { shardId: number },
|
||||
): unknown;
|
||||
export function log(
|
||||
type: "HEARTBEATING",
|
||||
data: { shardID: number; shard: DiscordenoShard },
|
||||
data: { shardId: number; shard: DiscordenoShard },
|
||||
): unknown;
|
||||
export function log(
|
||||
type: "HEARTBEATING_CLOSED",
|
||||
data: { shardID: number; shard: DiscordenoShard },
|
||||
data: { shardId: number; shard: DiscordenoShard },
|
||||
): unknown;
|
||||
export function log(
|
||||
type: "HEARTBEATING_DETAILS",
|
||||
data: { shardID: number; interval: number; shard: DiscordenoShard },
|
||||
data: { shardId: number; interval: number; shard: DiscordenoShard },
|
||||
): unknown;
|
||||
export function log(
|
||||
type: "HEARTBEATING_STARTED",
|
||||
data: { shardID: number; interval: number },
|
||||
data: { shardId: number; interval: number },
|
||||
): unknown;
|
||||
export function log(
|
||||
type: "IDENTIFYING",
|
||||
data: { shardID: number; maxShards: number },
|
||||
data: { shardId: number; maxShards: number },
|
||||
): unknown;
|
||||
export function log(
|
||||
type: "INVALID_SESSION",
|
||||
data: { shardID: number; payload: DiscordPayload },
|
||||
data: { shardId: number; payload: DiscordPayload },
|
||||
): unknown;
|
||||
export function log(type: "RAW", data: Record<string, unknown>): unknown;
|
||||
export function log(type: "RECONNECT", 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: "RECONNECT", 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:
|
||||
| "CLOSED"
|
||||
|
||||
@@ -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. */
|
||||
export async function handleDiscordPayload(
|
||||
data: DiscordPayload,
|
||||
shardID: number,
|
||||
shardId: number,
|
||||
) {
|
||||
await fetch(ws.url, {
|
||||
headers: {
|
||||
@@ -11,7 +11,7 @@ export async function handleDiscordPayload(
|
||||
},
|
||||
method: "post",
|
||||
body: JSON.stringify({
|
||||
shardID,
|
||||
shardId,
|
||||
data,
|
||||
}),
|
||||
}).catch(console.error);
|
||||
|
||||
+25
-25
@@ -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 { resume } from "./resume.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. */
|
||||
// deno-lint-ignore no-explicit-any
|
||||
export function handleOnMessage(message: any, shardID: number) {
|
||||
export function handleOnMessage(message: any, shardId: number) {
|
||||
if (message instanceof ArrayBuffer) {
|
||||
message = new Uint8Array(message);
|
||||
}
|
||||
@@ -28,69 +28,69 @@ export function handleOnMessage(message: any, shardID: number) {
|
||||
switch (messageData.op) {
|
||||
case DiscordGatewayOpcodes.Hello:
|
||||
ws.heartbeat(
|
||||
shardID,
|
||||
shardId,
|
||||
(messageData.d as DiscordHeartbeat).heartbeat_interval,
|
||||
);
|
||||
break;
|
||||
case DiscordGatewayOpcodes.HeartbeatACK:
|
||||
if (ws.shards.has(shardID)) {
|
||||
ws.shards.get(shardID)!.heartbeat.acknowledged = true;
|
||||
if (ws.shards.has(shardId)) {
|
||||
ws.shards.get(shardId)!.heartbeat.acknowledged = true;
|
||||
}
|
||||
break;
|
||||
case DiscordGatewayOpcodes.Reconnect:
|
||||
ws.log("RECONNECT", { shardID });
|
||||
ws.log("RECONNECT", { shardId });
|
||||
|
||||
if (ws.shards.has(shardID)) {
|
||||
ws.shards.get(shardID)!.resuming = true;
|
||||
if (ws.shards.has(shardId)) {
|
||||
ws.shards.get(shardId)!.resuming = true;
|
||||
}
|
||||
|
||||
resume(shardID);
|
||||
resume(shardId);
|
||||
break;
|
||||
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
|
||||
if (!messageData.d) {
|
||||
identify(shardID, ws.maxShards);
|
||||
identify(shardId, ws.maxShards);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ws.shards.has(shardID)) {
|
||||
ws.shards.get(shardID)!.resuming = true;
|
||||
if (ws.shards.has(shardId)) {
|
||||
ws.shards.get(shardId)!.resuming = true;
|
||||
}
|
||||
|
||||
resume(shardID);
|
||||
resume(shardId);
|
||||
break;
|
||||
default:
|
||||
if (messageData.t === "RESUMED") {
|
||||
ws.log("RESUMED", { shardID });
|
||||
ws.log("RESUMED", { shardId });
|
||||
|
||||
if (ws.shards.has(shardID)) {
|
||||
ws.shards.get(shardID)!.resuming = false;
|
||||
if (ws.shards.has(shardId)) {
|
||||
ws.shards.get(shardId)!.resuming = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Important for RESUME
|
||||
if (messageData.t === "READY") {
|
||||
const shard = ws.shards.get(shardID);
|
||||
const shard = ws.shards.get(shardId);
|
||||
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.delete(shardID);
|
||||
ws.loadingShards.get(shardId)?.resolve(true);
|
||||
ws.loadingShards.delete(shardId);
|
||||
}
|
||||
|
||||
// Update the sequence number if it is present
|
||||
if (messageData.s) {
|
||||
const shard = ws.shards.get(shardID);
|
||||
const shard = ws.shards.get(shardId);
|
||||
if (shard) {
|
||||
shard.previousSequenceNumber = messageData.s;
|
||||
}
|
||||
}
|
||||
|
||||
ws.handleDiscordPayload(messageData, shardID);
|
||||
ws.handleDiscordPayload(messageData, shardId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -1,33 +1,33 @@
|
||||
import { ws } from "./ws.ts";
|
||||
import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts";
|
||||
import { ws } from "./ws.ts";
|
||||
|
||||
export function heartbeat(shardID: number, interval: number) {
|
||||
ws.log("HEARTBEATING_STARTED", { shardID, interval });
|
||||
export function heartbeat(shardId: number, interval: number) {
|
||||
ws.log("HEARTBEATING_STARTED", { shardId, interval });
|
||||
|
||||
const shard = ws.shards.get(shardID);
|
||||
const shard = ws.shards.get(shardId);
|
||||
if (!shard) return;
|
||||
|
||||
ws.log("HEARTBEATING_DETAILS", { shardID, interval, shard });
|
||||
ws.log("HEARTBEATING_DETAILS", { shardId, interval, shard });
|
||||
|
||||
shard.heartbeat.keepAlive = true;
|
||||
shard.heartbeat.acknowledged = false;
|
||||
shard.heartbeat.lastSentAt = Date.now();
|
||||
shard.heartbeat.interval = interval;
|
||||
|
||||
shard.heartbeat.intervalID = setInterval(() => {
|
||||
const currentShard = ws.shards.get(shardID);
|
||||
shard.heartbeat.intervalId = setInterval(() => {
|
||||
const currentShard = ws.shards.get(shardId);
|
||||
if (!currentShard) return;
|
||||
|
||||
ws.log("HEARTBEATING", { shardID, shard: currentShard });
|
||||
ws.log("HEARTBEATING", { shardId, shard: currentShard });
|
||||
|
||||
if (
|
||||
currentShard.ws.readyState === WebSocket.CLOSED ||
|
||||
!currentShard.heartbeat.keepAlive
|
||||
) {
|
||||
ws.log("HEARTBEATING_CLOSED", { shardID, shard: currentShard });
|
||||
ws.log("HEARTBEATING_CLOSED", { shardId, shard: currentShard });
|
||||
|
||||
// STOP THE HEARTBEAT
|
||||
return clearInterval(currentShard.heartbeat.intervalID);
|
||||
return clearInterval(currentShard.heartbeat.intervalId);
|
||||
}
|
||||
|
||||
currentShard.ws.send(
|
||||
|
||||
+10
-10
@@ -1,18 +1,18 @@
|
||||
import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts";
|
||||
import { ws } from "./ws.ts";
|
||||
|
||||
export async function identify(shardID: number, maxShards: number) {
|
||||
ws.log("IDENTIFYING", { shardID, maxShards });
|
||||
export async function identify(shardId: number, maxShards: number) {
|
||||
ws.log("IDENTIFYING", { shardId, maxShards });
|
||||
|
||||
// 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
|
||||
ws.shards.set(shardID, {
|
||||
id: shardID,
|
||||
ws.shards.set(shardId, {
|
||||
id: shardId,
|
||||
ws: socket,
|
||||
resumeInterval: 0,
|
||||
sessionID: "",
|
||||
sessionId: "",
|
||||
previousSequenceNumber: 0,
|
||||
resuming: false,
|
||||
heartbeat: {
|
||||
@@ -21,7 +21,7 @@ export async function identify(shardID: number, maxShards: number) {
|
||||
acknowledged: false,
|
||||
keepAlive: false,
|
||||
interval: 0,
|
||||
intervalID: 0,
|
||||
intervalId: 0,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -29,14 +29,14 @@ export async function identify(shardID: number, maxShards: number) {
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
op: DiscordGatewayOpcodes.Identify,
|
||||
d: { ...ws.identifyPayload, shard: [shardID, maxShards] },
|
||||
d: { ...ws.identifyPayload, shard: [shardId, maxShards] },
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
ws.loadingShards.set(shardID, {
|
||||
shardID,
|
||||
ws.loadingShards.set(shardId, {
|
||||
shardId,
|
||||
resolve,
|
||||
reject,
|
||||
startedAt: Date.now(),
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { ws } from "./ws.ts";
|
||||
import { getGatewayBot } from "../helpers/misc/get_gateway_bot.ts";
|
||||
import { ws } from "./ws.ts";
|
||||
|
||||
/** The handler to automatically reshard when necessary. */
|
||||
export async function resharder() {
|
||||
@@ -26,5 +26,5 @@ export async function resharder() {
|
||||
ws.botGatewayData.shards = data.shards;
|
||||
ws.botGatewayData.url = data.url;
|
||||
|
||||
ws.spawnShards(ws.firstShardID);
|
||||
ws.spawnShards(ws.firstShardId);
|
||||
}
|
||||
|
||||
+11
-11
@@ -1,31 +1,31 @@
|
||||
import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts";
|
||||
import { ws } from "./ws.ts";
|
||||
|
||||
export async function resume(shardID: number) {
|
||||
ws.log("RESUMING", { shardID });
|
||||
export async function resume(shardId: number) {
|
||||
ws.log("RESUMING", { shardId });
|
||||
|
||||
// CREATE A SHARD
|
||||
const socket = await ws.createShard(shardID);
|
||||
const socket = await ws.createShard(shardId);
|
||||
|
||||
// NOW WE HANDLE RESUMING THIS SHARD
|
||||
// Get the old data for this shard necessary for resuming
|
||||
const oldShard = ws.shards.get(shardID);
|
||||
const oldShard = ws.shards.get(shardId);
|
||||
|
||||
if (oldShard) {
|
||||
// HOW TO CLOSE OLD SHARD SOCKET!!!
|
||||
oldShard.ws.close(4009, "Resuming the shard, closing old shard.");
|
||||
// STOP OLD HEARTBEAT
|
||||
clearInterval(oldShard.heartbeat.intervalID);
|
||||
clearInterval(oldShard.heartbeat.intervalId);
|
||||
}
|
||||
|
||||
const sessionID = oldShard?.sessionID || "";
|
||||
const sessionId = oldShard?.sessionId || "";
|
||||
const previousSequenceNumber = oldShard?.previousSequenceNumber || 0;
|
||||
|
||||
ws.shards.set(shardID, {
|
||||
id: shardID,
|
||||
ws.shards.set(shardId, {
|
||||
id: shardId,
|
||||
ws: socket,
|
||||
resumeInterval: 0,
|
||||
sessionID,
|
||||
sessionId,
|
||||
previousSequenceNumber,
|
||||
resuming: false,
|
||||
heartbeat: {
|
||||
@@ -34,7 +34,7 @@ export async function resume(shardID: number) {
|
||||
acknowledged: false,
|
||||
keepAlive: false,
|
||||
interval: 0,
|
||||
intervalID: 0,
|
||||
intervalId: 0,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -45,7 +45,7 @@ export async function resume(shardID: number) {
|
||||
op: DiscordGatewayOpcodes.Resume,
|
||||
d: {
|
||||
token: ws.identifyPayload.token,
|
||||
session_id: sessionID,
|
||||
session_id: sessionId,
|
||||
seq: previousSequenceNumber,
|
||||
},
|
||||
}),
|
||||
|
||||
+12
-12
@@ -2,25 +2,25 @@ import { Collection } from "../util/collection.ts";
|
||||
import { ws } from "./ws.ts";
|
||||
|
||||
/** Begin spawning shards. */
|
||||
export function spawnShards(firstShardID = 0) {
|
||||
/** Stored as bucketID: [clusterID, [ShardIDs]] */
|
||||
export function spawnShards(firstShardId = 0) {
|
||||
/** Stored as bucketId: [clusterId, [ShardIds]] */
|
||||
const buckets = new Collection<number, number[][]>();
|
||||
const maxShards = ws.maxShards || ws.botGatewayData.shards;
|
||||
let cluster = 0;
|
||||
|
||||
for (
|
||||
let index = firstShardID;
|
||||
let index = firstShardId;
|
||||
index < ws.botGatewayData.sessionStartLimit.maxConcurrency;
|
||||
index++
|
||||
) {
|
||||
// ORGANIZE ALL SHARDS INTO THEIR OWN BUCKETS
|
||||
for (let i = 0; i < maxShards; i++) {
|
||||
const bucketID = i % ws.botGatewayData.sessionStartLimit.maxConcurrency;
|
||||
const bucket = buckets.get(bucketID);
|
||||
const bucketId = i % ws.botGatewayData.sessionStartLimit.maxConcurrency;
|
||||
const bucket = buckets.get(bucketId);
|
||||
|
||||
if (!bucket) {
|
||||
// Create the bucket since it doesnt exist
|
||||
buckets.set(bucketID, [[cluster, i]]);
|
||||
buckets.set(bucketId, [[cluster, i]]);
|
||||
|
||||
if (cluster + 1 <= ws.maxClusters) cluster++;
|
||||
} else {
|
||||
@@ -39,13 +39,13 @@ export function spawnShards(firstShardID = 0) {
|
||||
}
|
||||
|
||||
// SPREAD THIS OUT TO DIFFERENT CLUSTERS TO BEGIN STARTING UP
|
||||
buckets.forEach(async (bucket, bucketID) => {
|
||||
for (const [clusterID, ...queue] of bucket) {
|
||||
let shardID = queue.shift();
|
||||
buckets.forEach(async (bucket, bucketId) => {
|
||||
for (const [clusterId, ...queue] of bucket) {
|
||||
let shardId = queue.shift();
|
||||
|
||||
while (shardID !== undefined) {
|
||||
await ws.tellClusterToIdentify(clusterID as number, shardID, bucketID);
|
||||
shardID = queue.shift();
|
||||
while (shardId !== undefined) {
|
||||
await ws.tellClusterToIdentify(clusterId as number, shardId, bucketId);
|
||||
shardId = queue.shift();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { StartGatewayOptions } from "./start_gateway_options.ts";
|
||||
import { DiscordGatewayIntents } from "../types/gateway/gateway_intents.ts";
|
||||
import { StartGatewayOptions } from "./start_gateway_options.ts";
|
||||
import { ws } from "./ws.ts";
|
||||
|
||||
/** ADVANCED DEVS ONLY!!!!!!
|
||||
@@ -9,7 +9,7 @@ import { ws } from "./ws.ts";
|
||||
export async function startGateway(options: StartGatewayOptions) {
|
||||
ws.identifyPayload.token = `Bot ${options.token}`;
|
||||
ws.secretKey = options.secretKey;
|
||||
ws.firstShardID = options.firstShardID;
|
||||
ws.firstShardId = options.firstShardId;
|
||||
ws.url = options.url;
|
||||
if (options.shardsPerCluster) ws.shardsPerCluster = options.shardsPerCluster;
|
||||
if (options.maxClusters) ws.maxClusters = options.maxClusters;
|
||||
@@ -36,7 +36,7 @@ export async function startGateway(options: StartGatewayOptions) {
|
||||
}).then((res) => res.json())) as DiscordBotGatewayData;
|
||||
|
||||
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
|
||||
// ws.botGatewayData = snakeToCamel(await getGatewayBot())
|
||||
@@ -50,6 +50,6 @@ export async function startGateway(options: StartGatewayOptions) {
|
||||
ws.botGatewayData.shards = data.shards;
|
||||
ws.botGatewayData.url = data.url;
|
||||
|
||||
ws.spawnShards(ws.firstShardID);
|
||||
ws.spawnShards(ws.firstShardId);
|
||||
ws.cleanupLoadingShards();
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ export interface StartGatewayOptions {
|
||||
intents: (DiscordGatewayIntents | keyof typeof DiscordGatewayIntents)[];
|
||||
/** The max amount of shards used for identifying. This can be useful for zero-downtime updates or resharding. */
|
||||
maxShards?: number;
|
||||
/** The first shard ID for this group of shards. */
|
||||
firstShardID: number;
|
||||
/** The last shard ID for this group. If none is provided, it will default to loading all shards. */
|
||||
lastShardID?: number;
|
||||
/** The first shard Id for this group of shards. */
|
||||
firstShardId: number;
|
||||
/** The last shard Id for this group. If none is provided, it will default to loading all shards. */
|
||||
lastShardId?: number;
|
||||
/** The url to forward all payloads to. */
|
||||
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. */
|
||||
|
||||
@@ -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. */
|
||||
export async function tellClusterToIdentify(
|
||||
workerID: number,
|
||||
shardID: number,
|
||||
bucketID: number,
|
||||
workerId: number,
|
||||
shardId: number,
|
||||
bucketId: number,
|
||||
) {
|
||||
// When resharding this may exist already
|
||||
const oldShard = ws.shards.get(shardID);
|
||||
const oldShard = ws.shards.get(shardId);
|
||||
|
||||
// TODO: Use workers
|
||||
await ws.identify(shardID, ws.maxShards);
|
||||
await ws.identify(shardId, ws.maxShards);
|
||||
|
||||
if (oldShard) {
|
||||
oldShard.ws.close(4009, "Resharded!");
|
||||
|
||||
+17
-17
@@ -1,15 +1,15 @@
|
||||
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 { 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 { 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
|
||||
export const ws = {
|
||||
@@ -21,16 +21,16 @@ export const ws = {
|
||||
reshard: true,
|
||||
/** The percentage at which resharding should occur. */
|
||||
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,
|
||||
/** The amount of shards to load per cluster */
|
||||
shardsPerCluster: 25,
|
||||
/** The maximum amount of clusters to use for your bot. */
|
||||
maxClusters: 4,
|
||||
/** The first shard ID to start spawning. */
|
||||
firstShardID: 0,
|
||||
/** The last shard ID for this cluster. */
|
||||
lastShardID: 1,
|
||||
/** The first shard Id to start spawning. */
|
||||
firstShardId: 0,
|
||||
/** The last shard Id for this cluster. */
|
||||
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. */
|
||||
createNextShard: true,
|
||||
/** 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<
|
||||
number,
|
||||
{
|
||||
shardID: number;
|
||||
shardId: number;
|
||||
resolve: (value: unknown) => void;
|
||||
reject: (reason?: unknown) => void;
|
||||
startedAt: number;
|
||||
@@ -111,7 +111,7 @@ export interface DiscordenoShard {
|
||||
/** The amount of milliseconds to wait between heartbeats */
|
||||
resumeInterval: number;
|
||||
/** The session id important for resuming connections. */
|
||||
sessionID: string;
|
||||
sessionId: string;
|
||||
/** The previous sequence number, important for resuming connections. */
|
||||
previousSequenceNumber: number | null;
|
||||
/** Whether the shard is currently resuming. */
|
||||
@@ -128,6 +128,6 @@ export interface DiscordenoShard {
|
||||
/** The interval between heartbeats requested by discord. */
|
||||
interval: number;
|
||||
/** The id of the interval, useful for stopping the interval if ws closed. */
|
||||
intervalID: number;
|
||||
intervalId: number;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user