108 lines
2.9 KiB
JavaScript
108 lines
2.9 KiB
JavaScript
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();
|