26-source OSINT intelligence engine with live Jarvis dashboard, auto-refresh via SSE, optional LLM layer (4 providers), delta/memory system, and Telegram breaking news alerts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
90 lines
2.8 KiB
JavaScript
90 lines
2.8 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;
|
|
});
|
|
|
|
return items.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));
|
|
}
|