diff options
| author | seth <[email protected]> | 2024-10-12 14:28:54 -0400 |
|---|---|---|
| committer | seth <[email protected]> | 2024-10-12 14:59:04 -0400 |
| commit | 6a2d9e752fab27b59da4f194b0ef6daf7e8b6d81 (patch) | |
| tree | ed8f9f07861a0a4463dcd910baa349b6cc6656aa /lib/teawie.ts | |
| parent | 08912b439bd61088dd849b9342a81341fa9e4a23 (diff) | |
port to deno
Diffstat (limited to 'lib/teawie.ts')
| -rw-r--r-- | lib/teawie.ts | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/lib/teawie.ts b/lib/teawie.ts new file mode 100644 index 0000000..b5c68c7 --- /dev/null +++ b/lib/teawie.ts @@ -0,0 +1,108 @@ +import { USER_AGENT } from "./consts.ts"; +import { Endpoints } from "@octokit/types"; + +const URL_CACHE_KEY = ["urls"]; +const URL_CACHE_TTL_MS = 1000 * 60 * 60; // 1 hour + +type repositoryPathContentsResponse = + Endpoints["GET /repos/{owner}/{repo}/contents/{path}"]["response"]; + +const GITHUB_API = "https://api.github.com"; + +// Teawie repository owner and name +const REPO_OWNER = "SympathyTea"; +const REPO_NAME = "Teawie-Archive"; + +// Subdirectories of the above repository containing files we want +const SUBDIRS = [ + "teawie-media/Original Teawies", + "teawie-media/Teawie Variants", + "teawie-media/Teawie in Places", + "teawie-media/Unfinished Teawies", +]; + +// File extensions we consider to be images +const IMAGE_EXTENSIONS = ["gif", "jpg", "jpeg", "png", "svg", "webp"]; + +const contentsOf = ( + path: string, +): Promise<repositoryPathContentsResponse["data"]> => + fetch(`${GITHUB_API}/repos/${REPO_OWNER}/${REPO_NAME}/contents/${path}`, { + headers: { + accept: "application/vnd.github+json", + "user-agent": USER_AGENT, + }, + }) + .then((response) => { + if (!response.ok) { + throw new Error( + `HTTP Error ${response.status}: ${response.statusText}`, + ); + } + + return response.json(); + }) + .then((json) => { + return json as repositoryPathContentsResponse["data"]; + }); + +const imageUrlsIn = ( + files: repositoryPathContentsResponse["data"], +): string[] => { + // NOTE: This is done because the response may only contain data + // for a single file's path + const filesArray = Array.isArray(files) ? files : [files]; + + return ( + filesArray + // Find *files* that are (probably) images and have a download URL + .filter( + (file) => + !Array.isArray(file) && + file.download_url && + file.type == "file" && + IMAGE_EXTENSIONS.includes( + file.name.split(".").at(-1) ?? "", + ), + ) + .map((file) => { + // Should this happen? No + // Could it? I don't know + // But let's be safe :steamhappy: + if (!file.download_url) { + throw new Error( + `Could not find download URL for file "${file.name}"`, + ); + } + + return file.download_url; + }) + ); +}; + +export const imageUrls = async (kv: Deno.Kv): Promise<string[]> => { + const cached = await kv.get(URL_CACHE_KEY); + const urls = cached.value; + if (typeof urls == "string") { + console.trace("Found Teawie URLs in cache!"); + return JSON.parse(urls); + } + + console.warn("Couldn't find Teawie URLs in cache! Fetching fresh ones"); + const fresh = await Promise.all(SUBDIRS.map(contentsOf)).then( + (responses) => { + // See the note above + const flatResponses = responses.flatMap((response) => + Array.isArray(response) ? response : [response] + ); + + return imageUrlsIn(flatResponses); + }, + ); + + await kv.set(URL_CACHE_KEY, JSON.stringify(fresh), { + expireIn: URL_CACHE_TTL_MS, + }); + + return fresh; +}; |
