hopefully make the DiscordClient connection more robust

This commit is contained in:
hippoz 2022-02-03 20:41:23 +02:00
parent fe04b99c35
commit 0950241d3b
No known key found for this signature in database
GPG key ID: 7C52899193467641
2 changed files with 84 additions and 34 deletions

View file

@ -7,10 +7,16 @@ const opcodes = {
EVENT: 0, EVENT: 0,
CLIENT_HEARTBEAT: 1, CLIENT_HEARTBEAT: 1,
IDENTIFY: 2, IDENTIFY: 2,
RESUME: 6,
INVALID_SESSION: 9,
HELLO: 10, HELLO: 10,
HEARTBEAT_ACK: 11, HEARTBEAT_ACK: 11,
}; };
const reconnectOnCloseCodes = [
1000, 1001, 4000, 4001, 4002, 4003, 4005, 4007, 4008, 4009
];
class DiscordClient extends EventEmitter { class DiscordClient extends EventEmitter {
constructor(token, { intents, baseDomain="discord.com", gatewayUrl="wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream", apiBase="https://discord.com/api/v9" } = {}) { constructor(token, { intents, baseDomain="discord.com", gatewayUrl="wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream", apiBase="https://discord.com/api/v9" } = {}) {
super(); super();
@ -50,27 +56,8 @@ class DiscordClient extends EventEmitter {
}, this._heartbeatIntervalTime); }, this._heartbeatIntervalTime);
} }
_handleGatewayMessage(ws, message) { _getIdentifyPayload() {
try { return {
message = JSON.parse(message);
} catch(e) {
console.error("error: DiscordClient: on `message`: failed to parse incoming message as JSON", e);
return;
}
if (message.s) {
this.seq = message.s;
}
const payload = message.d;
switch (message.op) {
case opcodes.HELLO: {
this._setHeartbeat(payload.heartbeat_interval);
ws.send(JSON.stringify({
op: opcodes.IDENTIFY,
d: {
token: this.token, token: this.token,
intents: this.intents, intents: this.intents,
properties: { properties: {
@ -89,8 +76,47 @@ class DiscordClient extends EventEmitter {
status: "online", status: "online",
afk: false afk: false
} }
};
} }
_getResumePayload() {
return {
token: this.token,
session_id: this.sessionId,
seq: this.seq
};
}
_handleGatewayMessage(ws, message) {
try {
message = JSON.parse(message);
} catch(e) {
console.error("error: DiscordClient: on `message`: failed to parse incoming message as JSON", e);
return;
}
if (message.s) {
this.seq = message.s;
}
const payload = message.d;
switch (message.op) {
case opcodes.HELLO: {
this._setHeartbeat(payload.heartbeat_interval);
if (this.resuming) {
console.warn("DiscordClient: resuming...");
ws.send(JSON.stringify({
op: opcodes.RESUME,
d: this._getResumePayload()
})); }));
} else {
ws.send(JSON.stringify({
op: opcodes.IDENTIFY,
d: this._getIdentifyPayload()
}));
}
break; break;
} }
@ -183,6 +209,23 @@ class DiscordClient extends EventEmitter {
break; break;
} }
case opcodes.INVALID_SESSION: {
if (message.d) {
// connection is resumable, we are going to resume the connection
this.resuming = true;
this.connect();
} else {
// connection is not resumable, wait some time and then send a new IDENTIFY payload
setTimeout(() => {
ws.send(JSON.stringify({
op: opcodes.IDENTIFY,
d: this._getIdentifyPayload()
}));
}, 3500);
}
break;
}
default: { default: {
console.warn(`warn: DiscordClient: got unhandled opcode "${message.op}"`); console.warn(`warn: DiscordClient: got unhandled opcode "${message.op}"`);
break; break;
@ -191,6 +234,12 @@ class DiscordClient extends EventEmitter {
} }
connect() { connect() {
if (this.ws) {
this.ws.removeAllListeners();
this.ws.close();
this.ws = null;
}
const ws = new WebSocket(this.gatewayUrl); const ws = new WebSocket(this.gatewayUrl);
this.ws = ws; this.ws = ws;
@ -208,16 +257,22 @@ class DiscordClient extends EventEmitter {
ws.on("close", (code, reason) => { ws.on("close", (code, reason) => {
reason = reason.toString(); reason = reason.toString();
this.emit("close", code, reason);
console.error(`DiscordClient: on \`close\`: disconnected from gateway: code \`${code}\`, reason \`${reason}\``); console.error(`DiscordClient: on \`close\`: disconnected from gateway: code \`${code}\`, reason \`${reason}\``);
this.emit("close", code, reason);
this._setHeartbeat(-1);
if (reconnectOnCloseCodes.includes(code)) {
this.resuming = true;
this.connect();
}
}); });
ws.on("error", (e) => { ws.on("error", (e) => {
console.error("DiscordClient: websocket error:", e); console.error("DiscordClient: websocket error:", e);
console.log("DiscordClient: reconnecting in a couple of miliseconds"); console.log("DiscordClient: reconnecting?");
setTimeout(() => { this._setHeartbeat(-1);
this.resuming = true;
this.connect(); this.connect();
}, 400);
}); });
} }

View file

@ -13,7 +13,7 @@ export function wait(time, shouldReject=false) {
}); });
} }
bot.on("READY", () => { bot.once("READY", () => {
watchedGuildIds.forEach(id => { watchedGuildIds.forEach(id => {
const watchedGuild = new WatchedGuild(); const watchedGuild = new WatchedGuild();
watchedGuild.upstreamGuildId = id; watchedGuild.upstreamGuildId = id;
@ -21,8 +21,3 @@ bot.on("READY", () => {
guildMap.set(id, watchedGuild); guildMap.set(id, watchedGuild);
}); });
}); });
bot.on("close", (code, reason) => {
console.log("bot: connection closed, reconnecting...");
bot.connect();
});