package ingestion import ( "context" "fmt" "html" "log/slog" "net/http" "regexp" "strconv" "strings" "time" "unicode/utf8" "github.com/mmcdole/gofeed" ext "github.com/mmcdole/gofeed/extensions" "pete/internal/safehttp" ) const userAgent = "Pete/1.0 (newsbot; +https://github.com/reala-misaki/pete)" // maxFeedBytes caps how much of a feed body we'll buffer into the parser, so a // hostile feed streaming an endless body within the request timeout can't OOM // the process. Generous headroom — real RSS/Atom feeds are well under this. const maxFeedBytes = 16 << 20 var feedClient = safehttp.NewClient(30 * time.Second) // FeedItem represents a parsed RSS item ready for routing. type FeedItem struct { GUID string Headline string Lede string Content string // full article text from content:encoded, block structure preserved; "" when the feed ships only a snippet ImageURL string ArticleURL string Source string DirectRoute string Language string // per-item (or dc:language) when present; "" otherwise PublishedAt int64 // unix seconds from RSS pubDate / Atom published (or updated as fallback); 0 if absent or unparseable } var ( htmlTagRe = regexp.MustCompile(`<[^>]*>`) wsRe = regexp.MustCompile(`\s+`) imgSrcRe = regexp.MustCompile(`(?i)]+src=["']([^"']+)["']`) // nonContentElemRe strips whole