import express from "express"; import expressWs from "express-ws"; import type { WebSocket } from "ws"; import crypto from "crypto"; import fs from "fs/promises"; interface Host { ws: WebSocket, code: string, messages: { from: "host"|"join", data: string }[], join?: Join, } interface Join { ws: WebSocket, code: string, host: Host, } const hosts: Host[] = []; const joins: Join[] = []; const app = expressWs(express()).app; const port = process.env.PORT || 3000; app.use(express.json()); app.use(express.static("public")); app.ws("/", (ws) => { ws.on("message", (msg: string) => { console.log(msg); if (msg === "host") { if (hosts.some(({ ws: ows }) => ows == ws) || joins.some(({ ws: ows }) => ows == ws)) { ws.send("failed"); return; } const code = crypto.randomBytes(4).toString("hex"); ws.send("code " + code); hosts.push({ ws, code, messages: [] }); return; } if (msg.startsWith("join ")) { if (hosts.some(({ ws: ows }) => ows == ws) || joins.some(({ ws: ows }) => ows == ws)) { ws.send("failed"); return; } const code = msg.substring(5); const host = hosts.find(({ code: oldCode }) => oldCode === code); if (!host || host.join) { ws.send("failed"); return; } ws.send("good"); const join: Join = { ws, code, host }; host.join = join; joins.push(join); } if (msg.startsWith("send ")) { const send = msg.substring(5); const host = hosts.find(({ ws: ows }) => ows == ws); const join = joins.find(({ ws: ows }) => ows == ws); if (host) { if (!host.join) { ws.send("failed"); return; } host.join.ws.send("rcv " + translateText(send, host.code)); host.messages.push({ from: "host", data: send }); return; } if (join) { join.host.ws.send("rcv " + translateText(send, join.code)); join.host.messages.push({ from: "join", data: send }); } } }); ws.on("close", () => { let index = hosts.findIndex(({ ws: thisWs }) => thisWs == ws); if (index > -1) { const host = hosts[index]; fs.appendFile( "./out.txt", `${host.code} | ${new Date().toUTCString()}\n` + host.messages.map(({ from, data }) => `${from}> ${data}`).join("\n") + "\n\n", ).catch(console.error); const join = host.join; hosts.splice(index, 1); if (join) { join.ws.send("disconnected"); index = joins.indexOf(join); if (index > -1) { joins.splice(index, 1); } } } index = joins.findIndex(({ ws: thisWs }) => thisWs == ws); if (index > -1) { const host = joins[index].host; joins.splice(index, 1); host.ws.send("disconnected"); } }); }); function translateText(msg: string, code: string): string { const shift = parseInt(code, 16); return msg .replaceAll(/[^\x20-\x7e]+/g, "") .replaceAll(/\s+/g, " ") .toLowerCase() .substring(0, 1024) .split(" ") .map(w => { let o = ""; let n = shift; for (let i = 0; i < w.length; i++) { const c = w[i]; const cc = c.charCodeAt(0); n ^= (cc * shift) >> (i % 4); n = Math.abs(n); if ("a" <= c && c <= "z") { o += String.fromCharCode(0x61 + (n % 26)); } else { const nc = n % (32 + 6 + 4); if (nc <= 32) { o += String.fromCharCode(0x21 + nc); } else if (nc <= 32 + 6) { o += String.fromCharCode(0x5B + nc - 32); } else { o += String.fromCharCode(0x7B + nc - 32 - 6); } } } return o; }) .join(" "); } app.listen(port, () => { console.log(`Server is running on port ${port}`); });