chore: update search scripts, extra sources and post drafts

Adds the one-off scripts accumulated across recent sessions, removes
temp scripts whose searches now live in config.ts as role-family
sweeps, and updates fetch-other.ts plus the LinkedIn post drafts.
This commit is contained in:
2026-08-27 09:03:05 +02:00
parent 3f5360f44c
commit b4957025b1
31 changed files with 2008 additions and 283 deletions
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env bun
import type { SearchConfig } from "../config.ts";
import { fetchJobs, isEnglishContent, JUNIOR_TITLE_RE, JAVA_TITLE_RE } from "../fetch.ts";
const CSV = "./results/jobs.csv";
// Titles the engineer-centric KEYWORDS list never searches for.
const BROAD_KEYWORDS = [
"Implementation Manager",
"Implementation Consultant",
"Implementation Specialist",
"Solutions Consultant",
"Solution Architect",
"Technical Consultant",
"Technical Account Manager",
"Technical Program Manager",
"Technical Project Manager",
"Delivery Manager",
"Integration Engineer",
"Integration Specialist",
"Customer Success Engineer",
"Sales Engineer",
"Presales Engineer",
"Digital Transformation",
"Product Owner",
"Scientific Software",
"Research Software",
"Automation Engineer",
"Systems Integration",
].join(" OR ");
// Microsoft stack = hard no (see cv/polo.md).
const MICROSOFT_RE = /\b(\.net|dotnet|c#|azure|sharepoint|dynamics|power\s*(platform|bi|apps|automate)|sccm|intune|windows\s*server|m365|office\s*365|uipath)\b/i;
const SAP_RE = /\bsap\b|\bs\/4hana\b|\bsalesforce\b|\bservicenow\b/i;
const AREAS: SearchConfig[] = [
{ label: "Basel (city)", keywords: BROAD_KEYWORDS, geoId: "103383283", location: "Basel", workType: ["onsite", "hybrid"], timePosted: "week" },
{ label: "Basel Metropolitan Area", keywords: BROAD_KEYWORDS, geoId: "90009885", location: "Basel Metropolitan Area", workType: ["onsite", "hybrid"], timePosted: "week" },
{ label: "Basel-Country", keywords: BROAD_KEYWORDS, geoId: "107117530", location: "Basel-Country", workType: ["onsite", "hybrid"], timePosted: "week" },
];
const existingIds = new Set<string>();
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 */ }
type Row = { id: string; title: string; company: string; location: string; link: string; posted: string; isNew: boolean };
const seen = new Set<string>();
const rows: Row[] = [];
for (const config of AREAS) {
// raw: true — bypass SOFTWARE_TITLE_RE so non-engineer titles survive
const jobs = await fetchJobs(config, { raw: true });
console.error(`[${config.label}] ${jobs.length} jobs`);
for (const job of jobs) {
if (seen.has(job.id)) continue;
seen.add(job.id);
const t = job.title;
if (JUNIOR_TITLE_RE.test(t)) { console.error(` junior: ${t}`); continue; }
if (JAVA_TITLE_RE.test(t)) { console.error(` java: ${t}`); continue; }
if (MICROSOFT_RE.test(t)) { console.error(` MS stack: ${t}`); continue; }
if (SAP_RE.test(t)) { console.error(` SAP/SF/SNOW: ${t}`); continue; }
rows.push({
id: job.id, title: t, company: job.company,
location: (job as any).location ?? "", link: job.link,
posted: (job as any).postedAt ?? "", isNew: !existingIds.has(job.id),
});
}
}
const survivors: Row[] = [];
for (const r of rows) {
if (await isEnglishContent(r.id)) survivors.push(r);
else console.error(` DE/FR content dropped: ${r.title} — ${r.company}`);
}
console.error(`\n${survivors.length} survivors (${survivors.filter((r) => r.isNew).length} not in CSV)`);
console.log(JSON.stringify(survivors, null, 2));