import * as cheerio from 'cheerio'; /** RSS 2.0 / Atom feed parsing (SPEC §1) — used for auction calendars, new-arrival feeds and news. */ export interface FeedItem { id: string | null; title: string; link: string | null; publishedAt: Date | null; summary: string | null; content: string | null; categories: string[]; imageUrl: string | null; } export function parseFeed(xml: string): { title: string | null; items: FeedItem[] } { const $ = cheerio.load(xml, { xml: true }); const items: FeedItem[] = []; const date = (s: string | undefined) => { if (!s) return null; const d = new Date(s.trim()); return Number.isNaN(d.getTime()) ? null : d; }; if ($('feed').length) { $('feed > entry').each((_, el) => { const e = $(el); const link = e.find('link[rel="alternate"]').attr('href') ?? e.find('link').first().attr('href') ?? null; const img = e.find('media\\:content, media\\:thumbnail, enclosure').first().attr('url') ?? null; items.push({ id: e.find('id').first().text().trim() || null, title: e.find('title').first().text().trim(), link, publishedAt: date(e.find('published').first().text() || e.find('updated').first().text()), summary: e.find('summary').first().text().trim() || null, content: e.find('content').first().text().trim() || null, categories: e.find('category').map((_, c) => $(c).attr('term') ?? $(c).text()).get().filter(Boolean), imageUrl: img }); }); return { title: $('feed > title').first().text().trim() || null, items }; } $('item').each((_, el) => { const e = $(el); const img = e.find('media\\:content, media\\:thumbnail, enclosure').first().attr('url') ?? null; items.push({ id: e.find('guid').first().text().trim() || null, title: e.find('title').first().text().trim(), link: e.find('link').first().text().trim() || null, publishedAt: date(e.find('pubDate').first().text() || e.find('dc\\:date').first().text()), summary: e.find('description').first().text().trim() || null, content: e.find('content\\:encoded').first().text().trim() || null, categories: e.find('category').map((_, c) => $(c).text()).get().filter(Boolean), imageUrl: img }); }); return { title: $('channel > title').first().text().trim() || null, items }; }