Actualizar robot.js

This commit is contained in:
marsalva 2025-12-26 10:01:01 +00:00
parent 25ef322f81
commit 1cab22b1a8
1 changed files with 89 additions and 14 deletions

103
robot.js
View File

@ -113,8 +113,20 @@ async function getAllPendingDocIds() {
return snap.docs.map(d => d.id); return snap.docs.map(d => d.id);
} }
async function archiveDoc(docRef, nowISO, reason, extra = {}) {
// Marca como archivado para que NO aparezca como "nuevo" (verde)
await docRef.set({
status: "archived",
archivedReason: reason,
archivedAt: nowISO,
updatedAt: nowISO,
...extra
}, { merge: true });
}
async function runRobot() { async function runRobot() {
console.log('🤖 [V7.5] Robot HomeServe (no dupes + in_system + archiva solo completed)...'); console.log('🤖 [V7.6] Robot HomeServe (no dupes + in_system + archiva completed + archiva missing/bloqueado)...');
// 0) Credenciales // 0) Credenciales
let creds; let creds;
try { try {
@ -124,6 +136,7 @@ async function runRobot() {
console.error("❌ No se pudieron cargar credenciales:", e.message); console.error("❌ No se pudieron cargar credenciales:", e.message);
process.exit(1); process.exit(1);
} }
const browser = await chromium.launch({ const browser = await chromium.launch({
headless: true, headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'], args: ['--no-sandbox', '--disable-setuid-sandbox'],
@ -131,12 +144,14 @@ async function runRobot() {
const context = await browser.newContext(); const context = await browser.newContext();
const page = await context.newPage(); const page = await context.newPage();
const nowISO = new Date().toISOString(); const nowISO = new Date().toISOString();
try { try {
// LOGIN // LOGIN
console.log('🔐 Entrando al login...'); console.log('🔐 Entrando al login...');
await page.goto('https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=PROF_PASS', { timeout: 60000 }); await page.goto('https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=PROF_PASS', { timeout: 60000 });
const selectorUsuario = 'input[name="CODIGO"]'; const selectorUsuario = 'input[name="CODIGO"]';
const selectorPass = 'input[type="password"]'; const selectorPass = 'input[type="password"]';
if (await page.isVisible(selectorUsuario)) { if (await page.isVisible(selectorUsuario)) {
await page.fill(selectorUsuario, ""); await page.fill(selectorUsuario, "");
await page.fill(selectorPass, ""); await page.fill(selectorPass, "");
@ -148,9 +163,11 @@ async function runRobot() {
} else { } else {
console.log("⚠️ No veo login (quizá ya logueado)."); console.log("⚠️ No veo login (quizá ya logueado).");
} }
// LISTA // LISTA
console.log('📂 Leyendo lista de servicios...'); console.log('📂 Leyendo lista de servicios...');
await page.goto('https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=lista_servicios_total'); await page.goto('https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=lista_servicios_total');
const referenciasEnWeb = await page.evaluate(() => { const referenciasEnWeb = await page.evaluate(() => {
const filas = Array.from(document.querySelectorAll('table tr')); const filas = Array.from(document.querySelectorAll('table tr'));
const refs = []; const refs = [];
@ -164,20 +181,25 @@ async function runRobot() {
}); });
return Array.from(new Set(refs)); return Array.from(new Set(refs));
}); });
console.log(`🔎 Encontrados ${referenciasEnWeb.length} servicios válidos.`); console.log(`🔎 Encontrados ${referenciasEnWeb.length} servicios válidos.`);
const referenciasNormalizadas = referenciasEnWeb const referenciasNormalizadas = referenciasEnWeb
.map(normalizeServiceNumber) .map(normalizeServiceNumber)
.filter(Boolean); .filter(Boolean);
const webSet = new Set(referenciasNormalizadas); const webSet = new Set(referenciasNormalizadas);
// ✅ precargar info del sistema // ✅ precargar info del sistema
console.log("🧠 Precargando datos del sistema (appointments/services)..."); console.log("🧠 Precargando datos del sistema (appointments/services)...");
const apptInfoMap = await preloadAppointmentsInfo(referenciasNormalizadas); const apptInfoMap = await preloadAppointmentsInfo(referenciasNormalizadas);
const servicesSet = await preloadServicesExistence(referenciasNormalizadas); const servicesSet = await preloadServicesExistence(referenciasNormalizadas);
console.log(`🧾 En appointments: ${apptInfoMap.size} | En services: ${servicesSet.size}`); console.log(`🧾 En appointments: ${apptInfoMap.size} | En services: ${servicesSet.size}`);
// ✅ ids que ya estaban en homeserve_pendientes // ✅ ids que ya estaban en homeserve_pendientes
console.log("📦 Cargando documentos actuales de homeserve_pendientes..."); console.log("📦 Cargando documentos actuales de homeserve_pendientes...");
const pendingDocIds = await getAllPendingDocIds(); const pendingDocIds = await getAllPendingDocIds();
const pendingSet = new Set(pendingDocIds); const pendingSet = new Set(pendingDocIds);
// --- CONTADORES --- // --- CONTADORES ---
let actualizados = 0; let actualizados = 0;
let nuevos = 0; let nuevos = 0;
@ -185,14 +207,18 @@ async function runRobot() {
let saltadosSinDatos = 0; let saltadosSinDatos = 0;
let marcadosInSystem = 0; let marcadosInSystem = 0;
let archivadosCompleted = 0; let archivadosCompleted = 0;
let marcadosMissingNoArchive = 0; let archivadosMissing = 0;
let archivadosBlocked = 0;
for (const ref of referenciasEnWeb) { for (const ref of referenciasEnWeb) {
const normalized = normalizeServiceNumber(ref); const normalized = normalizeServiceNumber(ref);
if (!normalized) continue; if (!normalized) continue;
const appt = apptInfoMap.get(normalized); const appt = apptInfoMap.get(normalized);
const existsInAppointments = !!appt; const existsInAppointments = !!appt;
const existsInServices = servicesSet.has(normalized); const existsInServices = servicesSet.has(normalized);
const existsInSystem = existsInAppointments || existsInServices; const existsInSystem = existsInAppointments || existsInServices;
// ✅ Si ya existe en sistema: // ✅ Si ya existe en sistema:
// - SI completed => archived // - SI completed => archived
// - SI NO completed => in_system // - SI NO completed => in_system
@ -227,24 +253,36 @@ async function runRobot() {
// No hace falta scrapear si ya existe el doc y ya está integrado // No hace falta scrapear si ya existe el doc y ya está integrado
continue; continue;
} }
// Si existe en sistema pero NO existe en homeserve_pendientes (no lo tenías): // Si existe en sistema pero NO existe en homeserve_pendientes (no lo tenías):
// lo tratamos como antes (scrape) y lo guardamos con status in_system (no archived). // si está completed, no creamos doc nuevo (no aporta mucho).
// Si está completed, no creamos doc nuevo (no aporta mucho).
if (existsInSystem && !pendingSet.has(normalized) && existsInAppointments && isCompletedStatus(appt.status)) { if (existsInSystem && !pendingSet.has(normalized) && existsInAppointments && isCompletedStatus(appt.status)) {
archivadosCompleted++; archivadosCompleted++;
console.log(`⏭️ SALTADO (completed y sin doc pendiente): ${normalized}`); console.log(`⏭️ SALTADO (completed y sin doc pendiente): ${normalized}`);
continue; continue;
} }
// Navegar a la lista y click // Navegar a la lista y click
await page.goto('https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=lista_servicios_total'); await page.goto('https://www.clientes.homeserve.es/cgi-bin/fccgi.exe?w3exec=lista_servicios_total');
try { try {
await page.click(`text="${normalized}"`, { timeout: 5000 }); await page.click(`text="${normalized}"`, { timeout: 5000 });
await page.waitForTimeout(1500); await page.waitForTimeout(1500);
} catch (e) { } catch (e) {
// 🔥 CAMBIO: si está bloqueado/no accesible, lo archivamos para que NO salga como "nuevo"
saltadosBloqueo++; saltadosBloqueo++;
console.warn(`⛔ SALTADO (bloqueado/no accesible): ${normalized}`); console.warn(`⛔ SALTADO (bloqueado/no accesible): ${normalized}`);
const docRef = db.collection(COLLECTION_NAME).doc(normalized);
await archiveDoc(docRef, nowISO, "blocked_or_unreadable", {
blockedAt: nowISO,
missingFromHomeServe: false,
lastSeenAt: nowISO
});
archivadosBlocked++;
continue; continue;
} }
const detalles = await page.evaluate(() => { const detalles = await page.evaluate(() => {
const d = {}; const d = {};
const filas = Array.from(document.querySelectorAll('tr')); const filas = Array.from(document.querySelectorAll('tr'));
@ -268,19 +306,32 @@ async function runRobot() {
}); });
return d; return d;
}); });
if (!hasMinimumData(detalles)) { if (!hasMinimumData(detalles)) {
// 🔥 CAMBIO: si no hay datos mínimos, también lo archivamos para que NO se quede "nuevo"
saltadosSinDatos++; saltadosSinDatos++;
console.warn(`⛔ SALTADO (sin datos mínimos): ${normalized}`); console.warn(`⛔ SALTADO (sin datos mínimos): ${normalized}`);
const docRef = db.collection(COLLECTION_NAME).doc(normalized);
await archiveDoc(docRef, nowISO, "missing_minimum_data", {
missingFromHomeServe: false,
lastSeenAt: nowISO
});
archivadosBlocked++;
continue; continue;
} }
const fullAddress = `${detalles.addressPart || ""} ${detalles.cityPart || ""}`.trim(); const fullAddress = `${detalles.addressPart || ""} ${detalles.cityPart || ""}`.trim();
let rawCompany = detalles.company || ""; let rawCompany = detalles.company || "";
if (rawCompany && !rawCompany.toUpperCase().includes("HOMESERVE")) { if (rawCompany && !rawCompany.toUpperCase().includes("HOMESERVE")) {
rawCompany = `HOMESERVE - ${rawCompany}`; rawCompany = `HOMESERVE - ${rawCompany}`;
} }
const docRef = db.collection(COLLECTION_NAME).doc(normalized); const docRef = db.collection(COLLECTION_NAME).doc(normalized);
const docSnapshot = await docRef.get(); const docSnapshot = await docRef.get();
const datosAntiguos = docSnapshot.exists ? docSnapshot.data() : null; const datosAntiguos = docSnapshot.exists ? docSnapshot.data() : null;
// Estado según sistema (si existe, es in_system; si no, pendiente_validacion) // Estado según sistema (si existe, es in_system; si no, pendiente_validacion)
let status = "pendiente_validacion"; let status = "pendiente_validacion";
let integratedIn = ""; let integratedIn = "";
@ -294,6 +345,7 @@ async function runRobot() {
integratedIn = "services"; integratedIn = "services";
} }
} }
const servicioFinal = { const servicioFinal = {
serviceNumber: normalized, serviceNumber: normalized,
clientName: detalles.clientName || "Desconocido", clientName: detalles.clientName || "Desconocido",
@ -310,7 +362,9 @@ async function runRobot() {
updatedAt: nowISO, updatedAt: nowISO,
missingFromHomeServe: false, missingFromHomeServe: false,
}; };
if (!datosAntiguos) servicioFinal.createdAt = nowISO; if (!datosAntiguos) servicioFinal.createdAt = nowISO;
if (!datosAntiguos) { if (!datosAntiguos) {
await docRef.set(servicioFinal); await docRef.set(servicioFinal);
console.log(`NUEVO: ${normalized} (status=${status})`); console.log(`NUEVO: ${normalized} (status=${status})`);
@ -322,6 +376,7 @@ async function runRobot() {
const cambioCliente = (datosAntiguos.clientName || "") !== (servicioFinal.clientName || ""); const cambioCliente = (datosAntiguos.clientName || "") !== (servicioFinal.clientName || "");
const cambioCompany = (datosAntiguos.company || "") !== (servicioFinal.company || ""); const cambioCompany = (datosAntiguos.company || "") !== (servicioFinal.company || "");
const cambioStatus = (datosAntiguos.status || "") !== (servicioFinal.status || ""); const cambioStatus = (datosAntiguos.status || "") !== (servicioFinal.status || "");
if (cambioEstado || cambioTelefono || cambioAddress || cambioCliente || cambioCompany || cambioStatus) { if (cambioEstado || cambioTelefono || cambioAddress || cambioCliente || cambioCompany || cambioStatus) {
console.log(`♻️ ACTUALIZADO: ${normalized}`); console.log(`♻️ ACTUALIZADO: ${normalized}`);
await docRef.set(servicioFinal, { merge: true }); await docRef.set(servicioFinal, { merge: true });
@ -329,6 +384,7 @@ async function runRobot() {
} else { } else {
await docRef.set({ lastSeenAt: nowISO, updatedAt: nowISO, missingFromHomeServe: false }, { merge: true }); await docRef.set({ lastSeenAt: nowISO, updatedAt: nowISO, missingFromHomeServe: false }, { merge: true });
} }
// Si estaba archivado pero reaparece y NO está completed => lo “desarchivamos” // Si estaba archivado pero reaparece y NO está completed => lo “desarchivamos”
if ((datosAntiguos.status || "") === "archived") { if ((datosAntiguos.status || "") === "archived") {
const shouldStayArchived = existsInAppointments && isCompletedStatus(appt?.status); const shouldStayArchived = existsInAppointments && isCompletedStatus(appt?.status);
@ -343,26 +399,30 @@ async function runRobot() {
} }
} }
} }
// ✅ Gestionar lo que ha desaparecido de HomeServe // ✅ Gestionar lo que ha desaparecido de HomeServe
// Regla NUEVA: NO archivar por desaparecer; SOLO archivar si completed. console.log("🗄️ Revisando los que han desaparecido de HomeServe...");
console.log("🗄️ Revisando los que han desaparecido de HomeServe (sin archivar salvo completed)...");
const missingIds = pendingDocIds const missingIds = pendingDocIds
.map(normalizeServiceNumber) .map(normalizeServiceNumber)
.filter(Boolean) .filter(Boolean)
.filter(sn => !webSet.has(sn)); .filter(sn => !webSet.has(sn));
// precargamos su estado en sistema para decidir // precargamos su estado en sistema para decidir
const apptMissingMap = await preloadAppointmentsInfo(missingIds); const apptMissingMap = await preloadAppointmentsInfo(missingIds);
const servicesMissingSet = await preloadServicesExistence(missingIds); const servicesMissingSet = await preloadServicesExistence(missingIds);
for (const sn of missingIds) { for (const sn of missingIds) {
const ref = db.collection(COLLECTION_NAME).doc(sn); const ref = db.collection(COLLECTION_NAME).doc(sn);
const snap = await ref.get(); const snap = await ref.get();
const data = snap.exists ? (snap.data() || {}) : {}; const data = snap.exists ? (snap.data() || {}) : {};
const appt = apptMissingMap.get(sn); const appt = apptMissingMap.get(sn);
const existsInAppointments = !!appt; const existsInAppointments = !!appt;
const existsInServices = servicesMissingSet.has(sn); const existsInServices = servicesMissingSet.has(sn);
const existsInSystem = existsInAppointments || existsInServices; const existsInSystem = existsInAppointments || existsInServices;
if (existsInAppointments && isCompletedStatus(appt.status)) { if (existsInAppointments && isCompletedStatus(appt.status)) {
// ✅ SOLO AQUÍ archivamos // ✅ SOLO AQUÍ archivamos por completed
if ((data.status || "") !== "archived") { if ((data.status || "") !== "archived") {
await ref.set({ await ref.set({
status: "archived", status: "archived",
@ -375,9 +435,16 @@ async function runRobot() {
missingAt: nowISO missingAt: nowISO
}, { merge: true }); }, { merge: true });
archivadosCompleted++; archivadosCompleted++;
} else {
await ref.set({
updatedAt: nowISO,
missingFromHomeServe: true,
missingAt: nowISO
}, { merge: true });
} }
continue; continue;
} }
if (existsInSystem) { if (existsInSystem) {
// Está en tu sistema pero no está completed => en sistema, NO archived // Está en tu sistema pero no está completed => en sistema, NO archived
await ref.set({ await ref.set({
@ -391,20 +458,27 @@ async function runRobot() {
marcadosInSystem++; marcadosInSystem++;
continue; continue;
} }
// No está en sistema y ha desaparecido de HomeServe -> NO archivar, solo marcar missing
// 🔥 CAMBIO: No está en sistema y ha desaparecido de HomeServe -> ARCHIVAR
// (para que NO aparezca como nuevo/verde y te quede rastro de que existió)
await ref.set({ await ref.set({
status: "archived",
archivedReason: "missing_from_homeserve",
archivedAt: nowISO,
missingFromHomeServe: true, missingFromHomeServe: true,
missingAt: nowISO, missingAt: nowISO,
updatedAt: nowISO updatedAt: nowISO
}, { merge: true }); }, { merge: true });
marcadosMissingNoArchive++; archivadosMissing++;
} }
console.log( console.log(
`🏁 FIN V7.5: ${nuevos} nuevos, ${actualizados} actualizados, ` + `🏁 FIN V7.6: ${nuevos} nuevos, ${actualizados} actualizados, ` +
`${saltadosBloqueo} saltados (bloqueados), ${saltadosSinDatos} saltados (sin datos), ` + `${saltadosBloqueo} bloqueados/no accesibles, ${saltadosSinDatos} sin datos mínimos, ` +
`${marcadosInSystem} marcados in_system, ${archivadosCompleted} archivados (completed), ` + `${marcadosInSystem} marcados in_system, ${archivadosCompleted} archivados (completed), ` +
`${marcadosMissingNoArchive} marcados missing (NO archived).` `${archivadosMissing} archivados (missing), ${archivadosBlocked} archivados (blocked/sin_datos).`
); );
} catch (error) { } catch (error) {
console.error('❌ ERROR:', error.message); console.error('❌ ERROR:', error.message);
process.exit(1); process.exit(1);
@ -414,12 +488,13 @@ async function runRobot() {
// --- PAUSA DE 15 MINUTOS ANTES DE REINICIAR --- // --- PAUSA DE 15 MINUTOS ANTES DE REINICIAR ---
const MINUTOS_ESPERA = 15; const MINUTOS_ESPERA = 15;
console.log(`😴 Pausando ejecución durante ${MINUTOS_ESPERA} minutos...`); console.log(`😴 Pausando ejecución durante ${MINUTOS_ESPERA} minutos...`);
// 15 minutos * 60 segundos * 1000 milisegundos // 15 minutos * 60 segundos * 1000 milisegundos
await new Promise(resolve => setTimeout(resolve, MINUTOS_ESPERA * 60 * 1000)); await new Promise(resolve => setTimeout(resolve, MINUTOS_ESPERA * 60 * 1000));
console.log("👋 Tiempo cumplido. Reiniciando proceso..."); console.log("👋 Tiempo cumplido. Reiniciando proceso...");
process.exit(0); process.exit(0);
} }
} }
runRobot(); runRobot();