diff --git a/5.ts b/5.ts new file mode 100644 index 0000000..efca2c2 --- /dev/null +++ b/5.ts @@ -0,0 +1,82 @@ +import { BufReader } from "https://deno.land/std@0.157.0/io/buffer.ts"; +import { copy } from "https://deno.land/std@0.156.0/streams/conversion.ts"; + +const PORT = 5588; +const PROXY_CHAT_SERVER = "chat.protohackers.com"; +const PROXY_CHAT_PORT = 16963; +const TONYS_ADDRESS = "7YWHMfk9JZe0LM0g1ZauHuiSxhI"; + +const tEnc = new TextEncoder(); + +console.log(`Starting TCP listener on on 0.0.0.0:${PORT}`); + +async function chatSession(conn: Deno.Conn) { + const proxy = await Deno.connect({ + hostname: PROXY_CHAT_SERVER, + port: PROXY_CHAT_PORT, + }); + + const closeOnDisconnect = () => { + console.warn("Disconnected client"); + try { + proxy.close(); + } catch (err) { + console.error("Error closing proxy connection:", err); + } + try { + conn.close(); + } catch (err) { + console.error("Error closing client connection:", err); + } + }; + + rewriteMessages(conn, proxy).catch((e) => e).finally(closeOnDisconnect); + rewriteMessages(proxy, conn).catch((e) => e).finally(closeOnDisconnect); +} + +const chatCommands = (reader: Deno.Conn) => ({ + async *[Symbol.asyncIterator]() { + let s = await BufReader.create(reader).readString("\n"); + if (!s) return; + while (s) { + console.log(s); + yield s; + s = await BufReader.create(reader).readString("\n"); + } + }, +}); + +async function rewriteMessages(reader: Deno.Conn, writer: Deno.Conn) { + try { + for await (const rawLine of chatCommands(reader)) { + if (!rawLine || !rawLine.endsWith("\n")) throw "incomplete chat command"; + const line = rawLine.trim(); + console.debug("Line:", line); + await writer.write(tEnc.encode(replaceAddresses(line) + "\n")); + } + } catch (err) { + console.error("Error reading line:", err, { ...err }); + throw err; + } +} + +function replaceAddresses(text: string): string { + const result = text.split(" ").map((word) => replaceAddress(word)).join(" "); + if (result != text) console.debug("replaced text:", result); + return result; +} + +function replaceAddress(word: string): string { + const result = word.replace( + /^7\w{25,34}$/i, + TONYS_ADDRESS, + ); + if (word != result) console.debug("word:", word, "replaced:", result); + return result; +} + +for await (const conn of Deno.listen({ port: PORT })) { + console.log("Connection established:", conn.remoteAddr); + chatSession(conn); + console.log("Connection closed:", conn.remoteAddr); +}