add session counters and presence (brainlet-react presence is locked behind an experiment flag, just enable it with an override at the moment)

This commit is contained in:
hippoz 2021-06-10 14:02:31 +03:00
parent 76f041e75e
commit 3a53d585ff
Signed by: hippoz
GPG key ID: 7C52899193467641
3 changed files with 61 additions and 13 deletions

View file

@ -6,7 +6,7 @@ const werift = require("werift");
const { experiments } = require("../../../experiments"); const { experiments } = require("../../../experiments");
const User = require("../../../models/User"); const User = require("../../../models/User");
const Channel = require("../../../models/Channel"); const Channel = require("../../../models/Channel");
const { parseMessage, opcodeSeparator, getOpcodeByName } = require("./messageparser"); const { parseMessage, packet } = require("./messageparser");
const { checkToken } = require("../../../common/auth/authfunctions"); const { checkToken } = require("../../../common/auth/authfunctions");
const pingCheckDelay = 10000; const pingCheckDelay = 10000;
@ -43,6 +43,7 @@ class GatewayServer extends EventEmitter {
constructor({ server }) { constructor({ server }) {
super(); super();
this.wss = new websockets.Server({ server: server, path: "/gateway" }); this.wss = new websockets.Server({ server: server, path: "/gateway" });
this.sessionCounters = {};
this.pingInterval = setInterval(() => { this.pingInterval = setInterval(() => {
this.wss.clients.forEach((client) => { this.wss.clients.forEach((client) => {
@ -61,7 +62,7 @@ class GatewayServer extends EventEmitter {
this.wss.on("connection", (ws) => { this.wss.on("connection", (ws) => {
// Send HELLO message as soon as the client connects // Send HELLO message as soon as the client connects
ws.send(this.packet("HELLO", {})); ws.send(packet("HELLO", {}));
ws.session = { ws.session = {
authenticated: false, authenticated: false,
user: null, user: null,
@ -73,6 +74,21 @@ class GatewayServer extends EventEmitter {
ws.alive = true; ws.alive = true;
}); });
ws.on("close", async () => { ws.on("close", async () => {
if (this.sessionCounters[ws.session.user._id] <= 1) {
this.inChannel(ws.channels[0], (client) => {
console.log(client.session);
client.send(packet("EVENT_CHANNEL_MEMBERS", {
[ws.session.user._id]: {
_id: ws.session.user._id,
username: ws.session.user.username,
status: 0,
status_text: "Now offline"
}
}));
});
}
this.sessionCounters[ws.session.user._id]--;
if (ws.rtc) await ws.rtc.connection.close(); if (ws.rtc) await ws.rtc.connection.close();
}); });
ws.on("message", async (data) => { ws.on("message", async (data) => {
@ -87,6 +103,12 @@ class GatewayServer extends EventEmitter {
ws.session.user = user; ws.session.user = user;
ws.session.authenticated = true; ws.session.authenticated = true;
if (!this.sessionCounters[user._id]) this.sessionCounters[user._id] = 0;
this.sessionCounters[user._id]++;
if (this.sessionCounters[user._id] > 5) {
return ws.close(4006, "Too many sessions.");
}
// The user is now successfully authenticated, send the YOO_ACK packet // The user is now successfully authenticated, send the YOO_ACK packet
// TODO: This is probably not efficient // TODO: This is probably not efficient
@ -96,7 +118,30 @@ class GatewayServer extends EventEmitter {
ws.channels = channels.map(x => x._id); ws.channels = channels.map(x => x._id);
ws.send(this.packet("YOO_ACK", { session_id: ws.session.sessionId, channels, user: { username: user.username, _id: user._id }, __global_experiments: experiments })); ws.send(packet("YOO_ACK", { session_id: ws.session.sessionId, channels, user: { username: user.username, _id: user._id }, __global_experiments: experiments }));
let presence = {};
const channel = channels[0];
this.inChannel(channel._id, (client) => {
presence[client.session.user._id] = {
_id: client.session.user._id,
username: client.session.user.username,
status: 1,
status_text: "Online"
};
if (client.session.sessionId !== ws.session.sessionId) client.send(packet("EVENT_CHANNEL_MEMBERS", {
[ws.session.user._id]: {
_id: ws.session.user._id,
username: ws.session.user.username,
status: 1,
status_text: "Online"
}
}));
});
ws.send(packet("EVENT_CHANNEL_MEMBERS", presence));
console.log(`gateway: user ${user.username}: handshake complete`); console.log(`gateway: user ${user.username}: handshake complete`);
} catch (e) { } catch (e) {
console.log("gateway:", e); console.log("gateway:", e);
@ -116,7 +161,7 @@ class GatewayServer extends EventEmitter {
// Check if the user is in that channel before broadcasting the message // Check if the user is in that channel before broadcasting the message
if (!ws.channels.includes(message.data.channel._id)) return ws.close(4008, "Not authorized to perform action."); if (!ws.channels.includes(message.data.channel._id)) return ws.close(4008, "Not authorized to perform action.");
this.broadcast(message.data.channel._id, this.packet("EVENT_CREATE_MESSAGE", { this.broadcast(message.data.channel._id, packet("EVENT_CREATE_MESSAGE", {
content: messageContent, content: messageContent,
channel: { channel: {
_id: message.data.channel._id _id: message.data.channel._id
@ -133,7 +178,7 @@ class GatewayServer extends EventEmitter {
case "ACTION_VOICE_REQUEST_SESSION": { case "ACTION_VOICE_REQUEST_SESSION": {
if (!experiments.voiceSFUTesting) return; if (!experiments.voiceSFUTesting) return;
// just send ourselves as the voice server lol // just send ourselves as the voice server lol
ws.send(this.packet("EVENT_VOICE_ASSIGN_SERVER", { ws.send(packet("EVENT_VOICE_ASSIGN_SERVER", {
reportTo: "/gateway", reportTo: "/gateway",
channel: message.data.channel channel: message.data.channel
})); }));
@ -168,7 +213,7 @@ class GatewayServer extends EventEmitter {
}); });
} }
ws.send(this.packet("EVENT_VOICE_CONNECTION_ANSWER", { ws.send(packet("EVENT_VOICE_CONNECTION_ANSWER", {
answer: await ws.rtc.answer(offer) answer: await ws.rtc.answer(offer)
})); }));
@ -225,9 +270,4 @@ GatewayServer.prototype.authMessage = function(ws) {
return true; return true;
}; };
GatewayServer.prototype.packet = function(op, data) {
if (typeof op === "string") op = getOpcodeByName(op);
return `${op}${opcodeSeparator}${JSON.stringify(data)}`;
};
module.exports = GatewayServer; module.exports = GatewayServer;

View file

@ -4,6 +4,9 @@ const opcodes = {
2: { name: "YOO_ACK", data: "JSON" }, 2: { name: "YOO_ACK", data: "JSON" },
3: { name: "ACTION_CREATE_MESSAGE", data: "JSON" }, 3: { name: "ACTION_CREATE_MESSAGE", data: "JSON" },
4: { name: "EVENT_CREATE_MESSAGE", data: "JSON" }, 4: { name: "EVENT_CREATE_MESSAGE", data: "JSON" },
5: { name: "ACTION_UPDATE_STATUS", data: "JSON" },
6: { name: "EVENT_CHANNEL_MEMBERS", data: "JSON" },
7: { name: "EVENT_CHANNEL_MEMBER_UPDATE", data: "JSON" },
21: { name: "ACTION_VOICE_REQUEST_SESSION", data: "JSON" }, 21: { name: "ACTION_VOICE_REQUEST_SESSION", data: "JSON" },
22: { name: "EVENT_VOICE_ASSIGN_SERVER", data: "JSON" }, 22: { name: "EVENT_VOICE_ASSIGN_SERVER", data: "JSON" },
23: { name: "ACTION_VOICE_CONNECTION_REQUEST", data: "JSON" }, 23: { name: "ACTION_VOICE_CONNECTION_REQUEST", data: "JSON" },
@ -45,4 +48,9 @@ const getOpcodeByName = (name) => {
for (const [key, value] of Object.entries(opcodes)) if (value.name === name) return key; for (const [key, value] of Object.entries(opcodes)) if (value.name === name) return key;
}; };
module.exports = { opcodes, parseMessage, opcodeSeparator, getOpcodeByName }; const packet = (op, data) => {
if (typeof op === "string") op = getOpcodeByName(op);
return `${op}${opcodeSeparator}${JSON.stringify(data)}`;
};
module.exports = { opcodes, parseMessage, opcodeSeparator, getOpcodeByName, packet };

View file

@ -1,6 +1,6 @@
module.exports = { module.exports = {
experiments: { experiments: {
voiceSFUTesting: false, voiceSFUTesting: false,
userListTest: true userListTest: false
} }
}; };