mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
113 lines
3.2 KiB
JavaScript
113 lines
3.2 KiB
JavaScript
import { fetchRegionalText, fetchRegionalJson } from './http.js';
|
|
import { normalizeRegionalArticles } from '../../../src/data/regionalModel.js';
|
|
|
|
function decodeRssText(value) {
|
|
return String(value || '')
|
|
.replace(/<!\[CDATA\[([\s\S]*?)\]\]>/g, '$1')
|
|
.replace(/&/g, '&')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, "'")
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/<[^>]+>/g, ' ')
|
|
.replace(/\s+/g, ' ')
|
|
.trim();
|
|
}
|
|
|
|
function rssTag(block, tag) {
|
|
return decodeRssText(
|
|
new RegExp(`<${tag}(?:\\s[^>]*)?>([\\s\\S]*?)<\\/${tag}>`, 'i').exec(
|
|
block,
|
|
)?.[1] || '',
|
|
);
|
|
}
|
|
|
|
function normalizeRssArticles(xml, limit = 5) {
|
|
const seen = new Set();
|
|
const articles = [];
|
|
for (const match of String(xml || '').matchAll(
|
|
/<item>([\s\S]*?)<\/item>/gi,
|
|
)) {
|
|
const item = match[1];
|
|
const title = rssTag(item, 'title').slice(0, 180);
|
|
const url = rssTag(item, 'link');
|
|
let parsedUrl;
|
|
try {
|
|
parsedUrl = new URL(url);
|
|
} catch {
|
|
continue;
|
|
}
|
|
if (!title || !['http:', 'https:'].includes(parsedUrl.protocol)) continue;
|
|
const source = rssTag(item, 'source');
|
|
const signature = `${title.toLowerCase()}|${source.toLowerCase() || parsedUrl.hostname}`;
|
|
if (seen.has(signature)) continue;
|
|
seen.add(signature);
|
|
const rawDate = rssTag(item, 'pubDate');
|
|
articles.push({
|
|
title,
|
|
url: parsedUrl.href,
|
|
domain: source || parsedUrl.hostname.replace(/^www\./, ''),
|
|
publishedAt: Number.isNaN(Date.parse(rawDate))
|
|
? null
|
|
: new Date(rawDate).toISOString(),
|
|
sourceCountry: null,
|
|
});
|
|
if (articles.length >= limit) break;
|
|
}
|
|
return articles;
|
|
}
|
|
|
|
async function fetchRegionalNews(place) {
|
|
const query = place?.locality || place?.region || place?.country;
|
|
if (!query)
|
|
return { status: 'unavailable', query: null, articles: [], source: null };
|
|
const rssParams = new URLSearchParams({
|
|
q: String(query).replace(/["\\]/g, ' ').trim(),
|
|
hl: 'en-US',
|
|
gl: 'US',
|
|
ceid: 'US:en',
|
|
});
|
|
try {
|
|
const xml = await fetchRegionalText(
|
|
`https://news.google.com/rss/search?${rssParams}`,
|
|
{
|
|
headers: { 'User-Agent': 'GodsEyeView/0.1' },
|
|
timeoutMs: 12_000,
|
|
},
|
|
);
|
|
const articles = normalizeRssArticles(xml, 5);
|
|
if (articles.length)
|
|
return { status: 'ready', query, articles, source: 'Google News RSS' };
|
|
} catch {
|
|
/* fall through to the existing free index */
|
|
}
|
|
const params = new URLSearchParams({
|
|
query: `"${String(query).replace(/["\\]/g, ' ').trim()}"`,
|
|
mode: 'artlist',
|
|
format: 'json',
|
|
maxrecords: '5',
|
|
sort: 'datedesc',
|
|
timespan: '48h',
|
|
});
|
|
try {
|
|
const payload = await fetchRegionalJson(
|
|
`https://api.gdeltproject.org/api/v2/doc/doc?${params}`,
|
|
{
|
|
headers: { 'User-Agent': 'GodsEyeView/0.1' },
|
|
timeoutMs: 12_000,
|
|
},
|
|
);
|
|
const articles = normalizeRegionalArticles(payload, 5);
|
|
return {
|
|
status: articles.length ? 'ready' : 'empty',
|
|
query,
|
|
articles,
|
|
source: 'GDELT fallback',
|
|
};
|
|
} catch {
|
|
return { status: 'unavailable', query, articles: [], source: null };
|
|
}
|
|
}
|
|
|
|
export { fetchRegionalNews };
|