package proton import ( "context" "fmt" "log/slog" "time" "prism/service/config" "prism/service/credentials" "prism/service/delivery" "github.com/emersion/hydroxide/protonmail" ) const ( pollInterval = 30 * time.Second prismTopic = "Proton Mail" protonAPIURL = "https://mail.proton.me/api" protonAppVersion = "Other" ) type Monitor struct { cfg *config.Config dispatcher *delivery.Publisher logger *slog.Logger credStore *credentials.Store client *protonmail.Client eventID string unseenMessageIDs map[string]time.Time startTime time.Time consecutiveErrs int lastConnected time.Time } func NewMonitor(cfg *config.Config, logger *slog.Logger) *Monitor { return &Monitor{ cfg: cfg, logger: logger, } } func (m *Monitor) Start(ctx context.Context, credStore *credentials.Store, publisher *delivery.Publisher) error { m.dispatcher = publisher if err := m.authenticateAndSetup(credStore); err != nil { return err } if m.client == nil { return nil } m.startTime = time.Now() m.lastConnected = time.Now() m.unseenMessageIDs = make(map[string]time.Time) go m.pollEvents(ctx) return nil } func (m *Monitor) pollEvents(ctx context.Context) { ticker := time.NewTicker(pollInterval) cleanupTicker := time.NewTicker(1 * time.Hour) defer ticker.Stop() defer cleanupTicker.Stop() for { select { case <-ctx.Done(): return case <-ticker.C: if err := m.checkEventsWithRetry(ctx); err != nil { m.logger.Error("Failed to check events after retries", "error", err, "consecutive_errors", m.consecutiveErrs) m.consecutiveErrs++ } else { if m.consecutiveErrs > 0 { m.logger.Info("Proton connection recovered", "downtime", time.Since(m.lastConnected).String()) m.consecutiveErrs = 0 } m.lastConnected = time.Now() } case <-cleanupTicker.C: m.cleanupOldMessages() } } } func (m *Monitor) checkEventsWithRetry(ctx context.Context) error { maxRetries := 3 baseDelay := 1 * time.Second for attempt := 0; attempt < maxRetries; attempt++ { if attempt > 0 { delay := baseDelay * time.Duration(1<