From e5d6f4c02b757c83244ba5e04fead08623a27299 Mon Sep 17 00:00:00 2001 From: Alexander Neonxp Kiryukhin Date: Sat, 14 Mar 2026 00:44:19 +0300 Subject: =?UTF-8?q?=D0=BD=D0=B0=D1=87=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=B9?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B8=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/source/feed.go | 95 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 internal/source/feed.go (limited to 'internal/source') diff --git a/internal/source/feed.go b/internal/source/feed.go new file mode 100644 index 0000000..31f8535 --- /dev/null +++ b/internal/source/feed.go @@ -0,0 +1,95 @@ +package source + +import ( + "context" + "errors" + "fmt" + "log/slog" + "time" + + "github.com/mmcdole/gofeed" + cm "go.neonxp.ru/conf/model" + "go.neonxp.ru/pose/internal/model" +) + +var ( + ErrNoFeedAddress = errors.New("no feed address") +) + +type Feed struct { + logger *slog.Logger + feed string + scrapeInterval time.Duration + feedParser *gofeed.Parser +} + +func NewFeed(cfg cm.Group, logger *slog.Logger) (*Feed, error) { + feedSource := cfg.Get("url").String() + if feedSource == "" { + return nil, ErrNoFeedAddress + } + durStr := cfg.Get("scrape_interval").String() + if durStr == "" { + durStr = "10m" + } + dur, err := time.ParseDuration(durStr) + if err != nil { + return nil, fmt.Errorf("failed parse duration at source: %w", err) + } + return &Feed{ + feed: feedSource, + scrapeInterval: dur, + feedParser: gofeed.NewParser(), + }, nil +} + +func (a *Feed) Retrive(ctx context.Context) <-chan model.Item { + out := make(chan model.Item) + ticker := time.NewTicker(a.scrapeInterval) + go func() { + <-ctx.Done() + ticker.Stop() + }() + go func() { + defer close(out) + for _, item := range a.scrapeFeed(ctx) { + out <- item + } + for range ticker.C { + for _, item := range a.scrapeFeed(ctx) { + out <- item + } + } + }() + + return out +} + +func (a *Feed) scrapeFeed(ctx context.Context) []model.Item { + feed, err := a.feedParser.ParseURLWithContext(a.feed, ctx) + if err != nil { + a.logger.Error("failed parse feed", slog.Any("err", err)) + return nil + } + result := make([]model.Item, 0, len(feed.Items)) + for _, it := range feed.Items { + date := time.Now() + if it.PublishedParsed != nil { + date = *it.PublishedParsed + } + image := "" + if it.Image != nil { + image = it.Image.URL + } + result = append(result, model.Item{ + ID: it.GUID, + Date: date, + Title: it.Title, + Summary: it.Description, + Link: it.Link, + Img: image, + }) + } + + return result +} -- cgit v1.2.3