- move temp scripts to scripts/, cv files to cv/, jobs.csv to results/ - update index.ts and recheck.ts CSV path to results/jobs.csv - fix imports in scripts/ to use ../config.ts and ../fetch.ts - update CLAUDE.md and README.md to reflect new structure - add plans/ with LinkedIn post drafts - update .gitignore: ignore results/, jobs/, cv binaries, .DS_Store
56 lines
2.1 KiB
TypeScript
56 lines
2.1 KiB
TypeScript
#!/usr/bin/env bun
|
|
import { fetchJobs, isEnglishContent } from "../fetch.ts";
|
|
import type { SearchConfig } from "../config.ts";
|
|
|
|
const CSV = "./jobs.csv";
|
|
const existingLinks = new Set<string>();
|
|
try {
|
|
const text = await Bun.file(CSV).text();
|
|
for (const line of text.split("\n").slice(1)) {
|
|
const url = line.match(/"(https?:\/\/[^"]+)"/)?.[1];
|
|
if (url) existingLinks.add(url);
|
|
}
|
|
} catch {}
|
|
|
|
const RUBY_KEYWORDS = "Ruby on Rails OR Ruby Developer OR Rails Developer OR Backend Ruby OR Ruby Engineer";
|
|
|
|
const searches: SearchConfig[] = [
|
|
{ label: "Basel Ruby", keywords: RUBY_KEYWORDS, geoId: "90009885", timePosted: "month" },
|
|
{ label: "Zürich Ruby", keywords: RUBY_KEYWORDS, geoId: "107814425", timePosted: "month" },
|
|
{ label: "Bern Ruby", keywords: RUBY_KEYWORDS, location: "Bern, Switzerland", timePosted: "month" },
|
|
{ label: "CH remote Ruby", keywords: RUBY_KEYWORDS, location: "Switzerland", workType: ["remote"], timePosted: "month" },
|
|
];
|
|
|
|
const RUBY_RE = /\bruby\b|\brails\b/i;
|
|
|
|
type Job = { id: string; title: string; company: string; link: string; search: string };
|
|
const results: Job[] = [];
|
|
const seen = new Set<string>();
|
|
|
|
for (const config of searches) {
|
|
console.log(`\n[${config.label}]`);
|
|
const jobs = await fetchJobs(config);
|
|
if (jobs.length === 0) { console.log(" no results"); continue; }
|
|
|
|
let added = 0;
|
|
for (const job of jobs) {
|
|
if (seen.has(job.id) || existingLinks.has(job.link)) continue;
|
|
seen.add(job.id);
|
|
process.stdout.write(` ${job.title} @ ${job.company} … `);
|
|
const english = await isEnglishContent(job.id);
|
|
if (!english) { console.log("dropped (lang)"); continue; }
|
|
// fetch description to confirm Ruby/Rails mention
|
|
const html = await fetch(`https://www.linkedin.com/jobs/view/${job.id}/`).then(r => r.text()).catch(() => "");
|
|
if (!RUBY_RE.test(html)) { console.log("dropped (no ruby/rails in desc)"); continue; }
|
|
console.log("ok");
|
|
results.push({ ...job, search: config.label });
|
|
added++;
|
|
}
|
|
console.log(` => ${added} new`);
|
|
}
|
|
|
|
console.log(`\n=== TOTAL NEW: ${results.length} ===\n`);
|
|
for (const j of results) {
|
|
console.log(`[${j.search}] ${j.title} | ${j.company} | ${j.id}`);
|
|
}
|