--- 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 vivo, asigna la imagen con cdp_set_file_input (DOM.setFileInputFiles), espera la bandeja inline y hace click en el boton enviar (icono wds-ic-send-filled) verificando que la bandeja se cerro. Si hay caption, lo envia como mensaje de texto de seguimiento via whatsapp_send_message (en la WhatsApp Web compacta actual el caption embebido en la imagen no es automatizable de forma fiable, asi que viaja como segunda burbuja [imagen][caption]). 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_set_file_input_py_browser, whatsapp_send_message_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 descriptivo. Se envia como un MENSAJE DE TEXTO de seguimiento (segunda burbuja [imagen][caption]) via whatsapp_send_message; '' (default) envia solo la imagen. La WhatsApp Web compacta actual no permite automatizar el caption embebido en la imagen de forma fiable." - 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 (imagen + caption enviados), sent: bool (imagen enviada), caption_sent: bool (caption de seguimiento enviado, False si no habia o fallo), recipient: str, image: str (ruta absoluta), caption: str, error: str (motivo del fallo, vacio si todo ok)}. sent=True solo si la imagen se adjunto y se envio dejando la bandeja vacia. Nunca lanza: los fallos se reportan en 'sent'/'ok' + 'error'." tested: true tests: ["test_golden_envia_imagen_y_caption_de_seguimiento", "test_envia_sin_caption_no_manda_texto", "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 compacta** sobre el composer (no un drawer a pantalla completa). - **El caption NO se embebe en la imagen: viaja como mensaje de texto de seguimiento.** En esta WhatsApp Web compacta hay dos botones de envio cuando hay media: "Enviar N seleccionados" (envia la bandeja, IGNORA el texto del composer) y "Enviar"/Enter (envia el texto como burbuja aparte, descartando la media en cola). No hay un campo de caption por-imagen automatizable de forma fiable. Por eso la funcion envia primero la imagen (boton de la bandeja) y, si hay `caption`, lo manda despues como mensaje de texto via `whatsapp_send_message` (`open_first=False`): el resultado es [imagen][caption] como dos burbujas. `caption_sent` indica si esa segunda burbuja salio. - **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`. - **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.