feat(browser): auto-commit con 6 cambios

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-26 23:09:32 +02:00
parent f5387aa30e
commit 4c4eec4b1d
6 changed files with 862 additions and 0 deletions
@@ -0,0 +1,94 @@
---
name: whatsapp_send_image
kind: function
lang: py
domain: browser
version: "1.0.0"
purity: impure
signature: "def whatsapp_send_image(name: str, image_path: str, *, caption: str = '', port: int = 9222, target_url_substr: str = 'whatsapp', open_first: bool = True) -> dict"
description: "Envia una imagen (con caption opcional) a un chat de WhatsApp Web en una pestana ya logueada del navegador diario via CDP, sin abrir ventana nueva ni darle foco. Abre el chat por nombre exacto (whatsapp_open_chat) y verifica el destinatario (salvaguarda anti-envio-equivocado), hace click real en 'Adjuntar' para exponer el <input type=file> vivo, asigna la imagen con cdp_set_file_input (DOM.setFileInputFiles), espera el preview de la bandeja inline, teclea el caption opcional con teclado CDP real, y hace click en el boton enviar (icono wds-ic-send-filled) verificando que la bandeja se cerro. Accion con efecto: envia la imagen DE VERDAD, no reversible."
tags: [whatsapp, cdp, browser, automation, image, upload, python, navegator]
uses_functions: [whatsapp_open_chat_py_browser, cdp_eval_py_browser, cdp_click_xy_py_browser, cdp_type_chars_py_browser, cdp_set_file_input_py_browser]
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: ["os", "sys", "time", "json"]
params_schema:
params:
- name: name
desc: "Nombre EXACTO del chat o grupo destinatario tal y como aparece en la lista lateral. Se usa para abrir el chat y como salvaguarda de que el composer apunta al destinatario correcto antes de adjuntar."
- name: image_path
desc: "Ruta de la imagen a enviar. Se expande (~) y se convierte a ruta ABSOLUTA; debe existir en disco o aborta con error sin abrir el chat."
- name: caption
desc: "Texto opcional que acompana la imagen. Se teclea en el composer con teclado CDP real caracter a caracter; '' (default) envia la imagen sin caption."
- name: port
desc: "Puerto de remote debugging de Chrome. Default 9222."
- name: target_url_substr
desc: "Substring que debe contener la URL del target (pestana). Default 'whatsapp'."
- name: open_first
desc: "Si True (default), abre el chat por su nombre antes de adjuntar. Si False, asume el chat ya abierto pero verifica el aria-label del composer contra name (aborta si no coincide)."
output: "dict {ok: bool (alias de sent), sent: bool, recipient: str, image: str (ruta absoluta), caption: str, error: str (motivo del fallo, vacio si sent=True)}. sent=True solo si la imagen se adjunto, el caption (si lo hay) se verifico y se pulso enviar dejando la bandeja vacia. Nunca lanza: los fallos se reportan en 'sent'/'ok' + 'error'."
tested: true
tests: ["test_golden_adjunta_caption_y_envia", "test_envia_sin_caption_no_teclea", "test_edge_imagen_no_existe_error_sin_abrir", "test_edge_open_fallido_error_sin_adjuntar", "test_seguridad_open_first_false_label_no_coincide_aborta", "test_error_set_file_input_falla_no_envia"]
test_file_path: "python/functions/browser/whatsapp_send_image_test.py"
file_path: "python/functions/browser/whatsapp_send_image.py"
---
## Ejemplo
```python
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from browser.whatsapp_send_image import whatsapp_send_image
# Requiere WhatsApp Web abierto y logueado (y DESBLOQUEADO si tiene app-lock) en un
# Chrome lanzado con --remote-debugging-port=9222.
res = whatsapp_send_image(
"NOTAS WASAP",
"/home/enmanuel/ComfyUI/output/item_icon_potion_00001_.png",
caption="item icon: potion",
)
print(res)
# -> {"ok": True, "sent": True, "recipient": "NOTAS WASAP",
# "image": ".../item_icon_potion_00001_.png", "caption": "item icon: potion", "error": ""}
```
O directo por CLI: `python3 python/functions/browser/whatsapp_send_image.py "NOTAS WASAP" /ruta/abs.png "mi caption"`.
## Cuando usarla
Cuando necesites **enviar una imagen (foto, captura, asset generado) a un contacto o grupo
por su nombre exacto** en WhatsApp Web, sin abrir ventana nueva ni robar el foco al usuario.
Es la version "imagen" de `whatsapp_send_message_py_browser`: usala cuando ya tienes el
nombre exacto del destinatario y la ruta de un archivo de imagen en disco. Para texto plano,
usa `whatsapp_send_message`; para leer/confirmar lo enviado, `whatsapp_read_chat`.
## Gotchas
- **Accion con efecto: envia la imagen DE VERDAD.** No es reversible. Verifica que `name` es
EXACTO antes de llamar (la salvaguarda abre el chat y comprueba el composer, pero el nombre
debe coincidir con el `title` de la lista lateral).
- **App-lock de WhatsApp Web.** Si la cuenta tiene el "bloqueo de la aplicacion" activo, el DOM
de chats no se renderiza (solo la pantalla de password) y la funcion fallara al abrir el chat.
Hay que desbloquearlo primero (teclear el password en `input[type=password]` + boton
"Desbloquear"). Sintoma: `whatsapp_open_chat` devuelve `opened: False` y la lista lateral sale
vacia aunque la sesion siga logueada.
- **El input vivo solo existe tras pulsar "Adjuntar".** Por eso la funcion hace el click real en
el boton "Adjuntar" antes de `setFileInputFiles`; asignar al input persistente decoy no abre el
preview. La WhatsApp Web actual usa una **bandeja de medios INLINE** sobre el composer (no un
drawer a pantalla completa): el caption se escribe en el MISMO composer del footer.
- **Selector de aria-label en espanol.** El preview se detecta por `[aria-label="Quitar archivo
adjunto"]` y el boton de adjuntar por `[aria-label="Adjuntar"]`: dependen del idioma de la UI
(espanol). En otro locale habria que ajustar los aria-labels.
- **Las imagenes se ACUMULAN en la bandeja.** Cada `setFileInputFiles` anade una miniatura; si un
envio queda a medias, la siguiente llamada podria sumar a las pendientes. La funcion verifica que
la bandeja queda vacia tras enviar (adjuntos=0) para confirmar; si no se cierra, devuelve
`sent=False` con "envio incierto".
- **Salvaguarda anti-destinatario-equivocado**: con `open_first=True` abre y verifica el chat; con
`open_first=False` lee el aria-label del composer y aborta si no contiene `name`.
- **Caption verificado**: tras teclear, re-lee el `innerText` del composer y solo envia si coincide
EXACTAMENTE con `caption`; si no, devuelve `sent=False` sin enviar.
- **Funciona con la ventana minimizada o sin foco**: CDP opera la pestana sin traerla a primer plano.
- **Viola los ToS de WhatsApp**: automatizar la web tiene riesgo de ban del numero personal. Usar
con cautela y bajo tu responsabilidad.