- 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.
84 lines
3.6 KiB
JavaScript
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" };
|
|
}
|