// 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" }; }