#!/usr/bin/env bun import { KEYWORDS, fetchJobs, isEnglishContent } from "../fetch.ts"; import type { SearchConfig } from "../config.ts"; // Re-export KEYWORDS isn't in fetch.ts, import from config import { KEYWORDS as CFG_KEYWORDS } from "../config.ts"; const HEADERS = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", "Accept-Language": "en-US,en;q=0.9", Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", }; const SAAS_RE = /\bsaas\b/i; async function hasSaaS(id: string): Promise { try { await Bun.sleep(350); const res = await fetch(`https://www.linkedin.com/jobs/view/${id}/`, { headers: HEADERS }); const html = await res.text(); const m = html.match(/class="show-more-less-html__markup[^"]*">([\s\S]{0,8000}?)<\/div>/); const text = m ? m[1].replace(/<[^>]+>/g, " ") : html.replace(/<[^>]+>/g, " ").slice(0, 5000); return SAAS_RE.test(text); } catch { return false; } } const RUBY_KEYWORDS = [ "Ruby on Rails", "Rails Engineer", "Rails Developer", "Ruby Engineer", "Ruby Developer", "Ruby", ].join(" OR "); // --- Part 1: Ruby/Rails in Switzerland --- const rubySearches: SearchConfig[] = [ { label: "Basel Ruby (onsite+hybrid)", keywords: RUBY_KEYWORDS, geoId: "103383283", location: "Basel", workType: ["onsite", "hybrid"], timePosted: "month" }, { label: "Basel Ruby (remote)", keywords: RUBY_KEYWORDS, geoId: "103383283", location: "Basel", workType: ["remote"], timePosted: "month" }, { label: "Zürich Ruby (onsite+hybrid)",keywords: RUBY_KEYWORDS, geoId: "102436504", location: "Zürich, Switzerland", workType: ["onsite", "hybrid"], timePosted: "month" }, { label: "Zürich Ruby (remote)", keywords: RUBY_KEYWORDS, geoId: "102436504", location: "Zürich, Switzerland", workType: ["remote"], timePosted: "month" }, { label: "Switzerland Ruby (remote)", keywords: RUBY_KEYWORDS, location: "Switzerland", workType: ["remote"], timePosted: "month" }, { label: "Bern Ruby", keywords: RUBY_KEYWORDS, location: "Bern, Switzerland", workType: ["onsite", "hybrid", "remote"], timePosted: "month" }, ]; // --- Part 2: SaaS-experience jobs in Switzerland --- const saasSearches: SearchConfig[] = [ { label: "Basel SaaS", keywords: CFG_KEYWORDS, geoId: "103383283", location: "Basel", workType: ["onsite", "hybrid"], timePosted: "month" }, { label: "Basel Metro SaaS", keywords: CFG_KEYWORDS, geoId: "90009885", location: "Basel Metropolitan Area", workType: ["onsite", "hybrid"], timePosted: "month" }, { label: "Zürich SaaS", keywords: CFG_KEYWORDS, geoId: "102436504", location: "Zürich, Switzerland", workType: ["onsite", "hybrid"], timePosted: "month" }, { label: "Switzerland SaaS remote", keywords: CFG_KEYWORDS, location: "Switzerland", workType: ["remote"], timePosted: "month" }, ]; type Job = { id: string; title: string; company: string; link: string; search: string }; async function runSearches(searches: SearchConfig[], label: string, filterFn?: (id: string) => Promise) { const results: Job[] = []; const seen = new Set(); console.log(`\n${"=".repeat(50)}\n${label}\n${"=".repeat(50)}`); 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; for (const job of jobs) { if (seen.has(job.id)) continue; seen.add(job.id); if (filterFn) { process.stdout.write(` ${job.title} @ ${job.company} … `); const pass = await filterFn(job.id); if (!pass) { console.log("no SaaS"); continue; } console.log("SaaS ✓"); } results.push({ ...job, search: config.label }); added++; } console.log(` => ${added} added`); } return results; } const rubyJobs = await runSearches(rubySearches, "RUBY / RAILS IN SWITZERLAND"); const saasJobs = await runSearches(saasSearches, "SaaS-EXPERIENCE JOBS IN SWITZERLAND", hasSaaS); console.log(`\n\n${"=".repeat(50)}\nRUBY RESULTS: ${rubyJobs.length}\n${"=".repeat(50)}`); for (const j of rubyJobs) console.log(`${j.title} | ${j.company} | ${j.id} | ${j.link}`); console.log(`\n${"=".repeat(50)}\nSaaS RESULTS: ${saasJobs.length}\n${"=".repeat(50)}`); for (const j of saasJobs) console.log(`${j.title} | ${j.company} | ${j.id} | ${j.link}`);