Fix: Snippets

This commit is contained in:
2026-02-10 14:11:39 -03:00
parent f6d8b34520
commit d1cd73945f
2 changed files with 168 additions and 162 deletions

View File

@@ -3,171 +3,172 @@ namespace WhatsappPromo.Engine.Wapi
public static class JsSnippets
{
public const string MediaExtractor = @"
console.log('Injected MediaExtractor initialized');
// Set to keep track of processed message IDs
window.processedMessages = new Set();
window.scanForMedia = async () => {
// Buscar todos los contenedores de mensajes
const messages = document.querySelectorAll('div[role=""row""]');
(function() {
var log = function(m) { if (window.onLog) window.onLog(m); };
log('[WAPP] Injected MediaExtractor initialized');
for (const msgRow of messages) {
// Chequear si es mensaje entrante (ignorar mensajes enviados)
if (!msgRow.classList.contains('message-in')) continue;
if (!window.processedMessages) window.processedMessages = new Set();
var triggerHumanClick = function(el) {
if (!el) return;
try {
el.scrollIntoView({ block: 'center', inline: 'nearest', behavior: 'instant' });
var rect = el.getBoundingClientRect();
var x = rect.left + rect.width / 2;
var y = rect.top + rect.height / 2;
var opt = { bubbles: true, cancelable: true, view: window, clientX: x, clientY: y, buttons: 1 };
el.dispatchEvent(new MouseEvent('mouseover', opt));
el.dispatchEvent(new MouseEvent('mousedown', opt));
el.focus();
setTimeout(function() {
el.dispatchEvent(new MouseEvent('mouseup', opt));
el.dispatchEvent(new MouseEvent('click', opt));
if (el.click) el.click();
}, 50);
} catch(e) { log('Error en clic: ' + e.message); }
};
// Chequear si tiene imagen/video
const img = msgRow.querySelector('img[src^=""blob:""]');
if (!img) continue;
const blobUrl = img.src;
// Usar ID único para evitar duplicados
const msgId = msgRow.getAttribute('data-id') || blobUrl;
if (window.processedMessages.has(msgId)) continue;
window.processedMessages.add(msgId);
console.log('Found new media:', blobUrl);
try {
// EXTRACCIÓN AUTOMÁTICA DE TELÉFONO (múltiples métodos)
let phone = '';
// MÉTODO 1: Buscar en atributos data-* del mensaje (más confiable)
// WhatsApp Web usa atributos internos que pueden contener el JID (phone@c.us)
const dataPrePlainText = msgRow.querySelector('[data-pre-plain-text]');
if (dataPrePlainText && !phone) {
const prePlainText = dataPrePlainText.getAttribute('data-pre-plain-text');
// Formato típico: ""[HH:MM, DD/MM/YYYY] Nombre: ""
// Pero en algunos casos incluye el número
const match = prePlainText.match(/(\d{10,15})/);
if (match) {
phone = match[1];
console.log('Phone extracted from data-pre-plain-text:', phone);
}
}
// MÉTODO 2: Buscar en el elemento padre que podría tener data-id con formato phone@c.us
if (!phone) {
let parentEl = msgRow;
for (let i = 0; i < 5; i++) { // Subir hasta 5 niveles
if (!parentEl) break;
const dataId = parentEl.getAttribute('data-id');
if (dataId && dataId.includes('@')) {
// Formato: ""false_5491112345678@c.us_MESSAGEID""
const parts = dataId.split('_');
for (const part of parts) {
if (part.includes('@c.us')) {
const phoneMatch = part.match(/(\d{10,15})@/);
if (phoneMatch) {
phone = phoneMatch[1];
console.log('Phone extracted from data-id:', phone);
break;
}
}
}
if (phone) break;
}
parentEl = parentEl.parentElement;
}
}
// MÉTODO 3: Buscar en elementos cercanos con atributos title o aria-label
if (!phone) {
const titleElements = msgRow.querySelectorAll('[title], [aria-label]');
for (const el of titleElements) {
const text = el.getAttribute('title') || el.getAttribute('aria-label') || '';
const phoneMatch = text.match(/\+?(\d{10,15})/);
if (phoneMatch && phoneMatch[1].length >= 10) {
phone = phoneMatch[1].replace(/\D/g, '');
console.log('Phone extracted from title/aria-label:', phone);
break;
}
}
}
// MÉTODO 4 (FALLBACK): Header del chat abierto (requiere intervención humana)
// Solo se usa si los métodos anteriores fallaron
if (!phone) {
const headerTitle = document.querySelector('header span[title]');
if (headerTitle) {
phone = headerTitle.getAttribute('title').replace(/\D/g, '');
console.log('Phone extracted from header (fallback):', phone);
}
}
// MÉTODO 5 (ÚLTIMO RECURSO): Buscar el chat activo en la lista lateral
if (!phone) {
const activeChat = document.querySelector('div[aria-selected=""true""]');
if (activeChat) {
const chatTitle = activeChat.querySelector('span[title]');
if (chatTitle) {
const titleText = chatTitle.getAttribute('title');
const phoneMatch = titleText.match(/\+?(\d{10,15})/);
if (phoneMatch) {
phone = phoneMatch[1].replace(/\D/g, '');
console.log('Phone extracted from active chat:', phone);
}
}
}
}
// VALIDACIÓN FINAL
if (!phone || phone.length < 8) {
console.warn('❌ No se pudo detectar el teléfono con ningún método. Mensaje ignorado.');
console.warn('💡 Sugerencia: Asegúrate de tener WhatsApp Web completamente cargado.');
// NO llamar a onMediaDownloaded, simplemente ignorar este mensaje
// y continuar con el siguiente
continue;
}
console.log('✅ Teléfono detectado exitosamente:', phone);
// Simular pequeño retraso random antes de descargar
await new Promise(r => setTimeout(r, Math.random() * 2000 + 500));
const response = await fetch(blobUrl);
const blob = await response.blob();
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = () => {
const base64data = reader.result.split(',')[1];
const mimeType = blob.type;
// Enviar a C#
window.onMediaDownloaded(phone, base64data, mimeType);
};
} catch (err) {
console.error('Error downloading media:', err);
}
}
};
// Anti-detección: Usar MutationObserver en lugar de polling constante
const observer = new MutationObserver((mutations) => {
let shouldScan = false;
for(const mutation of mutations) {
if (mutation.addedNodes.length > 0) {
shouldScan = true;
break;
window.getIdentityStrict = function() {
// El usuario nos dio el selector exacto: span[data-testid=""selectable-text""]
// Buscamos cualquier elemento con ese ID que contenga un ""+""
var potentialNumbers = Array.from(document.querySelectorAll('span[data-testid=""selectable-text""]'));
for (var i = 0; i < potentialNumbers.length; i++) {
var el = potentialNumbers[i];
var text = (el.textContent || '').trim();
// Solo tomamos números que empiecen con + y tengan longitud válida
if (text.startsWith('+') && text.replace(/\D/g, '').length >= 10) {
// IMPORTANTE: El panel lateral siempre está a la derecha (más del 50% del ancho)
var rect = el.getBoundingClientRect();
if (rect.left > (window.innerWidth / 2)) {
return text.replace(/[^\d+]/g, '');
}
}
}
}
if (shouldScan) {
// Debounce ligero
if (window.scanTimeout) clearTimeout(window.scanTimeout);
window.scanTimeout = setTimeout(window.scanForMedia, 1000);
}
});
// Conectar al contenedor principal de la app
const appRoot = document.getElementById('app') || document.body;
observer.observe(appRoot, { childList: true, subtree: true });
// Fallback: ejecutar scan ocasionalmente por si acaso
setInterval(window.scanForMedia, 10000);
// Si llegamos aquí, el número no se encontró en la parte derecha
// Intentamos abrir el panel clicando en la cabecera del chat
var header = document.querySelector('#main header');
if (header) {
log('[WAPP] No se detecta número en la derecha. Intentando abrir/recargar panel lateral...');
// Clicamos en la zona del nombre/avatar
var clickArea = header.querySelector('div[role=""button""]') ||
header.querySelector('div._akz_') ||
header;
triggerHumanClick(clickArea);
}
return null;
};
window.openUnreadChats = async function() {
var markers = Array.from(document.querySelectorAll('span[aria-label]')).filter(function(s) {
var lb = (s.getAttribute('aria-label') || '').toLowerCase();
return (lb.indexOf('unread') !== -1 || lb.indexOf('leído') !== -1 || lb.indexOf('leido') !== -1) && /^\d+$/.test(s.textContent.trim());
});
if (markers.length > 0) {
for (var i = 0; i < markers.length; i++) {
try {
var marker = markers[i];
var row = marker.closest('div[role=""listitem""]') ||
marker.closest('div[role=""gridcell""]') ||
marker.closest('[data-testid^=""list-item""]');
if (!row || row.getAttribute('aria-selected') === 'true' || row.querySelector('[aria-selected=""true""]')) continue;
log('[WAPP] >>> Navegando a chat pendiente...');
var target = row.querySelector('div[data-testid=""cell-frame-container""]') || row;
triggerHumanClick(target);
await new Promise(r => setTimeout(r, 6000));
break;
} catch (e) { log('Error nav: ' + e.message); }
}
}
};
window.scanForMedia = async function() {
// 1. Descargas pendientes (kB/MB)
var btns = document.querySelectorAll('div[role=""button""]');
for (var i = 0; i < btns.length; i++) {
var text = btns[i].textContent || '';
if (text.match(/\d+ (kB|MB)/) && !btns[i].getAttribute('data-scan-clicked')) {
log('[WAPP] Forzando clic de descarga: ' + text);
btns[i].setAttribute('data-scan-clicked', 'true');
triggerHumanClick(btns[i]);
return;
}
}
// 2. Identificación ESTRICTA (Solo derecha de la pantalla)
var iden = window.getIdentityStrict();
if (!iden) {
// Espera más larga para que el panel cargue número (+54...)
await new Promise(r => setTimeout(r, 5000));
iden = window.getIdentityStrict();
}
if (!iden) {
console.error('[WAPP] ERROR CRÍTICO: No se encontró número con ""+"" en el panel lateral derecho. Abortando guardado.');
return;
}
// 3. Captura de contenido
var messages = document.querySelectorAll('.message-in, [data-testid=""msg-container""]');
for (var j = 0; j < messages.length; j++) {
var msg = messages[j];
var msgId = msg.getAttribute('data-id') || msg.closest('[data-id]')?.getAttribute('data-id') || '';
var contents = msg.querySelectorAll('img[src^=""blob:""], video[src^=""blob:""]');
for (var k = 0; k < contents.length; k++) {
var media = contents[k];
var url = media.src || media.getAttribute('src');
if (!url || !url.startsWith('blob:')) continue;
var key = msgId + '_' + k;
if (window.processedMessages.has(key)) continue;
window.processedMessages.add(key);
log('[WAPP] Número validado: ' + iden + '. Guardando archivo...');
try {
await new Promise(r => setTimeout(r, 2000));
var res = await fetch(url);
var b = await res.blob();
var rd = new FileReader();
rd.readAsDataURL(b);
rd.onloadend = function() {
var b64 = rd.result.split(',')[1];
window.onMediaDownloaded(iden, b64, b.type);
};
} catch (err) { log('Error de guardado: ' + err.message); }
}
}
};
var obs = new MutationObserver(function(muts) {
var hasNew = false;
for(var m=0; m<muts.length; m++) { if(muts[m].addedNodes.length > 0) { hasNew = true; break; } }
if (hasNew) {
if (window.st) clearTimeout(window.st);
window.st = setTimeout(window.scanForMedia, 2000);
}
});
var startMonitor = function() {
var target = document.getElementById('app') || document.body;
if (!target) { setTimeout(startMonitor, 1000); return; }
obs.observe(target, { childList: true, subtree: true });
log('[WAPP] Monitor activo');
};
startMonitor();
setInterval(window.scanForMedia, 14000);
setInterval(window.openUnreadChats, 11000);
})();
";
}
}

View File

@@ -85,6 +85,11 @@ namespace WhatsappPromo.Worker
var page = await _browserService.GetPageAsync();
// Configurar bindings
await page.ExposeFunctionAsync("onLog", (Func<string, Task>)(async (string msg) =>
{
await SafeSendAsync("LogUpdate", $"[DEBUG] {msg}");
}));
await page.ExposeFunctionAsync("onNewMessage", (Func<string, Task>)(async (string jsonMessage) =>
{
await SafeSendAsync("LogUpdate", $"Mensaje recibido: {jsonMessage}");
@@ -113,7 +118,7 @@ namespace WhatsappPromo.Worker
}, stoppingToken);
}));
await page.EvaluateFunctionOnNewDocumentAsync(JsSnippets.MediaExtractor);
await page.EvaluateExpressionOnNewDocumentAsync(JsSnippets.MediaExtractor);
await page.ReloadAsync();
// Procesamiento en segundo plano