package notifier import ( "context" "fmt" "log" "os" "time" "codeberg.org/snonux/gorum/internal/config" ) func Start(ctx context.Context, conf config.Config, scoreCh <-chan string) { log.Println("notifier: Starting") changedCh := make(chan email, 1) errorCh := make(chan email, 1) update := func(ch chan email, email email) { // Replace (update) current element in the channel. select { case <-ch: default: } ch <- email } go func() { for scoresStr := range scoreCh { if err := persistToDisk(conf, scoresStr); err != nil { update(errorCh, email{"GORUM error", err.Error()}) } update(changedCh, email{"GORUM changed", scoresStr}) } }() go sendEmail(ctx, "change report", conf, changedCh) go sendEmail(ctx, "error report", conf, errorCh) } func sendEmail(ctx context.Context, what string, conf config.Config, ch <-chan email) { throttleDuration := time.Duration(conf.MailThrottle) * time.Second for { select { case email := <-ch: if err := email.send(conf); err != nil { log.Println(err) } case <-ctx.Done(): return } select { case <-time.After(throttleDuration): log.Println("notifier:", what, "slept some time", throttleDuration) case <-ctx.Done(): return } } } func persistToDisk(conf config.Config, scoresStr string) error { if _, err := os.Stat(conf.StateDir); os.IsNotExist(err) { if err := os.MkdirAll(conf.StateDir, 0755); err != nil { return err } } return writeFileViaTmp(fmt.Sprintf("%s/%s", conf.StateDir, conf.ScoreFile), scoresStr) } func writeFileViaTmp(filePath, content string) error { tmpFilePath := fmt.Sprintf("%s.tmp", filePath) fd, err := os.Create(tmpFilePath) if err != nil { return err } defer fd.Close() if _, err := fd.WriteString(content); err != nil { return err } if err := fd.Sync(); err != nil { return err } return os.Rename(tmpFilePath, filePath) }