6-hour cooldown after LinkedIn Easy Apply daily limit
When rate limited, writes timestamp to data/linkedin_rate_limited_at.json. Subsequent runs skip LinkedIn until 6 hours have passed. Other platforms (Wellfound) continue unaffected. Cooldown file auto-deleted on expiry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@ loadEnv(); // load .env before anything else
|
|||||||
* Reads jobs queue and applies using the appropriate handler per apply_type
|
* Reads jobs queue and applies using the appropriate handler per apply_type
|
||||||
* Run via cron or manually: node job_applier.mjs [--preview]
|
* Run via cron or manually: node job_applier.mjs [--preview]
|
||||||
*/
|
*/
|
||||||
import { existsSync, writeFileSync, createWriteStream } from 'fs';
|
import { existsSync, readFileSync, writeFileSync, createWriteStream } from 'fs';
|
||||||
import { dirname, resolve } from 'path';
|
import { dirname, resolve } from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
@@ -127,8 +127,29 @@ async function main() {
|
|||||||
const sortedPlatforms = Object.entries(byPlatform)
|
const sortedPlatforms = Object.entries(byPlatform)
|
||||||
.sort((a, b) => (platformOrder.indexOf(a[0]) ?? 99) - (platformOrder.indexOf(b[0]) ?? 99));
|
.sort((a, b) => (platformOrder.indexOf(a[0]) ?? 99) - (platformOrder.indexOf(b[0]) ?? 99));
|
||||||
let timedOut = false;
|
let timedOut = false;
|
||||||
|
const RATE_LIMIT_COOLDOWN_MS = 6 * 60 * 60 * 1000; // 6 hours
|
||||||
|
const rateLimitPath = resolve(__dir, 'data/linkedin_rate_limited_at.json');
|
||||||
|
|
||||||
for (const [platform, platformJobs] of sortedPlatforms) {
|
for (const [platform, platformJobs] of sortedPlatforms) {
|
||||||
if (timedOut) break;
|
if (timedOut) break;
|
||||||
|
|
||||||
|
// Skip LinkedIn if rate limited within cooldown window
|
||||||
|
if (platform === 'linkedin' && existsSync(rateLimitPath)) {
|
||||||
|
try {
|
||||||
|
const { at } = JSON.parse(readFileSync(rateLimitPath, 'utf8'));
|
||||||
|
const elapsed = Date.now() - at;
|
||||||
|
if (elapsed < RATE_LIMIT_COOLDOWN_MS) {
|
||||||
|
const hoursLeft = ((RATE_LIMIT_COOLDOWN_MS - elapsed) / 3600000).toFixed(1);
|
||||||
|
console.log(`\n--- LINKEDIN (${platformJobs.length} jobs) ---\n`);
|
||||||
|
console.log(` ⏸️ Easy Apply daily limit — ${hoursLeft}h cooldown remaining, skipping LinkedIn`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Cooldown expired — remove the file
|
||||||
|
const { unlinkSync } = await import('fs');
|
||||||
|
unlinkSync(rateLimitPath);
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`\n--- ${platform.toUpperCase()} (${platformJobs.length} jobs) ---\n`);
|
console.log(`\n--- ${platform.toUpperCase()} (${platformJobs.length} jobs) ---\n`);
|
||||||
let browser;
|
let browser;
|
||||||
let platformProfileName = null;
|
let platformProfileName = null;
|
||||||
@@ -243,7 +264,8 @@ async function main() {
|
|||||||
await browser?.browser?.close().catch(() => {});
|
await browser?.browser?.close().catch(() => {});
|
||||||
}
|
}
|
||||||
if (results.rate_limited) {
|
if (results.rate_limited) {
|
||||||
await sendTelegram(settings, `⚠️ *LinkedIn Easy Apply daily limit reached* — skipping LinkedIn, continuing other platforms.`).catch(() => {});
|
writeFileSync(rateLimitPath, JSON.stringify({ at: Date.now() }));
|
||||||
|
await sendTelegram(settings, `⚠️ *LinkedIn Easy Apply daily limit reached* — pausing LinkedIn for 6 hours, continuing other platforms.`).catch(() => {});
|
||||||
results.rate_limited = false; // Reset so other platforms can proceed
|
results.rate_limited = false; // Reset so other platforms can proceed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user