Scanner: ohne Altersgrenze suchen, Filter Anzeigenalter, Alarm-Schlagwörter
- Suche ohne 21-Tage-Grenze (BA ohne veroeffentlichtseit, Adzuna ohne max_days_old); Einstellung Höchstalter entfällt - Dashboard: Filter Anzeigenalter (bis/älter als 21 Tage), Sortierung älteste Anzeige, Filter nur Alarm, Zeitraum heißt jetzt Gefunden - Suchagent: Schlagwörter mit Alarm; Treffer mit alarm = 1 melden sich über den AlarmWatcher (App-weit, jede Minute) mit Ton und Pop-up, Quittieren gilt für alle Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
b34a1968df
commit
1ad774fdd2
@@ -1,7 +1,7 @@
|
||||
import { pool } from '../db.js';
|
||||
import { SOURCES, sourceReady } from './sources.js';
|
||||
import { findCompany, normalizeCompany, scoreJob, stripQuotes } from './matching.js';
|
||||
import { loadScannerSecrets, loadScannerSettings, setLastScanAt } from './settings.js';
|
||||
import { agentTerms, findCompany, normalizeCompany, scoreJob, stripQuotes } from './matching.js';
|
||||
import { loadScannerSecrets, setLastScanAt } from './settings.js';
|
||||
|
||||
/*
|
||||
* Ein Suchlauf.
|
||||
@@ -9,7 +9,7 @@ import { loadScannerSecrets, loadScannerSettings, setLastScanAt } from './settin
|
||||
* 1. aktive Agenten laden
|
||||
* 2. je Boerse die Schlagwoerter aller Agenten zusammenfuehren - dasselbe
|
||||
* Wort wird nur einmal abgefragt, egal wie viele Agenten es tragen
|
||||
* 3. Anzeigen abrufen, alles ueber der Altersgrenze verwerfen
|
||||
* 3. Anzeigen abrufen - jeden Alters (das Dashboard filtert nach Alter)
|
||||
* 4. Anzeigen speichern, Firma gegen das CRM abgleichen
|
||||
* 5. jede Anzeige gegen jeden Agenten bewerten, der diese Boerse durchsucht
|
||||
*
|
||||
@@ -56,24 +56,22 @@ async function execute(trigger) {
|
||||
const [runResult] = await pool.query('INSERT INTO icon_scanner_runs (trigger_kind) VALUES (?)', [trigger]);
|
||||
const runId = runResult.insertId;
|
||||
|
||||
const settings = await loadScannerSettings();
|
||||
const secrets = await loadScannerSecrets();
|
||||
const agents = await loadActiveAgents();
|
||||
|
||||
// Boerse -> Schlagwoerter (ohne Doppelte, Gross/klein egal)
|
||||
// Boerse -> Schlagwoerter inkl. Alarm-Schlagwoerter (ohne Doppelte)
|
||||
const plan = new Map();
|
||||
for (const agent of agents) {
|
||||
for (const source of agent.sources) {
|
||||
if (!SOURCES[source] || !sourceReady(source, secrets)) continue;
|
||||
const words = plan.get(source) ?? new Map();
|
||||
for (const keyword of agent.keywords) words.set(keyword.toLowerCase(), keyword);
|
||||
for (const keyword of agentTerms(agent)) words.set(keyword.toLowerCase(), keyword);
|
||||
plan.set(source, words);
|
||||
}
|
||||
}
|
||||
|
||||
const perSource = {};
|
||||
const jobs = new Map();
|
||||
const oldest = Date.now() - settings.maxAge * 24 * 3_600_000;
|
||||
|
||||
for (const [source, words] of plan) {
|
||||
const stats = { ok: true, count: 0, errors: [] };
|
||||
@@ -82,11 +80,10 @@ async function execute(trigger) {
|
||||
if (!first) await sleep(REQUEST_GAP_MS);
|
||||
first = false;
|
||||
try {
|
||||
const found = await SOURCES[source].fetch(keyword, { maxAge: settings.maxAge, secrets });
|
||||
// Seit 29.09.2026 ohne Altersgrenze: auch aeltere Anzeigen kommen
|
||||
// herein, gefiltert wird im Dashboard ("Anzeigenalter").
|
||||
const found = await SOURCES[source].fetch(keyword, { secrets });
|
||||
for (const job of found) {
|
||||
// Ohne Datum bleibt die Anzeige drin: die Boersen sortieren bzw.
|
||||
// filtern selbst nach Alter, das fehlende Feld ist ein Parserluecke.
|
||||
if (job.publishedAt && job.publishedAt.getTime() < oldest) continue;
|
||||
jobs.set(`${job.source}:${job.externalId}`, job);
|
||||
stats.count += 1;
|
||||
}
|
||||
@@ -111,8 +108,8 @@ async function execute(trigger) {
|
||||
const result = scoreJob(agent, job);
|
||||
if (!result) continue;
|
||||
const [insert] = await pool.query(
|
||||
`INSERT IGNORE INTO icon_scanner_matches (agent_id, job_id, score, hits) VALUES (?, ?, ?, ?)`,
|
||||
[agent.id, jobId, result.score, JSON.stringify(result.hits)]
|
||||
`INSERT IGNORE INTO icon_scanner_matches (agent_id, job_id, score, hits, alarm) VALUES (?, ?, ?, ?, ?)`,
|
||||
[agent.id, jobId, result.score, JSON.stringify(result.hits), result.alarm ? 1 : 0]
|
||||
);
|
||||
matchesNew += insert.affectedRows;
|
||||
}
|
||||
@@ -153,6 +150,7 @@ async function loadActiveAgents() {
|
||||
return rows.map((row) => ({
|
||||
id: row.id,
|
||||
keywords: parseList(row.keywords),
|
||||
alarmKeywords: parseList(row.alarm_keywords),
|
||||
sources: parseList(row.sources),
|
||||
excludes: parseList(row.excludes),
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user