package fetch import ( "context" "fmt" "time" "github.com/mmcdole/gofeed" "gitea.konchin.com/service/stonks/internal/config" ) func init() { Register("rss", newRSSFetcher) } // rssParams is the plugin-specific config for the rss fetcher. type rssParams struct { Feeds []string `yaml:"feeds"` // MaxItemsPerFeed caps how many of the newest entries are kept per feed. // Defaults to 10. MaxItemsPerFeed int `yaml:"max_items_per_feed"` // MaxAgeHours drops entries older than this many hours. Defaults to 48. // Feeds with entries lacking a published date are kept regardless. MaxAgeHours int `yaml:"max_age_hours"` } // rssFetcher pulls general market news headlines from a configured list of // RSS feeds. Free, no API key. type rssFetcher struct { feeds []string maxItemsPerFeed int maxAge time.Duration parser *gofeed.Parser } func newRSSFetcher(cfg config.PluginConfig) (Fetcher, error) { params := rssParams{ MaxItemsPerFeed: 10, MaxAgeHours: 48, } if err := cfg.DecodeParams(¶ms); err != nil { return nil, err } if len(params.Feeds) == 0 { return nil, fmt.Errorf("rss: at least one feed URL must be configured") } return &rssFetcher{ feeds: params.Feeds, maxItemsPerFeed: params.MaxItemsPerFeed, maxAge: time.Duration(params.MaxAgeHours) * time.Hour, parser: gofeed.NewParser(), }, nil } func (f *rssFetcher) Name() string { return "rss" } func (f *rssFetcher) Fetch(ctx context.Context, _ config.Targets) (Result, error) { var result Result var errs []error cutoff := time.Now().Add(-f.maxAge) for _, feedURL := range f.feeds { feed, err := f.parser.ParseURLWithContext(feedURL, ctx) if err != nil { errs = append(errs, fmt.Errorf("rss %s: %w", feedURL, err)) continue } count := 0 for _, item := range feed.Items { if count >= f.maxItemsPerFeed { break } if item.PublishedParsed != nil && item.PublishedParsed.Before(cutoff) { continue } var published time.Time if item.PublishedParsed != nil { published = *item.PublishedParsed } result.News = append(result.News, NewsItem{ Title: item.Title, Source: feed.Title, URL: item.Link, Published: published, Summary: item.Description, }) count++ } } return result, joinErrs(errs) }