// Package watch implements waiting/polling for new free game offers. package watch import ( "context" "log" "time" "gogs.netdome.biz/paul/cheapo/internal/epic" ) // Notifier is called for every game that newly became free. type Notifier interface { Notify(games []epic.Game) } // NotifierFunc adapts a function to Notifier. type NotifierFunc func(games []epic.Game) func (f NotifierFunc) Notify(games []epic.Game) { f(games) } // Watcher polls the client and reports games that were not seen before. type Watcher struct { Client *epic.Client Interval time.Duration seen map[string]bool } // NewWatcher creates a watcher. Interval defaults to 15m. func NewWatcher(c *epic.Client, interval time.Duration) *Watcher { if interval <= 0 { interval = 15 * time.Minute } return &Watcher{Client: c, Interval: interval, seen: map[string]bool{}} } // Run polls until ctx is done. On the first pass it reports all current free // games as "new" unless baselineOnly is true (useful to seed the seen-set // without notifying). func (w *Watcher) Run(ctx context.Context, baselineOnly bool, n Notifier) error { t := time.NewTicker(w.Interval) defer t.Stop() first := true for { games, err := w.Client.GetFreeGames(ctx) if err != nil { return err } var fresh []epic.Game for _, g := range games { if !w.seen[g.ID] { fresh = append(fresh, g) } } for _, g := range games { w.seen[g.ID] = true } if len(fresh) > 0 && !(first && baselineOnly) { n.Notify(fresh) } if first { log.Printf("watcher: baseline set with %d current free games, polling every %s", len(games), w.Interval) first = false } select { case <-ctx.Done(): return ctx.Err() case <-t.C: } } }