---
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 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 menu "Adjuntar" es un TOGGLE (`aria-expanded`).** El `<input type=file>` solo queda "vivo"
  mientras el menu esta ABIERTO; asignar al input con el menu cerrado es un decoy (no abre preview).
  Clickar "Adjuntar" cuando YA esta abierto lo CIERRA. Por eso la funcion clicka solo si
  `aria-expanded != "true"` y reintenta hasta verlo abierto (no un click ciego). La WhatsApp Web
  actual usa una **bandeja de medios INLINE compacta** sobre el composer (no un drawer a pantalla
  completa).
- **El envio se verifica por la ultima fila de `#main`, NO por contar filas.** Las filas de `#main`
  se VIRTUALIZAN (las antiguas se desmontan al llegar nuevas), asi que el total se mantiene casi
  constante. La funcion confirma el envio comprobando que la bandeja se vacio (adjuntos=0) Y que la
  ultima fila renderizada es ya una imagen (`img[src^="blob:"]`).
- **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.
