aboutsummaryrefslogtreecommitdiff
path: root/internal/source/feed.go
blob: 31f85354c1bd6f78d1feddaf3d02dc89e244484e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
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
}