Enable Wellfound apply: fix missing apply_type, add submit verification
- Search now sets apply_type: 'wellfound' on discovered jobs (was missing, so applier silently skipped all Wellfound jobs) - Add Wellfound to DEFAULT_ENABLED_APPLY_TYPES (LinkedIn first, then Wellfound) - Sort platform processing order: linkedin → wellfound → external - Apply handler: add closed listing detection, submit verification, error handling on meta extraction Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -32,7 +32,7 @@ import {
|
|||||||
APPLY_RUN_TIMEOUT_MS, PER_JOB_TIMEOUT_MS
|
APPLY_RUN_TIMEOUT_MS, PER_JOB_TIMEOUT_MS
|
||||||
} from './lib/constants.mjs';
|
} from './lib/constants.mjs';
|
||||||
|
|
||||||
const DEFAULT_ENABLED_APPLY_TYPES = ['easy_apply'];
|
const DEFAULT_ENABLED_APPLY_TYPES = ['easy_apply', 'wellfound'];
|
||||||
|
|
||||||
const isPreview = process.argv.includes('--preview');
|
const isPreview = process.argv.includes('--preview');
|
||||||
|
|
||||||
@@ -120,9 +120,12 @@ async function main() {
|
|||||||
byPlatform[platform].push(job);
|
byPlatform[platform].push(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process each platform group
|
// Process each platform group — LinkedIn first, then Wellfound, then external
|
||||||
|
const platformOrder = ['linkedin', 'wellfound', 'external'];
|
||||||
|
const sortedPlatforms = Object.entries(byPlatform)
|
||||||
|
.sort((a, b) => (platformOrder.indexOf(a[0]) ?? 99) - (platformOrder.indexOf(b[0]) ?? 99));
|
||||||
let timedOut = false;
|
let timedOut = false;
|
||||||
for (const [platform, platformJobs] of Object.entries(byPlatform)) {
|
for (const [platform, platformJobs] of sortedPlatforms) {
|
||||||
if (timedOut) break;
|
if (timedOut) break;
|
||||||
console.log(`\n--- ${platform.toUpperCase()} (${platformJobs.length} jobs) ---\n`);
|
console.log(`\n--- ${platform.toUpperCase()} (${platformJobs.length} jobs) ---\n`);
|
||||||
let browser;
|
let browser;
|
||||||
|
|||||||
@@ -14,7 +14,18 @@ export async function apply(page, job, formFiller) {
|
|||||||
const meta = await page.evaluate(() => ({
|
const meta = await page.evaluate(() => ({
|
||||||
title: document.querySelector('h1')?.textContent?.trim(),
|
title: document.querySelector('h1')?.textContent?.trim(),
|
||||||
company: document.querySelector('[class*="company"] h2, [class*="startup"] h2, h2')?.textContent?.trim(),
|
company: document.querySelector('[class*="company"] h2, [class*="startup"] h2, h2')?.textContent?.trim(),
|
||||||
}));
|
})).catch(() => ({}));
|
||||||
|
|
||||||
|
// Check if listing is closed/unavailable
|
||||||
|
const closed = await page.evaluate(() => {
|
||||||
|
const text = (document.body.innerText || '').toLowerCase();
|
||||||
|
return text.includes('no longer accepting') || text.includes('position has been filled') ||
|
||||||
|
text.includes('this job is no longer') || text.includes('job not found');
|
||||||
|
}).catch(() => false);
|
||||||
|
if (closed) {
|
||||||
|
console.log(` ℹ️ Job closed — no longer available`);
|
||||||
|
return { status: 'skipped_no_apply', meta };
|
||||||
|
}
|
||||||
|
|
||||||
const applyBtn = page.locator('a:has-text("Apply"), button:has-text("Apply Now"), a:has-text("Apply Now")').first();
|
const applyBtn = page.locator('a:has-text("Apply"), button:has-text("Apply Now"), a:has-text("Apply Now")').first();
|
||||||
if (await applyBtn.count() === 0) return { status: 'no_button', meta };
|
if (await applyBtn.count() === 0) return { status: 'no_button', meta };
|
||||||
@@ -33,5 +44,20 @@ export async function apply(page, job, formFiller) {
|
|||||||
await submitBtn.click();
|
await submitBtn.click();
|
||||||
await page.waitForTimeout(SUBMIT_WAIT);
|
await page.waitForTimeout(SUBMIT_WAIT);
|
||||||
|
|
||||||
|
// Verify submission — check for success indicators or form gone
|
||||||
|
const postSubmit = await page.evaluate(() => {
|
||||||
|
const text = (document.body.innerText || '').toLowerCase();
|
||||||
|
return {
|
||||||
|
hasSuccess: text.includes('application submitted') || text.includes('successfully applied') ||
|
||||||
|
text.includes('thank you') || text.includes('application received'),
|
||||||
|
hasForm: !!document.querySelector('form button[type="submit"]:not([disabled])'),
|
||||||
|
};
|
||||||
|
}).catch(() => ({ hasSuccess: false, hasForm: false }));
|
||||||
|
|
||||||
|
if (postSubmit.hasSuccess || !postSubmit.hasForm) {
|
||||||
return { status: 'submitted', meta };
|
return { status: 'submitted', meta };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(` ⚠️ Submit clicked but form still present — may not have submitted`);
|
||||||
|
return { status: 'incomplete', meta };
|
||||||
|
}
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ export async function searchWellfound(page, search, { onPage } = {}) {
|
|||||||
results.push({
|
results.push({
|
||||||
id: `wf_${slug}`,
|
id: `wf_${slug}`,
|
||||||
platform: 'wellfound',
|
platform: 'wellfound',
|
||||||
|
apply_type: 'wellfound',
|
||||||
track,
|
track,
|
||||||
title,
|
title,
|
||||||
company,
|
company,
|
||||||
|
|||||||
Reference in New Issue
Block a user