import { loadDotEnv } from '../src/env.mjs'; import { openDb, ensureSeeds } from '../src/db.mjs'; import { runCapture, createCaptureRun } from '../src/capture/runner.mjs'; import { describeSources } from '../src/sources/index.mjs'; import { randomUUID } from 'node:crypto'; import { createJobLease } from '../src/jobs/lease.mjs'; // CLI de captura (uso avancado / testes). A interface principal e o app: // node scripts/servidor.mjs (ou o Maquina-Comercial-Pessoal.exe) // // Exemplos: // node scripts/capturar-leads.mjs --sources=mock // node scripts/capturar-leads.mjs --sources=cnpj-base --uf=BA --cities="Santo Antonio de Jesus" // node scripts/capturar-leads.mjs --sources=cnpj-base,overpass --uf=BA --cities=all --segments=all await loadDotEnv(); const args = new Map( process.argv.slice(2).map((arg) => { const [key, value = 'true'] = arg.replace(/^--/, '').split('='); return [key, value]; }) ); function usage() { const fontes = describeSources().map((s) => ` ${s.id} — ${s.label}${s.disponivel ? '' : ` (indisponivel: ${s.motivo})`}`); return [ 'Uso: node scripts/capturar-leads.mjs --sources= [opcoes]', '', 'Opcoes:', ' --sources=cnpj-base,overpass fontes separadas por virgula (ou "mock")', ' --uf=BA estado alvo', ' --cities=all | "A,B,C" cidades (all = todas as cidades da UF na base)', ' --segments=all | "a,b,c" nichos pelo nome (all = todos os 57)', ' --segment-limit=N usa so os N primeiros nichos', ' --city-limit=N usa so as N primeiras cidades', ' --max=10 max de resultados por consulta (fontes de API)', ' --query-limit=0 max total de consultas (0 = sem limite)', '', 'Fontes:', ...fontes ].join('\n'); } async function main() { if (args.has('help')) { console.log(usage()); return; } const db = openDb(); await ensureSeeds(db, { log: (m) => console.log(`[captura] ${m}`) }); const uf = (args.get('uf') || 'BA').toUpperCase(); const sources = (args.get('sources') || 'mock').split(',').map((s) => s.trim()).filter(Boolean); let segments = db.prepare('SELECT * FROM segments WHERE active = 1 ORDER BY id').all().map((row) => ({ name: row.name, label: row.label, interest: row.interest, queries: JSON.parse(row.queries_json), cnaeCodes: JSON.parse(row.cnae_codes_json), overpassTags: JSON.parse(row.overpass_tags_json), pains: JSON.parse(row.pains_json) })); const segmentsArg = args.get('segments'); if (segmentsArg && segmentsArg !== 'all') { const wanted = new Set(segmentsArg.split(',').map((s) => s.trim())); segments = segments.filter((s) => wanted.has(s.name)); } const segmentLimit = Number(args.get('segment-limit') || 0); if (segmentLimit > 0) segments = segments.slice(0, segmentLimit); let cities; const citiesArg = args.get('cities') || 'all'; if (citiesArg === 'all') { cities = db.prepare('SELECT nome FROM municipios WHERE uf = ? ORDER BY nome').all(uf).map((r) => r.nome); } else { cities = citiesArg.split(',').map((c) => c.trim()).filter(Boolean); } const cityLimit = Number(args.get('city-limit') || 0); if (cityLimit > 0) cities = cities.slice(0, cityLimit); const params = { sources, uf, cities, segments, limits: { maxPerQuery: Number(args.get('max') || 10), queryLimit: Number(args.get('query-limit') || 0), googlePages: Number(args.get('google-pages') || 3) } }; console.log(`[captura] fontes: ${sources.join(', ')} | UF: ${uf} | cidades: ${cities.length} | nichos: ${segments.length}`); const active = db.prepare("SELECT id FROM capture_runs WHERE status IN ('pending','running')").get(); if (active) throw new Error(`ja existe uma captura em andamento (#${active.id})`); const runId = createCaptureRun(db, { ...params, segments: segments.map((s) => s.name) }); const lease = createJobLease({ db, table: 'capture_runs', id: runId, ownerToken: randomUUID() }); let summary; try { summary = await runCapture({ db, runId, params, emit: (p) => console.log(`[captura] ${p.phase}: ${p.message || ''}`), log: (m) => console.log(`[captura] ${m}`) }); } finally { lease.close(); } console.log(`[captura] resultado: ${summary.raw} brutos, ${summary.new} novos, ${summary.dup} duplicados (${summary.status})`); db.close(); } main().catch((error) => { console.error(`[captura] erro: ${error.stack || error.message}`); process.exit(1); });