From 1ae519267ef2bfd043d425a12fb791bffea2a4d9 Mon Sep 17 00:00:00 2001 From: mehbark Date: Wed, 25 Oct 2023 17:23:38 -0400 Subject: [PATCH] hs2-last-updated: woohoo! num updates --- serverside/hs2-last-updated.ts | 95 +++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 25 deletions(-) diff --git a/serverside/hs2-last-updated.ts b/serverside/hs2-last-updated.ts index aba3e4a..528de4a 100644 --- a/serverside/hs2-last-updated.ts +++ b/serverside/hs2-last-updated.ts @@ -4,9 +4,12 @@ // okay aaaa hs2 will end that will be sad and good i guess but // not important right now +// would be neat (but painful) to have it actually roll like an analog thing + import { contentType } from "https://deno.land/std@0.202.0/media_types/mod.ts"; import { DOMParser, + NodeList, NodeType, } from "https://deno.land/x/deno_dom@v0.1.38/deno-dom-wasm.ts"; import { @@ -17,7 +20,32 @@ import { // not sure how worthwhile this optimization is but const dom_parser = new DOMParser(); -async function get_last_update_date(): Promise { +const murica_short_date = new Intl.DateTimeFormat("en-US", { + month: "numeric", + day: "numeric", + year: "numeric", +}); + +function get_updates_for_date( + { date, ps }: { date: Date; ps: NodeList }, +): number { + let count = 0; + + const looking_for = murica_short_date.format(date) + " - "; + + ps.forEach((p) => { + if ( + p.childNodes[0] && p.childNodes[0].nodeType == NodeType.TEXT_NODE && + p.childNodes[0].textContent == looking_for + ) count++; + }); + + return count; +} + +async function get_last_update_date(): Promise< + { last_updated: Date; last_update_count: number } | string +> { try { const res = await fetch("https://homestuck2.com/log"); const body = await res.text(); @@ -27,11 +55,13 @@ async function get_last_update_date(): Promise { const doc = dom_parser.parseFromString(body, "text/html"); if (!doc) return "couldn't parse the body into the DOM"; - const p = doc.querySelector("p"); - if (!p) return "couldn't get a p from the doc"; + const ps = doc.querySelectorAll("p"); + if (!ps) return "couldn't get the ps from the doc"; + + if (!ps[0]) return "couldn't get even a single p from the doc (bad!)"; // should really enable strict indexing - const us_date_node = p.childNodes[0]; + const us_date_node = ps[0].childNodes[0]; if (!us_date_node || us_date_node.nodeType != NodeType.TEXT_NODE) { return "couldn't get a date node from the log entry"; } @@ -42,7 +72,10 @@ async function get_last_update_date(): Promise { if (Number.isNaN(date.valueOf())) { return `got an invalid date :(. the text_content was '${us_date}'`; } - return date; + return { + last_updated: date, + last_update_count: get_updates_for_date({ date, ps }), + }; } catch (e) { return `caught error: ${e}`; } @@ -51,9 +84,9 @@ async function get_last_update_date(): Promise { async function check_again() { last_checked = new Date(); - const new_date = await get_last_update_date(); - if (typeof new_date == "string") { - console.log(`failed to get a date when checking 😬: '${new_date}'`); + const stuff = await get_last_update_date(); + if (typeof stuff == "string") { + console.log(`failed to get a date when checking 😬: '${stuff}'`); console.log( "^ ironically enough, this is more likely when there *is* an update", ); @@ -62,7 +95,8 @@ async function check_again() { ); return; } - last_updated = new_date; + last_updated = stuff.last_updated; + last_update_count = stuff.last_update_count; } function days_since_update(): number { @@ -73,6 +107,7 @@ function days_since_update(): number { // silly, i know let last_updated: Date = new Date(0); let last_checked: Date = new Date(); +let last_update_count = 0; await check_again(); // *definitely* a worthwhile optimization @@ -81,9 +116,8 @@ const font = await fetch("https://static.pyrope.net/courier-std-bold.otf") .then((r) => r.arrayBuffer()) .then((b) => new Uint8Array(b)); -function make_image(): Image { - const days = Math.floor(days_since_update()).toString().padStart(3, "0"); - console.log(days_since_update()); +function make_image(n: number): Image { + const days = Math.floor(n).toString().padStart(3, "0"); const image = new Image(106, 44); const text = Image.renderText( font, @@ -96,22 +130,33 @@ function make_image(): Image { return image.composite(text, -5); } -async function update_image() { - image = await make_image().encode(); +async function update_images() { + [last_updated_image, last_update_count_image] = await Promise.all([ + make_image(days_since_update()).encode(), + make_image(last_update_count).encode(), + ]); } -let image: Uint8Array = new Uint8Array(); -await update_image(); +let last_updated_image: Uint8Array = new Uint8Array(); +let last_update_count_image: Uint8Array = new Uint8Array(); +await update_images(); -Deno.serve({ port: 61264 }, async () => { - if (new Date().getTime() - last_checked.getTime() >= 60 * 1000) { - await check_again(); - await update_image(); - } - - return new Response(image, { +const image_response = (data: Uint8Array): Response => + new Response(data, { headers: { "Content-Type": contentType("png") }, }); -}); -await Deno.writeFile("/tmp/poop.png", image); +Deno.serve({ port: 61264 }, async (req) => { + const url = new URL(req.url); + if ((new Date().getTime() - last_checked.getTime()) >= 60 * 1000) { + await check_again(); + await update_images(); + } + + // could use URLPattern but that would be overkill for this + if (url.pathname.match("count")) { + return image_response(last_update_count_image); + } else { + return image_response(last_updated_image); + } +});