Ryan dd2477390c Who he is: live Wikipedia bio (auto-refreshed every 15-min crawl tick) + article link
- lib/bio.js: refreshBio() pulls the article intro via the Wikipedia API
  (exintro, descriptive UA) and upserts the bio cache; getBio() reads it
  with a short accurate static fallback. Page loads NEVER hit Wikipedia.
- crawl route: refreshBio() runs on every 15-min tick (over daily) —
  best-effort, a wiki hiccup can't fail the news crawl.
- /api/bio: read-only route.
- page.js: WhoBox renders the live text + 'Full article on Wikipedia' link
  + SOURCE: WIKIPEDIA · UPDATED <date> line.
- tests/bio-test.mjs: fallback + live-refresh + offline degradation.
2026-09-16 17:47:20 +00:00

84 lines
3.6 KiB
JavaScript

// BidaAngMamamayan — live "Who he is" bio, sourced from the Wikipedia article.
//
// The front page's "Who he is" box used to be a hard-coded paragraph (which
// drifted: it said "Francis 'Bam' Tan Aquino IV" and hand-picked a couple of
// facts). Daniel (2026-09-16): source it from the LIVE Wikipedia article,
// link to it, and keep it updated daily.
//
// How it stays fresh without slowing page loads:
// * refreshBio() is called by the crawl tick (scripts/cron-crawl every 15
// min). It fetches the article's intro via the Wikipedia API and upserts
// a single doc in the `bio` collection. That is well over "daily".
// * getBio() (used by /api/bio on page load) only READS that cached doc —
// it never calls Wikipedia, so a page load costs zero external requests
// and a Wikipedia outage can't break the site.
// * If the cache is empty or stale-but-unrefreshable, getBio() falls back to
// a short, accurate static blurb so the box is never blank.
//
// Wikipedia API: action=query, prop=extracts, exintro, explaintext,
// titles="Bam Aquino", redirects=1. Requires a descriptive User-Agent
// (Wikipedia blocks the default Node UA).
import { getCollections } from "./db.js";
const ARTICLE_TITLE = "Bam Aquino";
const WIKI_API = "https://en.wikipedia.org/w/api.php";
const WIKI_LINK = "https://en.wikipedia.org/wiki/Bam_Aquino";
const UA = "BamaDeskBot/1.0 (bidaangmamamayan.com; contact danieldiaz@boyemma.com)";
// Fallback blurb — used ONLY when the live cache is unavailable. Kept short
// and accurate; it is a last resort, not the normal path.
const FALLBACK_TEXT =
'Paolo Benigno "Bam" Aguirre Aquino IV is a Filipino politician and media ' +
"personality who has served as a senator of the Philippines since 2025. He " +
"is the first gamer to reach the Philippine Senate — a runner-up in the " +
"2025 PalaPinas esports championship — and has made the fight for college " +
"students, scholarships, and fair electricity his legislative signature.";
export async function refreshBio() {
const { bio } = await getCollections();
const params = new URLSearchParams({
action: "query",
prop: "extracts",
exintro: "1",
explaintext: "1",
titles: ARTICLE_TITLE,
format: "json",
redirects: "1",
});
const res = await fetch(`${WIKI_API}?${params}`, {
headers: { "User-Agent": UA, Accept: "application/json" },
signal: AbortSignal.timeout(15000),
});
if (!res.ok) throw new Error(`wiki ${res.status}`);
const j = await res.json();
const page = j && j.query && j.query.pages
? Object.values(j.query.pages)[0]
: null;
const text = (page && page.extract ? page.extract : "").trim();
if (!text) throw new Error("empty intro");
// Keep only the lead section (first few paragraphs) — the box is a blurb,
// not the whole article. The full text is one click away via the link.
const paragraphs = text.split(/\n\n+/).map((p) => p.trim()).filter(Boolean);
const lead = paragraphs.slice(0, 3).join(" ");
await bio.updateOne(
{ _id: "primary" },
{ $set: { _id: "primary", text: lead, full: text, url: WIKI_LINK, updatedAt: new Date() } },
{ upsert: true }
);
return { ok: true, text: lead, updatedAt: new Date() };
}
export async function getBio() {
try {
const { bio } = await getCollections();
const row = await bio.findOne({ _id: "primary" });
if (row && row.text) {
return { ok: true, text: row.text, url: row.url || WIKI_LINK, updatedAt: row.updatedAt || null, source: "Wikipedia" };
}
} catch {
/* fall through to the static blurb */
}
return { ok: true, text: FALLBACK_TEXT, url: WIKI_LINK, updatedAt: null, source: "fallback" };
}