const config = require("../config"); const logger = require("./logger"); class RateLimiter { constructor() { this.sentCount = 0; this.startTime = Date.now(); this.lastSentTime = null; } // Record a successful send recordSuccess() { this.sentCount++; this.lastSentTime = Date.now(); } // Get randomized delay between min and max getRandomDelay() { const baseDelay = config.app.delayMinutes * 60 * 1000; // Convert to ms const minDelay = baseDelay * 0.8; // 20% less than base const maxDelay = baseDelay * 1.2; // 20% more than base // Add additional randomization const randomFactor = Math.random() * (maxDelay - minDelay) + minDelay; // Add jitter to avoid patterns const jitter = (Math.random() - 0.5) * 30000; // +/- 30 seconds return Math.floor(randomFactor + jitter); } // Check if we should pause based on sent count shouldPause() { const hoursSinceStart = (Date.now() - this.startTime) / (1000 * 60 * 60); const emailsPerHour = this.sentCount / hoursSinceStart; // Gmail limits: ~500/day = ~20/hour // Be conservative: pause if exceeding 15/hour if (emailsPerHour > 15) { return true; } // Pause every 50 emails for 30 minutes if (this.sentCount > 0 && this.sentCount % 50 === 0) { return true; } return false; } // Get pause duration getPauseDuration() { // Standard pause: 30 minutes const basePause = 30 * 60 * 1000; // Add randomization to pause duration const randomPause = basePause + Math.random() * 10 * 60 * 1000; // +0-10 minutes return randomPause; } // Calculate next send time async getNextSendDelay() { if (this.shouldPause()) { const pauseDuration = this.getPauseDuration(); const pauseMinutes = Math.round(pauseDuration / 60000); logger.rateLimitPause( pauseDuration, `Automatic pause after ${this.sentCount} emails` ); console.log(`Rate limit pause: ${pauseMinutes} minutes`); return pauseDuration; } return this.getRandomDelay(); } // Get human-readable time formatDelay(ms) { const minutes = Math.floor(ms / 60000); const seconds = Math.floor((ms % 60000) / 1000); return `${minutes}m ${seconds}s`; } // Reset counters (for testing) reset() { this.sentCount = 0; this.startTime = Date.now(); this.lastSentTime = null; } // Get current stats getStats() { const runtime = (Date.now() - this.startTime) / 1000; // seconds const avgRate = this.sentCount / (runtime / 3600); // emails per hour return { sentCount: this.sentCount, runtime: Math.floor(runtime / 60), // minutes averageRate: avgRate.toFixed(1), nextDelay: this.formatDelay(this.getRandomDelay()), }; } } module.exports = new RateLimiter();