feedbender/app.ts
2025-05-15 11:47:32 +02:00

71 lines
1.9 KiB
TypeScript

import { Hono } from 'hono';
import { serve } from 'bun';
import Parser from 'rss-parser';
import { Feed, type Item } from 'feed';
const app = new Hono();
const parser = new Parser();
app.get('/group-by-day', async (c) => {
const feedUrl = c.req.query('feedUrl');
if (!feedUrl) {
return c.text('Missing feedUrl query parameter', 400);
}
try {
const { title, feedUrl: feedId, link: feedLink, items } = await parser.parseURL(feedUrl);
const grouped = items.reduce((acc, { pubDate, content, contentSnippet, summary, link }) => {
const day = new Date(pubDate as string).toISOString().slice(0, 10);
acc[day] = acc[day] || [];
acc[day].push({
link,
content: content || contentSnippet || summary || ''
});
return acc;
}, {});
const days = Object.keys(grouped).sort().reverse();
const today = new Date();
today.setHours(0, 0, 0, 0);
const feedItems: Item[] = days.map(day => {
const dateStr = new Date(day).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
return {
title: `${title} - ${dateStr}`,
id: day,
date: new Date(day),
link: grouped[day][0].link,
content: grouped[day].map((p: any) => p.content).join('\n\n---------------------\n\n')
};
}).filter(p => p.date < today);
const outFeed = new Feed({
title: title as string,
id: feedId as string,
link: feedLink,
copyright: ''
});
for (const item of feedItems) {
outFeed.addItem(item)
}
const xml = outFeed.rss2();
return c.text(xml, 200, {
'Content-Type': 'text/xml',
});
} catch (err: any) {
return c.text(`Error fetching or processing feed: ${err.message}`, 500);
}
});
// Start Bun server on port 3000
serve({
fetch: app.fetch,
port: 3000
});