← back to Abrams Report
scrapers/scrape-rss.js
70 lines
'use strict';
const Parser = require('rss-parser');
const crypto = require('crypto');
const { upsertHeadline, logScrape } = require('./db');
const { RSS_SOURCES } = require('./sources');
const parser = new Parser({
timeout: 15000,
headers: {
'User-Agent': 'AbramsDrReport/1.0 (+https://abramsreport.agentabrams.com)',
},
});
function makeUid(source, url) {
return crypto.createHash('sha1').update(source + '|' + url).digest('hex').slice(0, 16);
}
function isBreaking(title) {
return /breaking|emergency|urgent|just in/i.test(title);
}
function isDeveloping(title) {
return /developing|exclusive|update:|report:/i.test(title);
}
async function scrapeRss(src) {
let feed;
try {
feed = await parser.parseURL(src.rssUrl);
} catch (err) {
console.error(`[rss] ${src.key} failed: ${err.message}`);
logScrape(src.key, 0, err.message);
return 0;
}
let added = 0;
for (const item of (feed.items || [])) {
const title = (item.title || '').trim();
const url = item.link || item.guid;
if (!title || !url) continue;
// Headlines + links only — no excerpt, no images (copyright safe)
const uid = makeUid(src.key, url);
const pubDate = item.pubDate ? Math.floor(new Date(item.pubDate).getTime() / 1000) : null;
added += upsertHeadline({
uid,
title,
url,
source: src.name,
category: src.category,
pub_date: pubDate,
is_breaking: isBreaking(title) ? 1 : 0,
is_developing: isDeveloping(title) ? 1 : 0,
is_top: 0,
});
}
logScrape(src.key, added);
return added;
}
async function scrapeAllRss() {
const results = [];
for (const src of RSS_SOURCES) {
const count = await scrapeRss(src);
console.log(`[rss] ${src.name}: +${count}`);
results.push({ source: src.key, added: count });
}
return results;
}
module.exports = { scrapeAllRss, scrapeRss };