newsbot/index.js
2026-04-14 08:37:57 +02:00

132 lines
4.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ============================================================
// index.js — noctura newsbot
// Matrix Bot der täglich News-Feeds postet
// ============================================================
const { MatrixClient, SimpleFsStorageProvider, AutojoinRoomsMixin } = require('matrix-bot-sdk');
const RSSParser = require('rss-parser');
const cron = require('node-cron');
const { FEEDS, MAX_ITEMS_PER_FEED, HOURS_LOOKBACK } = require('./feeds');
const { formatDailyDigest, formatNoNews, formatError } = require('./formatter');
// ---- Konfiguration aus Umgebungsvariablen ----
const HOMESERVER = process.env.MATRIX_HOMESERVER || 'http://localhost:8008';
const ACCESS_TOKEN = process.env.MATRIX_ACCESS_TOKEN;
const ROOM_ID = process.env.MATRIX_ROOM_ID;
const CRON_SCHEDULE= process.env.CRON_SCHEDULE || '0 8 * * *'; // täglich 08:00 Uhr
if (!ACCESS_TOKEN) { console.error('❌ MATRIX_ACCESS_TOKEN fehlt!'); process.exit(1); }
if (!ROOM_ID) { console.error('❌ MATRIX_ROOM_ID fehlt!'); process.exit(1); }
// ---- Matrix Client Setup ----
const storage = new SimpleFsStorageProvider('/data/bot-storage.json');
const client = new MatrixClient(HOMESERVER, ACCESS_TOKEN, storage);
AutojoinRoomsMixin.setupOnClient(client);
const parser = new RSSParser({
timeout: 10000,
headers: { 'User-Agent': 'noctura-newsbot/1.0' }
});
// ---- RSS Feed abrufen ----
async function fetchFeed(feed) {
try {
const parsed = await parser.parseURL(feed.url);
const cutoff = Date.now() - HOURS_LOOKBACK * 60 * 60 * 1000;
return parsed.items
.filter(item => {
const pubDate = item.pubDate ? new Date(item.pubDate).getTime() : Date.now();
return pubDate >= cutoff;
})
.slice(0, MAX_ITEMS_PER_FEED)
.map(item => ({
title: item.title?.trim() || 'Kein Titel',
link: item.link || item.guid || '',
summary: item.contentSnippet
? item.contentSnippet.slice(0, 120).trim() + '…'
: '',
feedName: feed.name,
category: feed.category,
pubDate: item.pubDate
}));
} catch (err) {
console.error(`⚠️ Feed-Fehler [${feed.name}]: ${err.message}`);
return [];
}
}
// ---- Alle Feeds abrufen & gruppieren ----
async function fetchAllNews() {
console.log('🔍 Feeds werden abgerufen...');
const results = await Promise.allSettled(FEEDS.map(fetchFeed));
const grouped = {};
results.forEach((result, i) => {
if (result.status !== 'fulfilled') return;
const items = result.value;
items.forEach(item => {
if (!grouped[item.category]) grouped[item.category] = [];
grouped[item.category].push(item);
});
});
return grouped;
}
// ---- Nachricht in Matrix Room senden ----
async function sendToMatrix(plain, html) {
await client.sendMessage(ROOM_ID, {
msgtype: 'm.text',
body: plain,
format: 'org.matrix.custom.html',
formatted_body: html
});
console.log('✅ Digest erfolgreich gesendet!');
}
// ---- Haupt-Job ----
async function runDigest() {
console.log(`\n⏰ [${new Date().toISOString()}] Täglicher Digest wird erstellt...`);
try {
const grouped = await fetchAllNews();
const totalItems = Object.values(grouped).flat().length;
console.log(`📊 ${totalItems} Artikel gefunden`);
if (totalItems === 0) {
const { plain, html } = formatNoNews();
await sendToMatrix(plain, html);
} else {
const { plain, html } = formatDailyDigest(grouped);
await sendToMatrix(plain, html);
}
} catch (err) {
console.error('❌ Fehler beim Digest:', err);
}
}
// ---- Bot starten ----
async function main() {
console.log('🚀 noctura newsbot startet...');
console.log(` Homeserver: ${HOMESERVER}`);
console.log(` Room: ${ROOM_ID}`);
console.log(` Cron: ${CRON_SCHEDULE}`);
await client.start();
console.log('✅ Matrix Client verbunden!');
// Cron Job registrieren
cron.schedule(CRON_SCHEDULE, runDigest, { timezone: 'Europe/Berlin' });
console.log(`📅 Cron Job aktiv: ${CRON_SCHEDULE} (Europe/Berlin)`);
// Beim Start einmal sofort ausführen wenn TEST_RUN gesetzt
if (process.env.TEST_RUN === 'true') {
console.log('🧪 TEST_RUN aktiv einmalig sofort ausführen...');
await runDigest();
}
}
main().catch(err => {
console.error('❌ Bot-Fehler:', err);
process.exit(1);
});