package sources import ( "crypto/sha1" "fmt" "net/http" "time" pubsub "github.com/alash3al/go-pubsub" "github.com/gorilla/mux" "github.com/mmcdole/gofeed" "github.com/rs/zerolog/log" "code.chrissexton.org/cws/lameralert/config" "code.chrissexton.org/cws/lameralert/event" ) type RSS struct { broker *pubsub.Broker router *mux.Router config *config.Config configRoot string url string name string } func NewRSS(c *config.Config, r *mux.Router, name, url string) *RSS { rss := RSS{ broker: pubsub.NewBroker(), router: r, config: c, url: url, name: name, configRoot: fmt.Sprintf("rss.%s", name), } go rss.serve() return &rss } func (rss *RSS) GetSender() *pubsub.Broker { return rss.broker } func (rss *RSS) GetTopics() []string { return []string{rss.name} } func (rss *RSS) pingHard(w http.ResponseWriter, r *http.Request) { rss.check(true) http.Error(w, "", http.StatusNoContent) } func (rss *RSS) ping(w http.ResponseWriter, r *http.Request) { rss.check(false) http.Error(w, "", http.StatusNoContent) } func (rss *RSS) serve() { rss.router.HandleFunc("/force", rss.pingHard) rss.router.HandleFunc("", rss.ping) for { wait := rss.config.GetInt(rss.configRoot+".wait", 900) t := time.NewTimer(time.Duration(wait) * time.Second) log.Debug().Msgf("%s waiting %v", rss.configRoot+".wait", wait) <-t.C log.Debug().Msgf("%s checking after %v", rss.name, wait) rss.check(false) } } func in(item string, slice []string) bool { for _, it := range slice { if item == it { return true } } return false } func hash(item string) string { return fmt.Sprintf("%x", sha1.Sum([]byte(item))) } func (rss *RSS) check(force bool) { fp := gofeed.NewParser() feed, err := fp.ParseURL(rss.url) if err != nil { log.Error().Err(err).Msg("error getting RSS") return } cache := rss.config.GetStringSlice(rss.configRoot+".cache", []string{}) newCache := []string{} if force { cache = []string{} } for _, it := range feed.Items { h := hash(it.Title + it.Description) newCache = append(newCache, h) if !in(h, cache) { payload := map[string]string{ "title": it.Title, "description": it.Description, "content": it.Content, "published": it.Published, } ev := event.Event{ Time: time.Now(), Payload: payload, } rss.broker.Broadcast(ev, rss.name) } } rss.config.Set(rss.configRoot+".cache", newCache) }