package inbox import ( "os" "os/signal" "syscall" "time" "github.com/dominicgisler/imap-spam-cleaner/app" "github.com/dominicgisler/imap-spam-cleaner/database" "github.com/dominicgisler/imap-spam-cleaner/imap" "github.com/dominicgisler/imap-spam-cleaner/logx" "github.com/dominicgisler/imap-spam-cleaner/provider" "github.com/go-co-op/gocron/v2" ) func Schedule(ctx app.Context) { s, err := gocron.NewScheduler() if err != nil { logx.Errorf("Could not create scheduler: %v", err) return } for i, inbox := range ctx.Config.Inboxes { logx.Infof("Scheduling inbox %s (%s)", inbox.Username, inbox.Schedule) prov, ok := ctx.Config.Providers[inbox.Provider] if !ok { logx.Errorf("Invalid provider %s for inbox %d", inbox.Provider, i) continue } if _, err = s.NewJob( gocron.CronJob(inbox.Schedule, false), gocron.NewTask(processInbox, ctx, inbox, prov), gocron.WithSingletonMode(gocron.LimitModeReschedule), ); err != nil { logx.Errorf("Could not schedule inbox %s (%s): %v", inbox.Username, inbox.Schedule, err) } } s.Start() ch := make(chan os.Signal, 1) signal.Notify(ch, os.Interrupt, syscall.SIGTERM) sig := <-ch logx.Debugf("Received %s, shutting down", sig.String()) if err = s.Shutdown(); err != nil { logx.Errorf("Could not shutdown scheduler: %v ", err) } } func RunAllInboxes(ctx app.Context) { for i, inbox := range ctx.Config.Inboxes { logx.Infof("Processing inbox %s", inbox.Username) prov, ok := ctx.Config.Providers[inbox.Provider] if !ok { logx.Errorf("Invalid provider %s for inbox %d", inbox.Provider, i) continue } processInbox(ctx, inbox, prov) } } func processInbox(ctx app.Context, inbox app.Inbox, prov app.Provider) { var err error var msgs []imap.Message var p provider.Provider var im *imap.Imap var n int var run database.Run logx.Infof("Handling %s", inbox.Username) run.StartedAt = time.Now() run.Inbox = inbox.Username if im, err = imap.New(inbox); err != nil { logx.Errorf("Could not load imap: %v\n", err) return } if msgs, err = im.LoadMessages(); err != nil { logx.Errorf("Could not load messages: %v\n", err) im.Close() return } logx.Infof("Loaded %d messages", len(msgs)) run.MessageCount = len(msgs) p, err = provider.New(prov.Type) if err != nil { logx.Errorf("Could not load provider: %v\n", err) im.Close() return } if err = p.Init(prov.Config); err != nil { logx.Errorf("Could not init provider: %v\n", err) im.Close() return } moved := 0 for _, m := range msgs { if wl, ok := ctx.Config.Whitelists[inbox.Whitelist]; ok { trustedSender := false for _, rgx := range wl { if rgx.Match([]byte(m.From)) { trustedSender = true break } } if trustedSender { logx.Debugf("Skipping message #%d (%s) because of trusted sender (%s)", m.UID, m.Subject, m.From) run.SkippedCount++ continue } } if n, err = p.Analyze(m); err != nil { logx.Errorf("Could not analyze message #%d (%s): %v\n", m.UID, m.Subject, err) run.FailedCount++ continue } logx.Debugf("Spam score of message #%d (%s): %d/100", m.UID, m.Subject, n) if n >= inbox.MinScore { if ctx.Options.AnalyzeOnly { logx.Debugf("Analyze only mode, not moving message #%d (%s)", m.UID, m.Subject) continue } if inbox.MarkRead { if err = im.MarkRead(m.UID); err != nil { logx.Errorf("Could not mark message #%d (%s) read: %v\n", m.UID, m.Subject, err) } } if err = im.MoveMessage(m.UID, inbox.Spam); err != nil { logx.Errorf("Could not move message (%s): %v\n", m.Subject, err) continue } moved++ } } logx.Infof("Moved %d messages", moved) run.MovedCount = moved im.Close() if !ctx.Options.AnalyzeOnly { run.FinishedAt = time.Now() if err = database.AddRun(&run); err != nil { logx.Errorf("Could not save run: %v\n", err) } } }