window.modules.register("GatewayClient", () => { const messageSchema = { t: "number", d: "object" }; const messageTypes = { HELLO: 0, YOO: 1, READY: 2, EVENT: 3 }; class GatewayClient { constructor(gatewayPath) { this.gatewayPath = gatewayPath; this.ws = null; this.token = null; this.user = null; this.onEvent = (e) => {}; this.onConnected = () => {}; this.onDisconnected = () => {}; } connect(token) { if (!token) token = this.token; console.log("gateway: connecting"); this.ws = new WebSocket(this.gatewayPath); this.ws.addEventListener("message", ({ data }) => { if (typeof data !== "string") { console.warn("gateway: got non-string data from server, ignoring..."); return; } let message; try { message = JSON.parse(data); } catch(e) { console.warn("gateway: got invalid JSON from server (failed to parse), ignoring..."); return; } if (!this._checkMessageSchema(message)) { console.warn("gateway: got invalid JSON from server (does not match schema), ignoring..."); return; } switch (message.t) { case messageTypes.HELLO: { console.log("gateway: HELLO"); this.ws.send(JSON.stringify({ t: messageTypes.YOO, d: { token } })); break; } case messageTypes.READY: { console.log("gateway: READY"); this.user = message.d.user; this.onConnected(); break; } case messageTypes.EVENT: { this.onEvent(message.d); break; } default: { console.warn("gateway: got invalid JSON from server (invalid type), ignoring..."); return; } } }); this.ws.addEventListener("open", () => { console.log("gateway: open"); }); this.ws.addEventListener("close", ({ code }) => { console.log("gateway: closed"); this.onDisconnected(code); if (code === 4001) { console.log(`gateway: disconnect code is ${code} (bad auth), will not attempt reconnect`); return; } setTimeout(() => { console.log("gateway: reconnecting"); this.connect(token); }, 4000); }); } _checkMessageSchema(message) { for (const [key, value] of Object.entries(message)) { if (!messageSchema[key]) return false; if (typeof value !== messageSchema[key]) return false; } return true; } } return GatewayClient; });