Merge pull request #36 from Skillz4Killz/unresumable-error-handling

bette error handling
This commit is contained in:
Skillz4Killz
2020-05-17 21:26:19 -04:00
committed by GitHub
+83 -82
View File
@@ -12,13 +12,14 @@ import {
import { logRed } from "../utils/logger.ts";
import { FetchMembersOptions } from "../types/guild.ts";
import { delay } from "https://deno.land/std@0.50.0/async/delay.ts";
let shardSocket: WebSocket;
/** The session id is needed for RESUME functionality when discord disconnects randomly. */
let sessionID = "";
// Discord requests null if no number has yet been sent by discord
export let previousSequenceNumber: number | null = null;
let previousSequenceNumber: number | null = null;
let needToResume = false;
// TODO: If a client does not receive a heartbeat ack between its attempts at sending heartbeats, it should immediately terminate the connection with a non-1000 close code, reconnect, and attempt to resume.
@@ -43,7 +44,7 @@ async function resumeConnection(
if (needToResume) resumeConnection(botGatewayData, identifyPayload);
}
export const createShard = async (
const createShard = async (
botGatewayData: DiscordBotGatewayData,
identifyPayload: object,
resuming = false,
@@ -67,57 +68,64 @@ export const createShard = async (
}));
}
try {
for await (const message of shardSocket) {
if (typeof message === "string") {
const data = JSON.parse(message);
for await (const message of shardSocket) {
if (typeof message === "string") {
const data = JSON.parse(message);
switch (data.op) {
case GatewayOpcode.Hello:
sendConstantHeartbeats(
(data.d as DiscordHeartbeatPayload).heartbeat_interval,
);
switch (data.op) {
case GatewayOpcode.Hello:
sendConstantHeartbeats(
(data.d as DiscordHeartbeatPayload).heartbeat_interval,
);
break;
case GatewayOpcode.Reconnect:
case GatewayOpcode.InvalidSession:
needToResume = true;
resumeConnection(botGatewayData, identifyPayload);
break;
default:
if (data.t === "RESUMED") {
needToResume = false;
break;
case GatewayOpcode.Reconnect:
case GatewayOpcode.InvalidSession:
needToResume = true;
resumeConnection(botGatewayData, identifyPayload);
break;
default:
if (data.t === "RESUMED") {
needToResume = false;
break;
}
// Important for RESUME
if (data.t === "READY") {
sessionID = (data.d as ReadyPayload).session_id;
}
}
// Important for RESUME
if (data.t === "READY") {
sessionID = (data.d as ReadyPayload).session_id;
}
// Update the sequence number if it is present
if (data.s) previousSequenceNumber = data.s;
// Update the sequence number if it is present
if (data.s) previousSequenceNumber = data.s;
// @ts-ignore
postMessage(
{
type: "HANDLE_DISCORD_PAYLOAD",
payload: message,
resumeInterval,
},
);
break;
}
} else if (isWebSocketCloseEvent(message)) {
logRed(`Close :( ${JSON.stringify(message)}`);
// @ts-ignore
postMessage(
{
type: "HANDLE_DISCORD_PAYLOAD",
payload: message,
resumeInterval,
},
);
break;
}
} else if (isWebSocketCloseEvent(message)) {
logRed(`Close :( ${JSON.stringify(message)}`);
// These error codes should just crash the projects
if ([4004, 4005, 4012, 4013, 4014].includes(message.code)) {
throw new Error(
"Shard.ts: Error occurred that is not resumeable or able to be reconnected.",
);
}
// These error codes can not be resumed but need to reconnect from start
if ([4003, 4007, 4008, 4009].includes(message.code)) {
createShard(botGatewayData, identifyPayload);
} else {
needToResume = true;
resumeConnection(botGatewayData, identifyPayload);
}
}
} catch (error) {
logRed(error);
}
};
export function requestGuildMembers(
function requestGuildMembers(
guildID: string,
nonce: string,
options?: FetchMembersOptions,
@@ -135,46 +143,39 @@ export function requestGuildMembers(
}));
}
// TODO: Remove ts-ignore once https://github.com/denoland/deno/issues/5262 fixed
// TODO: Errors need to be fixed by VSC plugin
postMessage({ type: "REQUEST_CLIENT_OPTIONS" });
// @ts-ignore
if (typeof self.postMessage === "function") {
// @ts-ignore
postMessage({ type: "REQUEST_CLIENT_OPTIONS" });
}
// @ts-ignore
if (typeof self.onmessage === "function") {
// @ts-ignore
onmessage = (message) => {
if (message.data.type === "CREATE_SHARD") {
createShard(
message.data.botGatewayData,
message.data.identifyPayload,
);
}
onmessage = (message: MessageEvent) => {
if (message.data.type === "CREATE_SHARD") {
createShard(
message.data.botGatewayData,
message.data.identifyPayload,
);
}
if (message.data.type === "FETCH_MEMBERS") {
requestGuildMembers(
message.data.guildID,
message.data.nonce,
message.data.options,
);
}
if (message.data.type === "FETCH_MEMBERS") {
requestGuildMembers(
message.data.guildID,
message.data.nonce,
message.data.options,
);
}
if (message.data.type === "EDIT_BOTS_STATUS") {
shardSocket.send(JSON.stringify({
op: GatewayOpcode.StatusUpdate,
d: {
since: null,
game: message.data.game.name
? {
name: message.data.game.name,
type: message.data.game.type,
}
: null,
status: message.data.status,
afk: false,
},
}));
}
};
}
if (message.data.type === "EDIT_BOTS_STATUS") {
shardSocket.send(JSON.stringify({
op: GatewayOpcode.StatusUpdate,
d: {
since: null,
game: message.data.game.name
? {
name: message.data.game.name,
type: message.data.game.type,
}
: null,
status: message.data.status,
afk: false,
},
}));
}
};