From 3a53d585ffdf7f4be47eb61113d7cf5b5de7b91c Mon Sep 17 00:00:00 2001 From: hippoz Date: Thu, 10 Jun 2021 14:02:31 +0300 Subject: [PATCH] add session counters and presence (brainlet-react presence is locked behind an experiment flag, just enable it with an override at the moment) --- brainlet/api/v2/gateway/index.js | 62 +++++++++++++++++++----- brainlet/api/v2/gateway/messageparser.js | 10 +++- brainlet/experiments.js | 2 +- 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/brainlet/api/v2/gateway/index.js b/brainlet/api/v2/gateway/index.js index 8e0a5ad..0e9f4c3 100644 --- a/brainlet/api/v2/gateway/index.js +++ b/brainlet/api/v2/gateway/index.js @@ -6,7 +6,7 @@ const werift = require("werift"); const { experiments } = require("../../../experiments"); const User = require("../../../models/User"); const Channel = require("../../../models/Channel"); -const { parseMessage, opcodeSeparator, getOpcodeByName } = require("./messageparser"); +const { parseMessage, packet } = require("./messageparser"); const { checkToken } = require("../../../common/auth/authfunctions"); const pingCheckDelay = 10000; @@ -43,6 +43,7 @@ class GatewayServer extends EventEmitter { constructor({ server }) { super(); this.wss = new websockets.Server({ server: server, path: "/gateway" }); + this.sessionCounters = {}; this.pingInterval = setInterval(() => { this.wss.clients.forEach((client) => { @@ -61,7 +62,7 @@ class GatewayServer extends EventEmitter { this.wss.on("connection", (ws) => { // Send HELLO message as soon as the client connects - ws.send(this.packet("HELLO", {})); + ws.send(packet("HELLO", {})); ws.session = { authenticated: false, user: null, @@ -73,6 +74,21 @@ class GatewayServer extends EventEmitter { ws.alive = true; }); 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(); }); ws.on("message", async (data) => { @@ -87,6 +103,12 @@ class GatewayServer extends EventEmitter { ws.session.user = user; 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 // TODO: This is probably not efficient @@ -96,7 +118,30 @@ class GatewayServer extends EventEmitter { 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`); } catch (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 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, channel: { _id: message.data.channel._id @@ -133,7 +178,7 @@ class GatewayServer extends EventEmitter { case "ACTION_VOICE_REQUEST_SESSION": { if (!experiments.voiceSFUTesting) return; // 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", 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) })); @@ -225,9 +270,4 @@ GatewayServer.prototype.authMessage = function(ws) { return true; }; -GatewayServer.prototype.packet = function(op, data) { - if (typeof op === "string") op = getOpcodeByName(op); - return `${op}${opcodeSeparator}${JSON.stringify(data)}`; -}; - module.exports = GatewayServer; diff --git a/brainlet/api/v2/gateway/messageparser.js b/brainlet/api/v2/gateway/messageparser.js index d2cad7e..217e819 100644 --- a/brainlet/api/v2/gateway/messageparser.js +++ b/brainlet/api/v2/gateway/messageparser.js @@ -4,6 +4,9 @@ const opcodes = { 2: { name: "YOO_ACK", data: "JSON" }, 3: { name: "ACTION_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" }, 22: { name: "EVENT_VOICE_ASSIGN_SERVER", 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; }; -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 }; diff --git a/brainlet/experiments.js b/brainlet/experiments.js index f27a15f..2971298 100644 --- a/brainlet/experiments.js +++ b/brainlet/experiments.js @@ -1,6 +1,6 @@ module.exports = { experiments: { voiceSFUTesting: false, - userListTest: true + userListTest: false } };