118 lines
3.5 KiB
TypeScript
118 lines
3.5 KiB
TypeScript
|
// once a minute (what's the point if it's out of date!)
|
||
|
// do days since last update (0 will be obvious then), should (please please please let this be true) only need three digits MAX
|
||
|
// until hs2 ends of course but let's not think about that now
|
||
|
// okay aaaa hs2 will end that will be sad and good i guess but
|
||
|
// not important right now
|
||
|
|
||
|
import { contentType } from "https://deno.land/std@0.202.0/media_types/mod.ts";
|
||
|
import {
|
||
|
DOMParser,
|
||
|
NodeType,
|
||
|
} from "https://deno.land/x/deno_dom@v0.1.38/deno-dom-wasm.ts";
|
||
|
import {
|
||
|
Image,
|
||
|
TextLayout,
|
||
|
} from "https://deno.land/x/imagescript@1.2.15/ImageScript.js";
|
||
|
|
||
|
// not sure how worthwhile this optimization is but
|
||
|
const dom_parser = new DOMParser();
|
||
|
|
||
|
async function get_last_update_date(): Promise<Date | string> {
|
||
|
try {
|
||
|
const res = await fetch("https://homestuck2.com/log");
|
||
|
const body = await res.text();
|
||
|
if (!body) return "couldn't get a string body";
|
||
|
|
||
|
// could just regex for the date lol
|
||
|
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";
|
||
|
|
||
|
// should really enable strict indexing
|
||
|
const us_date_node = p.childNodes[0];
|
||
|
if (!us_date_node || us_date_node.nodeType != NodeType.TEXT_NODE) {
|
||
|
return "couldn't get a date node from the log entry";
|
||
|
}
|
||
|
|
||
|
const us_date = us_date_node.textContent.replace(" - ", "");
|
||
|
// apparently doing new Date("10/8/2023") is implementation defined which is icky but it works with deno
|
||
|
const date = new Date(us_date);
|
||
|
if (Number.isNaN(date.valueOf())) {
|
||
|
return `got an invalid date :(. the text_content was '${us_date}'`;
|
||
|
}
|
||
|
return date;
|
||
|
} catch (e) {
|
||
|
return `caught error: ${e}`;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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}'`);
|
||
|
console.log(
|
||
|
"^ ironically enough, this is more likely when there *is* an update",
|
||
|
);
|
||
|
console.log(
|
||
|
"^ unironically, this is *most* likely the result of a bug",
|
||
|
);
|
||
|
return;
|
||
|
}
|
||
|
last_updated = new_date;
|
||
|
}
|
||
|
|
||
|
function days_since_update(): number {
|
||
|
const millis = new Date().getTime() - last_updated.getTime();
|
||
|
return millis / (24 * 60 * 60 * 1000);
|
||
|
}
|
||
|
|
||
|
// silly, i know
|
||
|
let last_updated: Date = new Date(0);
|
||
|
let last_checked: Date = new Date();
|
||
|
await check_again();
|
||
|
|
||
|
// *definitely* a worthwhile optimization
|
||
|
// (still inefficient to go over http...)
|
||
|
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());
|
||
|
const image = new Image(106, 44);
|
||
|
const text = Image.renderText(
|
||
|
font,
|
||
|
64,
|
||
|
days,
|
||
|
0x00_00_00_ff,
|
||
|
new TextLayout({ verticalAlign: "center", horizontalAlign: "middle" }),
|
||
|
);
|
||
|
|
||
|
return image.composite(text, -5);
|
||
|
}
|
||
|
|
||
|
async function update_image() {
|
||
|
image = await make_image().encode();
|
||
|
}
|
||
|
|
||
|
let image: Uint8Array = new Uint8Array();
|
||
|
await update_image();
|
||
|
|
||
|
Deno.serve({ port: 61264 }, async () => {
|
||
|
if (new Date().getTime() - last_checked.getTime() >= 60 * 1000) {
|
||
|
await check_again();
|
||
|
await update_image();
|
||
|
}
|
||
|
|
||
|
return new Response(image, {
|
||
|
headers: { "Content-Type": contentType("png") },
|
||
|
});
|
||
|
});
|
||
|
|
||
|
await Deno.writeFile("/tmp/poop.png", image);
|