← back to Ventura Claw

server/connectors/wikipedia.js

62 lines

// Wikipedia / Wikimedia REST API. No key.
// https://en.wikipedia.org/api/rest_v1/
const BASE = "https://en.wikipedia.org/api/rest_v1";
async function call(path) {
  const r = await fetch(`${BASE}${path}`, {
    headers: { "User-Agent": "VenturaClaw/1.0 (steve@venturaclaw.com)", Accept: "application/json" },
    signal: AbortSignal.timeout(15_000),
  });
  if (!r.ok) throw new Error(`wikipedia ${r.status}`);
  return r.json();
}
module.exports = {
  meta: { id: "wikipedia", name: "Wikipedia", category: "data", docsUrl: "https://en.wikipedia.org/api/rest_v1/", auth: "none", realImpl: true },
  fields: [],
  configured() { return true; },
  async health() {
    try {
      const d = await call("/page/summary/Designer_Wallcoverings");
      return { ok: true, label: "live · " + (d.title || "english wikipedia") };
    } catch (e) {
      // Fallback to a guaranteed page
      try {
        await call("/page/summary/Earth");
        return { ok: true, label: "live · english wikipedia" };
      } catch (e2) { return { ok: false, reason: e2.message }; }
    }
  },
  actions: {
    async "summary"({ title }) {
      if (!title) throw new Error("title required");
      const d = await call(`/page/summary/${encodeURIComponent(title.replace(/\s+/g, "_"))}`);
      return {
        title: d.title, description: d.description, extract: d.extract,
        thumbnail: d.thumbnail?.source, url: d.content_urls?.desktop?.page,
        wikibase_item: d.wikibase_item, type: d.type, lang: d.lang,
      };
    },
    async "search"({ q, limit }) {
      if (!q) throw new Error("q required");
      const qs = new URLSearchParams({ q, limit: String(Math.min(limit || 10, 50)) });
      const r = await fetch(`https://en.wikipedia.org/w/rest.php/v1/search/page?${qs}`, {
        headers: { "User-Agent": "VenturaClaw/1.0", Accept: "application/json" },
        signal: AbortSignal.timeout(15_000),
      });
      if (!r.ok) throw new Error(`wikipedia search ${r.status}`);
      const d = await r.json();
      return {
        pages: (d.pages || []).map(p => ({
          id: p.id, title: p.title, excerpt: p.excerpt?.replace(/<[^>]+>/g, ""), thumbnail: p.thumbnail?.url,
          url: `https://en.wikipedia.org/wiki/${encodeURIComponent(p.key)}`,
        })),
      };
    },
    async "onthisday"({ type, month, day }) {
      const t = type || "events";
      const m = String(month || (new Date().getMonth() + 1)).padStart(2, "0");
      const d = String(day || new Date().getDate()).padStart(2, "0");
      return await call(`/feed/onthisday/${t}/${m}/${d}`);
    },
  },
};