package app import ( "bytes" "context" "log/slog" "os" "text/template" "time" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" "github.com/mmcdole/gofeed" "gitrepo.ru/neonxp/rss2world/templates" ) type App struct { config *Config telegram *tgbotapi.BotAPI templates *template.Template } func New(cfg *Config) (*App, error) { bot, err := tgbotapi.NewBotAPI(cfg.Telegram.BotToken) if err != nil { return nil, err } tpl, err := template.ParseFS(templates.Templates, "*.gotmpl") if err != nil { return nil, err } return &App{ config: cfg, telegram: bot, templates: tpl, }, nil } func (a *App) Run(ctx context.Context) error { ticker := time.NewTicker(a.config.RSS.CheckInterval) for { select { case <-ctx.Done(): return nil case <-ticker.C: if err := a.iteration(); err != nil { slog.ErrorContext(ctx, "failed iteration", slog.Any("error", err)) } } } } func (a *App) iteration() error { seq, err := a.readSeqFile() if os.IsNotExist(err) { seq = "" err = nil } if err != nil { return err } items, err := a.findNewItems(seq) if err != nil || len(items) == 0 { return err } for i := len(items) - 1; i >= 0; i-- { if err := a.processItem(items[i]); err != nil { return err } if err := a.writeSeqFile(items[i].GUID); err != nil { return err } } return nil } func (a *App) processItem(item *gofeed.Item) error { buf := bytes.NewBufferString("") if err := a.templates.ExecuteTemplate(buf, "telegram", item); err != nil { return err } for _, group := range a.config.Telegram.TargetGroups { msg := tgbotapi.NewMessage(group, buf.String()) msg.ParseMode = tgbotapi.ModeHTML if _, err := a.telegram.Send(msg); err != nil { return err } } return nil } func (a *App) readSeqFile() (string, error) { seqVal, err := os.ReadFile(a.config.RSS.SeqFile) if err != nil { return "", err } return string(seqVal), nil } func (a *App) writeSeqFile(seqVal string) error { return os.WriteFile(a.config.RSS.SeqFile, []byte(seqVal), 0644) } func (a *App) findNewItems(from string) ([]*gofeed.Item, error) { fp := gofeed.NewParser() feed, err := fp.ParseURL(a.config.RSS.URL) if err != nil { return nil, err } out := make([]*gofeed.Item, 0, len(feed.Items)) for _, item := range feed.Items { if item.GUID == from { break } out = append(out, item) } return out, nil }