#!/usr/bin/env bun import { searches } from "./config.ts"; import { fetchJobs, isEnglishContent } from "./fetch.ts"; const CSV = "./jobs.csv"; const HEADER = "date_found,posted_at,search,title,company,link"; // Load existing IDs from CSV to deduplicate across runs const existingIds = new Set(); try { const text = await Bun.file(CSV).text(); for (const line of text.split("\n").slice(1)) { const id = line.match(/\/jobs\/view\/(\d+)\//)?.[1]; if (id) existingIds.add(id); } } catch { // first run — file doesn't exist yet } const date = new Date().toISOString().slice(0, 10); const newRows: string[] = []; function csvCell(val: string): string { return `"${val.replace(/"/g, '""')}"`; } for (const config of searches) { console.log(`\n[${config.label}]`); const jobs = await fetchJobs(config); if (jobs.length === 0) { console.log(" No jobs found."); continue; } let added = 0; let langDropped = 0; for (const job of jobs) { if (!existingIds.has(job.id)) { process.stdout.write(` checking content: ${job.title} … `); const english = await isEnglishContent(job.id); if (!english) { console.log("dropped (non-English)"); langDropped++; continue; } console.log("ok"); existingIds.add(job.id); newRows.push( [date, job.postedAt, config.label, job.title, job.company, job.link] .map(csvCell) .join(",") ); added++; } } console.log(` ${jobs.length} found, ${added} new, ${langDropped} non-English dropped`); } if (newRows.length === 0) { console.log("\nNo new jobs."); process.exit(0); } // Append to CSV (write header if first run) const existing = await Bun.file(CSV).exists(); const append = existing ? await Bun.file(CSV).text() : HEADER + "\n"; await Bun.write(CSV, append + newRows.join("\n") + "\n"); console.log(`\n${newRows.length} new jobs → ${CSV}`);