Initial commit
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user