Files
claw-apply/lib/apply/wellfound.mjs
Matthew Jackson 094824abb2 Add dedicated 'closed' status for listings no longer accepting applications
Distinguishes closed listings from missing apply buttons. Shows in summary
as a separate line item.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 13:23:39 -08:00

64 lines
2.6 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* wellfound.mjs — Wellfound apply handler
*/
import {
NAVIGATION_TIMEOUT, PAGE_LOAD_WAIT, FORM_FILL_WAIT, SUBMIT_WAIT
} from '../constants.mjs';
export const SUPPORTED_TYPES = ['wellfound', 'wellfound_apply'];
export async function apply(page, job, formFiller) {
await page.goto(job.url, { waitUntil: 'domcontentloaded', timeout: NAVIGATION_TIMEOUT });
await page.waitForTimeout(PAGE_LOAD_WAIT);
const meta = await page.evaluate(() => ({
title: document.querySelector('h1')?.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: 'closed', meta };
}
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 };
await applyBtn.click();
await page.waitForTimeout(FORM_FILL_WAIT);
const unknowns = await formFiller.fill(page, formFiller.profile.resume_path);
if (unknowns[0]?.honeypot) return { status: 'skipped_honeypot', meta };
if (unknowns.length > 0) return { status: 'needs_answer', pending_question: unknowns[0], meta };
const submitBtn = await page.$('button[type="submit"]:not([disabled]), input[type="submit"]');
if (!submitBtn) return { status: 'no_submit', meta };
await submitBtn.click();
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 };
}
console.log(` ⚠️ Submit clicked but form still present — may not have submitted`);
return { status: 'incomplete', meta };
}