feat: add whatsapp_modular_v3_6 - envio automatizado WhatsApp Web con busqueda por nombre, deteccion de ticks via msg-meta/SVG title, y validacion de selectores
This commit is contained in:
133
Python/whatsapp_modular_v3_6/whatsapp_sender/processor.py
Normal file
133
Python/whatsapp_modular_v3_6/whatsapp_sender/processor.py
Normal file
@@ -0,0 +1,133 @@
|
||||
import os
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from .config import EVIDENCIAS_DIR, MAX_REINTENTOS, MINUTOS_ANTICIPACION, WAIT_CHAT
|
||||
from .driver_session import ensure_session_active
|
||||
from .utils import asegurar_directorio, log_text
|
||||
from .whatsapp import abrir_chat, enviar_mensaje, obtener_nombre_chat_actual, validate_sent, verificar_ticks
|
||||
|
||||
|
||||
def construir_resultado(tipo, destino, mensaje, hora, minuto):
|
||||
return {
|
||||
"tipo": tipo,
|
||||
"destino": destino,
|
||||
"mensaje": mensaje,
|
||||
"hora_programada": f"{hora if hora is not None else ''}:{minuto if minuto is not None else ''}",
|
||||
"estado": "NO_INTENTADO",
|
||||
"intento_exitoso": None,
|
||||
"timestamp": None,
|
||||
"captura": None,
|
||||
"nota": "",
|
||||
}
|
||||
|
||||
|
||||
def obtener_hora_minuto(row):
|
||||
try:
|
||||
return int(float(row["hora"])), int(float(row["minuto"]))
|
||||
except Exception:
|
||||
return None, None
|
||||
|
||||
|
||||
def esperar_hora_programada(hora, minuto, destino):
|
||||
if hora is None or minuto is None:
|
||||
return
|
||||
|
||||
ahora = datetime.now()
|
||||
hora_envio = datetime(ahora.year, ahora.month, ahora.day, hora, minuto)
|
||||
if hora_envio <= ahora + timedelta(minutes=MINUTOS_ANTICIPACION):
|
||||
hora_envio = ahora + timedelta(minutes=2)
|
||||
|
||||
espera = (hora_envio - datetime.now()).total_seconds()
|
||||
if espera > 0:
|
||||
print(f"Esperando {int(espera)}s hasta {hora_envio.strftime('%H:%M')} para destino {destino}")
|
||||
time.sleep(espera)
|
||||
|
||||
|
||||
def guardar_captura_evidencia(driver, destino):
|
||||
nombre_chat = obtener_nombre_chat_actual(driver)
|
||||
asegurar_directorio(EVIDENCIAS_DIR)
|
||||
safe = "".join(c for c in nombre_chat if c.isalnum() or c in (" ", "_", "-")).strip().replace(" ", "_")[:40]
|
||||
|
||||
if nombre_chat == "chat_desconocido":
|
||||
nombre_captura = os.path.join(EVIDENCIAS_DIR, f"envio_{destino[:12]}_{int(time.time())}.png")
|
||||
else:
|
||||
nombre_captura = os.path.join(EVIDENCIAS_DIR, f"envio_{safe}_{int(time.time())}.png")
|
||||
|
||||
try:
|
||||
driver.save_screenshot(nombre_captura)
|
||||
return nombre_captura
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def procesar_row(driver, row):
|
||||
tipo = str(row["tipo"]).strip().lower()
|
||||
destino = str(row["id_destino"]).strip()
|
||||
mensaje = str(row["mensaje"])
|
||||
hora, minuto = obtener_hora_minuto(row)
|
||||
resultado = construir_resultado(tipo, destino, mensaje, hora, minuto)
|
||||
|
||||
esperar_hora_programada(hora, minuto, destino)
|
||||
|
||||
for intento in range(1, MAX_REINTENTOS + 1):
|
||||
print(f"Intento {intento}/{MAX_REINTENTOS} -> {tipo} '{destino}'")
|
||||
|
||||
if not ensure_session_active(driver):
|
||||
resultado["estado"] = "ERROR_SESSION"
|
||||
resultado["nota"] = "No se pudo recuperar sesion"
|
||||
resultado["timestamp"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
log_text(f"{destino} | ERROR_SESSION")
|
||||
return resultado
|
||||
|
||||
if not abrir_chat(driver, tipo, destino):
|
||||
resultado["estado"] = "NO_ENCONTRADO"
|
||||
resultado["nota"] = "No se encontro chat/grupo"
|
||||
resultado["timestamp"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
log_text(f"{destino} | NO_ENCONTRADO")
|
||||
return resultado
|
||||
|
||||
try:
|
||||
time.sleep(WAIT_CHAT)
|
||||
if not enviar_mensaje(driver, mensaje):
|
||||
raise Exception("Error al ejecutar accion de envio")
|
||||
|
||||
time.sleep(1)
|
||||
validacion = validate_sent(driver, mensaje, timeout=15)
|
||||
if validacion is True:
|
||||
resultado["estado"] = verificar_ticks(driver)
|
||||
resultado["nota"] = "ENVIADO_CONFIRMED"
|
||||
elif validacion is None:
|
||||
print("La sesion se perdio durante validacion. Intentando recuperacion...")
|
||||
if ensure_session_active(driver):
|
||||
print("Sesion recuperada. Reintentando envio.")
|
||||
time.sleep(2)
|
||||
continue
|
||||
|
||||
resultado["estado"] = "ERROR_SESSION"
|
||||
resultado["nota"] = "Sesion perdida y no recuperada"
|
||||
resultado["timestamp"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
log_text(f"{destino} | ERROR_SESSION durante validacion")
|
||||
return resultado
|
||||
else:
|
||||
resultado["estado"] = "ENVIADO (SIN CONFIRMACION)"
|
||||
resultado["nota"] = "ENVIADO_SIN_CONFIRMACION"
|
||||
|
||||
resultado["captura"] = guardar_captura_evidencia(driver, destino)
|
||||
resultado["intento_exitoso"] = intento
|
||||
resultado["timestamp"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
log_text(f"{destino} | {resultado['estado']} | intento {intento} | captura:{resultado['captura']}")
|
||||
print(f"Resultado para {destino}: {resultado['estado']}")
|
||||
return resultado
|
||||
except Exception as exc:
|
||||
print(f"Error en intento {intento} con {destino}: {exc}")
|
||||
log_text(f"{destino} | ERROR intento {intento}: {exc}")
|
||||
resultado["nota"] = str(exc)
|
||||
if intento < MAX_REINTENTOS:
|
||||
print("Reintentando en 3s...")
|
||||
time.sleep(3)
|
||||
|
||||
resultado["estado"] = "FALLIDO"
|
||||
resultado["timestamp"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
log_text(f"{destino} | FALLIDO despues de {MAX_REINTENTOS} intentos")
|
||||
return resultado
|
||||
Reference in New Issue
Block a user