| 1 | // Originally written for https://lol.zip on May 16, 2023 |
| 2 | // Now hosted on https://paperclover.net/lol.zip |
| 3 | // See https://paperclover.net/q+a/2512200208 |
| 4 | import { Hono } from "hono"; |
| 5 | import sourceText from "./lines.txt?text"; |
| 6 | |
| 7 | const encoder = new TextEncoder(); |
| 8 | |
| 9 | const SPACE = 32; |
| 10 | const NEWLINE = 10; |
| 11 | |
| 12 | let stats_total = 0; |
| 13 | let stats_currentlySending = 0; |
| 14 | |
| 15 | let start: Uint8Array | null = null; |
| 16 | let chunks: Uint8Array[] = []; |
| 17 | let last = 0; |
| 18 | |
| 19 | const buffer = encoder.encode(sourceText); |
| 20 | for (let i = 0; i < buffer.length; i++) { |
| 21 | if (buffer[i] === NEWLINE) { |
| 22 | if (start === null) { |
| 23 | start = buffer.subarray(last, i + 1); |
| 24 | } else { |
| 25 | chunks.push(buffer.subarray(last, i + 1)); |
| 26 | } |
| 27 | buffer[i] = SPACE; |
| 28 | last = i + 1; |
| 29 | } |
| 30 | } |
| 31 | const length = chunks.length; |
| 32 | |
| 33 | function shuffle<T>(array: T[]): T[] { |
| 34 | let currentIndex = array.length, |
| 35 | randomIndex; |
| 36 | let val; |
| 37 | while (currentIndex != 0) { |
| 38 | randomIndex = Math.floor(Math.random() * currentIndex); |
| 39 | currentIndex--; |
| 40 | val = array[currentIndex]!; |
| 41 | array[currentIndex] = array[randomIndex]!; |
| 42 | array[randomIndex] = val; |
| 43 | } |
| 44 | return array; |
| 45 | } |
| 46 | |
| 47 | export const app = new Hono(); |
| 48 | |
| 49 | app.get("/lol.zip", (c) => { |
| 50 | const userAgent = c.req.header("User-Agent") ?? ""; |
| 51 | |
| 52 | // if known search engine, return a 404 |
| 53 | if ( |
| 54 | userAgent.includes("Googlebot") |
| 55 | || userAgent.includes("bingbot") |
| 56 | || userAgent.includes("Discordbot") |
| 57 | ) { |
| 58 | return c.text("Not Found", 404); |
| 59 | } |
| 60 | |
| 61 | let cleanup: VoidFunction | null = null; |
| 62 | const stream = new ReadableStream({ |
| 63 | async start(controller) { |
| 64 | const array = chunks.slice(); |
| 65 | shuffle(array); |
| 66 | let i = 0; |
| 67 | |
| 68 | controller.enqueue(start); |
| 69 | |
| 70 | const interval = setInterval(() => { |
| 71 | if (i >= length) { |
| 72 | shuffle(array); |
| 73 | i = 0; |
| 74 | } |
| 75 | controller.enqueue(array[i]); |
| 76 | i++; |
| 77 | }, 100); |
| 78 | |
| 79 | const timeout = setTimeout(() => { |
| 80 | clearInterval(interval); |
| 81 | controller.enqueue( |
| 82 | encoder.encode( |
| 83 | "\n\ndamn, you got to the end. congrats. you win nothing. this site closes the connection after 24 hours, since i think the joke wears off at that point.", |
| 84 | ), |
| 85 | ); |
| 86 | controller.close(); |
| 87 | stats_currentlySending--; |
| 88 | }, 86400000); |
| 89 | |
| 90 | // cleanup on abort |
| 91 | cleanup = () => { |
| 92 | clearInterval(interval); |
| 93 | clearTimeout(timeout); |
| 94 | stats_currentlySending--; |
| 95 | }; |
| 96 | }, |
| 97 | cancel() { |
| 98 | cleanup?.(); |
| 99 | }, |
| 100 | }); |
| 101 | |
| 102 | stats_currentlySending++; |
| 103 | stats_total++; |
| 104 | |
| 105 | return new Response(stream, { |
| 106 | headers: { |
| 107 | "Content-Type": "application/zip", |
| 108 | "Content-Length": "77700420420420420", |
| 109 | "Content-Disposition": "attachment; filename=lol.zip", |
| 110 | "Server": "by clover caruso <paperclover.net>", |
| 111 | "X-Source-Code": "https://git.paperclover.net/clo/sitegen/src/branch/master/src/q+a/lol.zip/", |
| 112 | }, |
| 113 | }); |
| 114 | }); |
| 115 | |
| 116 | app.get("/lol.stats", (c) => { |
| 117 | return c.json({ |
| 118 | currentlySending: stats_currentlySending, |
| 119 | total: stats_total, |
| 120 | }); |
| 121 | }); |