Initial release — Crucix Intelligence Engine v2.0.0
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>
This commit is contained in:
123
apis/sources/gdelt.mjs
Normal file
123
apis/sources/gdelt.mjs
Normal file
@@ -0,0 +1,123 @@
|
||||
// GDELT — Global Database of Events, Language, and Tone
|
||||
// No auth required. Updates every 15 minutes. Monitors news in 100+ languages.
|
||||
// DOC 2.0 API: full-text search across last 3 months of global news
|
||||
// GEO 2.0 API: geolocation mapping of events
|
||||
|
||||
import { safeFetch } from '../utils/fetch.mjs';
|
||||
|
||||
const BASE = 'https://api.gdeltproject.org/api/v2';
|
||||
|
||||
// Search recent global events/articles by keyword
|
||||
export async function searchEvents(query = '', opts = {}) {
|
||||
const {
|
||||
mode = 'ArtList', // ArtList, TimelineVol, TimelineVolInfo, TimelineTone, TimelineLang, TimelineSourceCountry
|
||||
maxRecords = 75,
|
||||
timespan = '24h', // e.g. "24h", "7d", "3m"
|
||||
format = 'json',
|
||||
sortBy = 'DateDesc', // DateDesc, DateAsc, ToneDesc, ToneAsc
|
||||
} = opts;
|
||||
|
||||
// If no query, use broad geopolitical terms
|
||||
const q = query || 'conflict OR crisis OR military OR sanctions OR war OR economy';
|
||||
const params = new URLSearchParams({
|
||||
query: q,
|
||||
mode,
|
||||
maxrecords: String(maxRecords),
|
||||
timespan,
|
||||
format,
|
||||
sort: sortBy,
|
||||
});
|
||||
|
||||
return safeFetch(`${BASE}/doc/doc?${params}`);
|
||||
}
|
||||
|
||||
// Get tone/sentiment timeline for a topic
|
||||
export async function toneTrend(query, timespan = '7d') {
|
||||
const params = new URLSearchParams({
|
||||
query,
|
||||
mode: 'TimelineTone',
|
||||
timespan,
|
||||
format: 'json',
|
||||
});
|
||||
return safeFetch(`${BASE}/doc/doc?${params}`);
|
||||
}
|
||||
|
||||
// Get volume timeline for a topic (how much coverage)
|
||||
export async function volumeTrend(query, timespan = '7d') {
|
||||
const params = new URLSearchParams({
|
||||
query,
|
||||
mode: 'TimelineVol',
|
||||
timespan,
|
||||
format: 'json',
|
||||
});
|
||||
return safeFetch(`${BASE}/doc/doc?${params}`);
|
||||
}
|
||||
|
||||
// GEO API — geographic event mapping
|
||||
export async function geoEvents(query = '', opts = {}) {
|
||||
const {
|
||||
mode = 'PointData',
|
||||
timespan = '24h',
|
||||
format = 'GeoJSON',
|
||||
maxPoints = 500,
|
||||
} = opts;
|
||||
|
||||
const q = query || 'conflict OR military OR protest OR explosion';
|
||||
const params = new URLSearchParams({
|
||||
query: q,
|
||||
mode,
|
||||
timespan,
|
||||
format,
|
||||
maxpoints: String(maxPoints),
|
||||
});
|
||||
|
||||
return safeFetch(`${BASE}/geo/geo?${params}`);
|
||||
}
|
||||
|
||||
// Compact article for briefing
|
||||
function compactArticle(a) {
|
||||
return {
|
||||
title: a.title,
|
||||
url: a.url,
|
||||
date: a.seendate,
|
||||
domain: a.domain,
|
||||
language: a.language,
|
||||
country: a.sourcecountry,
|
||||
};
|
||||
}
|
||||
|
||||
// GDELT rate limit: 1 request per 5 seconds
|
||||
function delay(ms) { return new Promise(r => setTimeout(r, ms)); }
|
||||
|
||||
// Briefing mode — get top global events summary (sequential due to rate limit)
|
||||
export async function briefing() {
|
||||
// Single broad query to stay within rate limits
|
||||
const all = await searchEvents(
|
||||
'conflict OR military OR economy OR crisis OR war OR sanctions OR tariff OR strike OR outbreak',
|
||||
{ maxRecords: 50, timespan: '24h' }
|
||||
);
|
||||
|
||||
const articles = (all?.articles || []).map(compactArticle);
|
||||
|
||||
// Categorize by keyword matching in titles
|
||||
const categorize = (keywords) => articles.filter(a =>
|
||||
keywords.some(k => a.title?.toLowerCase().includes(k))
|
||||
);
|
||||
|
||||
return {
|
||||
source: 'GDELT',
|
||||
timestamp: new Date().toISOString(),
|
||||
totalArticles: articles.length,
|
||||
allArticles: articles,
|
||||
conflicts: categorize(['military', 'conflict', 'war', 'strike', 'missile', 'attack', 'bomb', 'troops']),
|
||||
economy: categorize(['economy', 'recession', 'inflation', 'market', 'sanctions', 'tariff', 'trade', 'gdp']),
|
||||
health: categorize(['pandemic', 'outbreak', 'epidemic', 'disease', 'virus', 'health']),
|
||||
crisis: categorize(['crisis', 'disaster', 'emergency', 'refugee', 'famine']),
|
||||
};
|
||||
}
|
||||
|
||||
// Run standalone
|
||||
if (process.argv[1]?.endsWith('gdelt.mjs')) {
|
||||
const data = await briefing();
|
||||
console.log(JSON.stringify(data, null, 2));
|
||||
}
|
||||
Reference in New Issue
Block a user