Compare commits
2 commits
742a46c708
...
d24929da0c
Author | SHA1 | Date | |
---|---|---|---|
|
d24929da0c | ||
|
5ce0b81864 |
2 changed files with 52 additions and 57 deletions
|
@ -7,18 +7,17 @@ const opcodes = {
|
||||||
EVENT: 0,
|
EVENT: 0,
|
||||||
CLIENT_HEARTBEAT: 1,
|
CLIENT_HEARTBEAT: 1,
|
||||||
IDENTIFY: 2,
|
IDENTIFY: 2,
|
||||||
RESUME: 6,
|
|
||||||
INVALID_SESSION: 9,
|
INVALID_SESSION: 9,
|
||||||
HELLO: 10,
|
HELLO: 10,
|
||||||
HEARTBEAT_ACK: 11,
|
HEARTBEAT_ACK: 11,
|
||||||
};
|
};
|
||||||
|
|
||||||
const reconnectOnCloseCodes = [
|
const skipReconnectFor = [
|
||||||
1000, 1001, 4000, 4001, 4002, 4003, 4005, 4007, 4008, 4009
|
4004, 4010, 4011, 4012, 4013, 4014
|
||||||
];
|
];
|
||||||
|
|
||||||
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, gatewayUrl="wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream", apiBase="https://discord.com/api/v9" } = {}) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.token = token;
|
this.token = token;
|
||||||
|
@ -79,19 +78,11 @@ class DiscordClient extends EventEmitter {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_getResumePayload() {
|
|
||||||
return {
|
|
||||||
token: this.token,
|
|
||||||
session_id: this.sessionId,
|
|
||||||
seq: this.seq
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleGatewayMessage(ws, message) {
|
_handleGatewayMessage(ws, message) {
|
||||||
try {
|
try {
|
||||||
message = JSON.parse(message);
|
message = JSON.parse(message);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.error("error: DiscordClient: on `message`: failed to parse incoming message as JSON", e);
|
console.error("error: DiscordClient: on 'message': failed to parse incoming message as JSON", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,28 +94,20 @@ class DiscordClient extends EventEmitter {
|
||||||
|
|
||||||
switch (message.op) {
|
switch (message.op) {
|
||||||
case opcodes.HELLO: {
|
case opcodes.HELLO: {
|
||||||
|
console.log(`DiscordClient: HELLO; heartbeat_interval=${payload.heartbeat_interval}`);
|
||||||
this._setHeartbeat(payload.heartbeat_interval);
|
this._setHeartbeat(payload.heartbeat_interval);
|
||||||
|
|
||||||
if (this.resuming) {
|
|
||||||
console.warn("DiscordClient: resuming...");
|
|
||||||
this.resuming = false;
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
op: opcodes.RESUME,
|
|
||||||
d: this._getResumePayload()
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
op: opcodes.IDENTIFY,
|
op: opcodes.IDENTIFY,
|
||||||
d: this._getIdentifyPayload()
|
d: this._getIdentifyPayload()
|
||||||
}));
|
}));
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case opcodes.EVENT: {
|
case opcodes.EVENT: {
|
||||||
switch (message.t) {
|
switch (message.t) {
|
||||||
case "READY": {
|
case "READY": {
|
||||||
console.log("DiscordClient: ready");
|
console.log("DiscordClient: READY");
|
||||||
this.user = payload.user;
|
this.user = payload.user;
|
||||||
this.sessionId = payload.session_id;
|
this.sessionId = payload.session_id;
|
||||||
this.guilds = payload.guilds;
|
this.guilds = payload.guilds;
|
||||||
|
@ -211,19 +194,8 @@ class DiscordClient extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
case opcodes.INVALID_SESSION: {
|
case opcodes.INVALID_SESSION: {
|
||||||
if (message.d) {
|
console.error("DiscordClient: INVALID_SESSION - please check your authentication token");
|
||||||
// connection is resumable, we are going to resume the connection
|
console.error("DiscordClient: INVALID_SESSION: will not reconnect");
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +207,9 @@ class DiscordClient extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
|
console.log("DiscordClient: connecting...");
|
||||||
if (this.ws) {
|
if (this.ws) {
|
||||||
|
console.log("DiscordClient: a websocket connection already exists, killing...");
|
||||||
this.ws.removeAllListeners();
|
this.ws.removeAllListeners();
|
||||||
this.ws.close();
|
this.ws.close();
|
||||||
this.ws = null;
|
this.ws = null;
|
||||||
|
@ -249,30 +223,37 @@ class DiscordClient extends EventEmitter {
|
||||||
});
|
});
|
||||||
|
|
||||||
// we decompressed the data, send it to the handler now
|
// we decompressed the data, send it to the handler now
|
||||||
this.inflate.on("data", (message) => this._handleGatewayMessage(ws, message));
|
this.inflate.on("data", (message) =>
|
||||||
|
this._handleGatewayMessage(ws, message)
|
||||||
|
);
|
||||||
ws.on("message", (data, isBinary) => {
|
ws.on("message", (data, isBinary) => {
|
||||||
// pass the data to the decompressor
|
// pass the data to the decompressor
|
||||||
this.inflate.write(data);
|
this.inflate.write(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
ws.on("open", () => {
|
||||||
|
console.log("DiscordClient: WebSocket 'open'")
|
||||||
|
});
|
||||||
|
|
||||||
ws.on("close", (code, reason) => {
|
ws.on("close", (code, reason) => {
|
||||||
reason = reason.toString();
|
reason = reason.toString();
|
||||||
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.emit("close", code, reason);
|
||||||
this._setHeartbeat(-1);
|
this._setHeartbeat(-1);
|
||||||
if (reconnectOnCloseCodes.includes(code)) {
|
if (skipReconnectFor.includes(code)) {
|
||||||
this.resuming = true;
|
console.error("DiscordClient: on 'close': the exit code above is in skipReconnectFor, and thus the server will not reconnect.");
|
||||||
|
} else {
|
||||||
|
console.log("DiscordClient: on 'close': the client will now attempt to reconnect...");
|
||||||
this.connect();
|
this.connect();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.on("error", (e) => {
|
ws.on("error", (e) => {
|
||||||
console.error("DiscordClient: websocket error:", e);
|
console.error("DiscordClient: on 'error': websocket error:", e);
|
||||||
console.log("DiscordClient: reconnecting?");
|
console.log("DiscordClient: on 'error': reconnecting due to previous websocket error...");
|
||||||
this._setHeartbeat(-1);
|
this._setHeartbeat(-1);
|
||||||
this.resuming = true;
|
|
||||||
this.connect();
|
this.connect();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@ const messageTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const chatMessageRegex = /^\[(?:.*?)\]: \<(?<username>.*)\> (?<message>.*)/;
|
const chatMessageRegex = /^\[(?:.*?)\]: \<(?<username>.*)\> (?<message>.*)/;
|
||||||
|
const joinNotificationRegex = /^\[(?:.*?)\]: (?<username>.*) joined the game/;
|
||||||
|
const leaveNotificationRegex = /^\[(?:.*?)\]: (?<username>.*) left the game/;
|
||||||
const rconConnection = new Rcon("localhost", "25575", process.env.RCON_PASSWORD);
|
const rconConnection = new Rcon("localhost", "25575", process.env.RCON_PASSWORD);
|
||||||
|
|
||||||
export default class GatewayClient {
|
export default class GatewayClient {
|
||||||
|
@ -91,7 +93,7 @@ export default class GatewayClient {
|
||||||
console.log("gateway: open");
|
console.log("gateway: open");
|
||||||
});
|
});
|
||||||
this.ws.on("close", () => {
|
this.ws.on("close", () => {
|
||||||
console.log("gateway: closed");
|
console.log("gateway: closed, reconnecting in 4000ms");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
console.log("gateway: reconnecting");
|
console.log("gateway: reconnecting");
|
||||||
this.connect(token);
|
this.connect(token);
|
||||||
|
@ -141,10 +143,10 @@ async function main() {
|
||||||
rconConnection.on("error", (e) => {
|
rconConnection.on("error", (e) => {
|
||||||
console.error("rcon: got error", e);
|
console.error("rcon: got error", e);
|
||||||
if (!rconConnection.hasAuthed) {
|
if (!rconConnection.hasAuthed) {
|
||||||
console.log("rcon: reconnecting in 1200ms due to error before hasAuthed");
|
console.log("rcon: reconnecting in 5000ms due to error before hasAuthed (server might not be up yet?)");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
rconConnection.connect();
|
rconConnection.connect();
|
||||||
}, 1200);
|
}, 5000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const gateway = new GatewayClient(GATEWAY_ORIGIN);
|
const gateway = new GatewayClient(GATEWAY_ORIGIN);
|
||||||
|
@ -160,12 +162,24 @@ async function main() {
|
||||||
process.stdin.on("data", async (rawDataBuffer) => {
|
process.stdin.on("data", async (rawDataBuffer) => {
|
||||||
const stringData = rawDataBuffer.toString().trim();
|
const stringData = rawDataBuffer.toString().trim();
|
||||||
console.log(stringData);
|
console.log(stringData);
|
||||||
const result = chatMessageRegex.exec(stringData);
|
|
||||||
if (!result)
|
|
||||||
return;
|
|
||||||
const { username, message } = result.groups;
|
|
||||||
|
|
||||||
await sendBridgeMessageAs(TARGET_GUILD_ID, TARGET_CHANNEL_ID, message, username, null);
|
const joinResult = joinNotificationRegex.exec(stringData);
|
||||||
|
if (joinResult) {
|
||||||
|
await sendBridgeMessageAs(TARGET_GUILD_ID, TARGET_CHANNEL_ID, `**${joinResult.groups.username}** joined the game`, null, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const leaveResult = leaveNotificationRegex.exec(stringData);
|
||||||
|
if (leaveResult) {
|
||||||
|
await sendBridgeMessageAs(TARGET_GUILD_ID, TARGET_CHANNEL_ID, `**${leaveResult.groups.username}** left the game`, null, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageResult = chatMessageRegex.exec(stringData);
|
||||||
|
if (messageResult) {
|
||||||
|
await sendBridgeMessageAs(TARGET_GUILD_ID, TARGET_CHANNEL_ID, messageResult.groups.message, messageResult.groups.username, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue