149 lines
3.9 KiB
TypeScript
149 lines
3.9 KiB
TypeScript
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}`);
|
|
});
|