rss2twitter/app/rss/notify.go

115 lines
2.4 KiB
Go
Raw Normal View History

2018-12-05 06:40:31 +01:00
package rss
import (
"context"
"net/http"
"sync"
2018-12-05 06:40:31 +01:00
"time"
2019-01-08 10:16:29 +01:00
log "github.com/go-pkgz/lgr"
2018-12-05 06:40:31 +01:00
"github.com/mmcdole/gofeed"
2019-01-08 20:18:44 +01:00
"github.com/pkg/errors"
2018-12-05 06:40:31 +01:00
)
// Notify on RSS change
type Notify struct {
Feed string
Duration time.Duration
Timeout time.Duration
2018-12-05 06:40:31 +01:00
once sync.Once
2018-12-05 06:40:31 +01:00
ctx context.Context
cancel context.CancelFunc
}
// Event from RSS
type Event struct {
ChanTitle string
Title string
Link string
2019-01-08 21:39:32 +01:00
Text string
2019-01-09 08:51:55 +01:00
GUID string
2018-12-05 06:40:31 +01:00
}
// Go starts notifier and returns events channel
func (n *Notify) Go(ctx context.Context) <-chan Event {
log.Printf("[INFO] start notifier for %s, every %s", n.Feed, n.Duration)
n.once.Do(func() { n.ctx, n.cancel = context.WithCancel(ctx) })
2018-12-05 06:40:31 +01:00
ch := make(chan Event)
// wait for duration, can be terminated by ctx
waitOrCancel := func(ctx context.Context) bool {
select {
case <-ctx.Done():
return false
case <-time.After(n.Duration):
return true
}
}
2018-12-05 06:40:31 +01:00
go func() {
2018-12-05 06:40:31 +01:00
defer func() {
close(ch)
n.cancel()
}()
2018-12-05 06:40:31 +01:00
fp := gofeed.NewParser()
fp.Client = &http.Client{Timeout: n.Timeout}
2019-01-08 10:38:16 +01:00
log.Printf("[DEBUG] notifier uses http timeout %v", n.Timeout)
lastGUID := ""
2018-12-05 06:40:31 +01:00
for {
feedData, err := fp.ParseURL(n.Feed)
2018-12-05 06:40:31 +01:00
if err != nil {
2019-01-08 10:23:02 +01:00
log.Printf("[WARN] failed to fetch/parse url from %s, %v", n.Feed, err)
if !waitOrCancel(n.ctx) {
return
}
2018-12-05 06:40:31 +01:00
continue
}
event, err := n.feedEvent(feedData)
2019-01-09 08:51:55 +01:00
if lastGUID != event.GUID && err == nil {
2019-01-09 01:26:22 +01:00
if lastGUID != "" { // don't notify on initial change
2019-01-09 08:51:55 +01:00
log.Printf("[INFO] new event %s - %s", event.GUID, event.Title)
2018-12-05 06:40:31 +01:00
ch <- event
2019-01-08 10:23:02 +01:00
} else {
2019-01-09 08:51:55 +01:00
log.Printf("[INFO] ignore first event %s - %s", event.GUID, event.Title)
2018-12-05 06:40:31 +01:00
}
2019-01-09 08:51:55 +01:00
lastGUID = event.GUID
2018-12-05 06:40:31 +01:00
}
if !waitOrCancel(n.ctx) {
2019-01-08 10:23:02 +01:00
log.Print("[WARN] notifier canceled")
2018-12-05 06:40:31 +01:00
return
}
}
}()
return ch
}
// Shutdown notifier
func (n *Notify) Shutdown() {
2019-01-08 10:23:02 +01:00
log.Print("[DEBUG] shutdown initiated")
2018-12-05 06:40:31 +01:00
n.cancel()
<-n.ctx.Done()
}
2019-01-08 10:16:29 +01:00
// feedEvent gets latest item from rss feed
func (n *Notify) feedEvent(feed *gofeed.Feed) (e Event, err error) {
if len(feed.Items) == 0 {
return e, errors.New("no items in rss feed")
}
if feed.Items[0].GUID == "" {
return e, errors.Errorf("no guid for rss entry %+v", feed.Items[0])
2018-12-05 06:40:31 +01:00
}
e.ChanTitle = feed.Title
e.Title = feed.Items[0].Title
e.Link = feed.Items[0].Link
2019-01-09 23:17:04 +01:00
e.Text = feed.Items[0].Description
2019-01-09 08:51:55 +01:00
e.GUID = feed.Items[0].GUID
return e, nil
2018-12-05 06:40:31 +01:00
}