This commit is contained in:
Daniel Flanagan 2022-10-11 13:43:47 -05:00
parent e0a15d5333
commit b089a0b810
Signed by untrusted user: lytedev-divvy
GPG key ID: 6D69CEEE4ABBCD82

121
3.ts
View file

@ -1,25 +1,108 @@
const PORT = 5588; import { readLines } from "https://deno.land/std@0.157.0/io/buffer.ts";
console.log(`Starting UDP listener on port ${PORT}`);
const PORT = 5588;
console.log(`Starting TCP listener on on 0.0.0.0:${PORT}`);
const clients = new Map<Deno.Conn, string>();
const tDec = new TextDecoder();
const tEnc = new TextEncoder(); const tEnc = new TextEncoder();
const reservedData: Record<string, string> = { version: "KeyVal 1.0" }; async function chatSession(conn: Deno.Conn) {
const data = new Map<string, string>(); try {
let myNick: string | null = null;
const listener = Deno.listenDatagram({ port: PORT, transport: "udp" }); await sendPrompt(conn);
for await (const line of readLines(conn)) {
for await (const [bytes, addr] of listener) { console.debug("Received line:", line);
const message = tDec.decode(bytes).trim(); const message = line.trim();
console.debug("Datagram Received from:", addr, message); if (myNick == null) {
if (message.includes("=")) { if (isLegalNick(message)) {
const [key, val] = message.split("=", 1); myNick = message;
console.debug(`Setting ${key} to ${val}`); await join(myNick, conn);
if (!(key in reservedData)) data.set(key, val);
} else { } else {
const value = reservedData[message] || data.get(message) || ""; console.error("Illegal nick:", message);
const bytes = tEnc.encode(`${message}=${value}`); break;
console.debug(`Sending ${message}=${value}`); }
await listener.send(bytes, addr); } else {
const conns: Deno.Conn[] = [];
for (const themConn of clients.keys()) {
if (themConn != conn) conns.push(themConn);
}
await send(conns, `[${myNick}] ${message}`);
}
}
} finally {
if (clients.has(conn)) {
try {
await disconnect(conn);
} catch (err) {
console.error("Failed to disconnect:", err);
}
}
try {
if (conn.writable) {
send(conn, "You are being disconnected...");
}
} catch (err) {
console.error("Failed to send disconnect notice:", err);
}
}
try {
conn.close();
} catch (err) {
console.error("Failed to close connection:", err);
} }
} }
async function send(
connsArg: Deno.Conn | Array<Deno.Conn>,
text: string,
) {
const conns = !Array.isArray(connsArg) ? [connsArg] : connsArg;
const messages: Promise<number>[] = [];
const bytes = tEnc.encode(text + "\n");
conns.forEach((conn) => messages.push(conn.write(bytes)));
return await Promise.all(messages);
}
const broadcast = async (text: string) => await send([...clients.keys()], text);
const LEGAL_NICK_REGEXP = /^[a-z0-9]+$/i;
const isLegalNick = (nick: string) => {
console.debug(
`Checking nick: ${nick} (Bytes: ${tEnc.encode(nick)})`,
);
return LEGAL_NICK_REGEXP.exec(nick) != null;
};
const sendPrompt = async (conn: Deno.Conn) => {
console.debug("Sending prompt...");
return await send(conn, "Who you be?");
};
async function join(myNick: string, conn: Deno.Conn) {
const userList: string[] = [];
clients.forEach((nick, _) => userList.push(nick));
await broadcast(`* ${myNick} joined`);
clients.set(conn, myNick);
await send(conn, `* users: ${userList.join(", ")}`);
}
async function disconnect(conn: Deno.Conn) {
const myNick = clients.get(conn);
clients.delete(conn);
await broadcast(`* ${myNick} left`);
}
for await (const conn of Deno.listen({ port: PORT })) {
console.log("Connection established:", conn.remoteAddr);
try {
chatSession(conn);
} catch (e) {
console.error(
"Exception occurred during chat session:",
conn.remoteAddr,
e,
);
}
console.log("Connection closed:", conn.remoteAddr);
}