Files
intelligence-terminal/apis/sources/who.mjs
Ketchalegend ba9e93679f feat: add news sources, 30-day filter, and fix ticker performance
- Fix Al Jazeera RSS URL (feeds.aljazeera.com is dead, moved to www.aljazeera.com)
- Add 8 new RSS sources: DW, France 24, Euronews, DW Africa, RFI, Africa News,
  NYT Africa, NPR — covering Germany, Europe, Africa, Cameroon region, and USA
- Filter WHO outbreak news and ticker feed to last 30 days (drops stale alerts
  like 733-day-old WHO DONs)
- Fix ticker causing Chrome to crash: add will-change:transform and
  contain:layout style for GPU compositing, reduce DOM nodes from 80 to 40
- Add badge styles for new source categories (DW, EU, Africa)
2026-03-19 08:01:20 -07:00

94 lines
3.0 KiB
JavaScript

// WHO — World Health Organization Global Health Observatory
// No auth required. Disease outbreak monitoring.
import { safeFetch } from '../utils/fetch.mjs';
const GHO_BASE = 'https://ghoapi.azureedge.net/api';
const DON_API = 'https://www.who.int/api/news/diseaseoutbreaknews';
// Get GHO indicator data
export async function getIndicator(code, opts = {}) {
const { filter = '', top = 20 } = opts;
let url = `${GHO_BASE}/${code}?$top=${top}&$orderby=TimeDim desc`;
if (filter) url += `&$filter=${filter}`;
return safeFetch(url);
}
// Key health indicators
const INDICATORS = {
MDG_0000000020: 'TB incidence (per 100k)',
MALARIA_EST_CASES: 'Malaria estimated cases',
WHOSIS_000001: 'Life expectancy at birth',
UHC_INDEX_REPORTED: 'UHC Service Coverage Index',
};
// Get Disease Outbreak News via WHO JSON API
// The old RSS feed at /feeds/entity/don/en/rss.xml returns 404.
// This JSON endpoint returns ~50 items; OData $orderby is ignored by
// the server, so we sort client-side by PublicationDate descending.
export async function getOutbreakNews() {
try {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 15000);
const res = await fetch(DON_API, {
signal: controller.signal,
headers: { 'User-Agent': 'Crucix/1.0' },
});
clearTimeout(timer);
if (!res.ok) {
const body = await res.text().catch(() => '');
throw new Error(`HTTP ${res.status}: ${body.slice(0, 200)}`);
}
const data = await res.json();
const items = data?.value || [];
// Sort by PublicationDate descending (server ignores $orderby)
items.sort((a, b) => {
const da = new Date(a.PublicationDate || 0);
const db = new Date(b.PublicationDate || 0);
return db - da;
});
// Filter to last 30 days only
const cutoff = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
const recent = items.filter(item => new Date(item.PublicationDate || 0) >= cutoff);
return recent.map(item => ({
title: item.Title,
date: item.PublicationDate,
donId: item.DonId || null,
url: item.ItemDefaultUrl
? `https://www.who.int/emergencies/disease-outbreak-news${item.ItemDefaultUrl}`
: null,
summary: (item.Summary || item.Overview || '').replace(/<[^>]*>/g, '').slice(0, 300) || null,
}));
} catch (e) {
return { error: e.message };
}
}
// Briefing
export async function briefing() {
const outbreaks = await getOutbreakNews();
return {
source: 'WHO',
timestamp: new Date().toISOString(),
diseaseOutbreakNews: Array.isArray(outbreaks) ? outbreaks.slice(0, 15) : [],
outbreakError: Array.isArray(outbreaks) ? null : outbreaks.error,
monitoringCapabilities: [
'Disease Outbreak News (DONs)',
'Global health indicators (GHO)',
'Pandemic early warning signals',
'Cross-reference with GDELT health event mentions',
],
};
}
if (process.argv[1]?.endsWith('who.mjs')) {
const data = await briefing();
console.log(JSON.stringify(data, null, 2));
}