--- name: whatsapp_send_message kind: function lang: py domain: browser version: "1.0.0" purity: impure signature: "def whatsapp_send_message(name: str, text: str, *, port: int = 9222, target_url_substr: str = 'whatsapp', open_first: bool = True) -> dict" description: "Envia un mensaje de texto 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), verifica que el composer apunta al destinatario correcto antes de escribir (salvaguarda anti-envio-equivocado), teclea el texto con teclado CDP real (unico metodo que funciona con el editor Lexical), comprueba que el composer tiene exactamente el texto y envia con Enter. Accion con efecto: envia un mensaje DE VERDAD, no reversible." tags: [whatsapp, cdp, browser, automation, python, navegator] uses_functions: [whatsapp_open_chat_py_browser, cdp_eval_py_browser, cdp_type_chars_py_browser, cdp_press_key_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 escribir." - name: text desc: "Texto a enviar. Se teclea con teclado CDP real caracter a caracter. Enter lo envia (no inserta salto de linea); multilinea no soportado." - 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 enviar. Si False, asume el chat ya abierto pero verifica el aria-label del composer contra name antes de escribir (aborta si no coincide)." output: "dict {sent: bool, name: str, last_row: str (texto de la ultima fila de #main tras enviar, si sent=True), reason: str (motivo del fallo, si sent=False), composer: str (contenido real del composer cuando hubo mismatch de texto)}. sent=True solo si el composer contenia exactamente el texto y se pulso Enter. Nunca lanza: los fallos se reportan en 'sent' + 'reason'." tested: true tests: ["test_golden_envia_mensaje_y_devuelve_last_row", "test_edge_open_fallido_sent_false_reason", "test_seguridad_open_first_false_label_no_coincide_aborta_sin_escribir", "test_mismatch_composer_sent_false_sin_press_enter"] test_file_path: "python/functions/browser/whatsapp_send_message_test.py" file_path: "python/functions/browser/whatsapp_send_message.py" --- ## Ejemplo ```python import sys, os sys.path.insert(0, os.path.join("python", "functions")) from browser.whatsapp_send_message import whatsapp_send_message # Requiere WhatsApp Web abierto y logueado en un Chrome lanzado con # --remote-debugging-port=9222. res = whatsapp_send_message("NOTAS WASAP", "hola desde el registry") print(res) # -> {"sent": True, "name": "NOTAS WASAP", "last_row": "hola desde el registry 11:40"} ``` O directo por CLI: `python3 python/functions/browser/whatsapp_send_message.py "NOTAS WASAP" "hola desde el registry"`. ## Cuando usarla Cuando necesites **enviar un texto a un contacto o grupo por su nombre exacto** en WhatsApp Web, sin abrir ventana nueva ni robar el foco al usuario. Compone `whatsapp_open_chat` (abre y localiza el chat) con las primitivas CDP de teclado para escribir y enviar. Es el paso de "envio" del navegador diario: usala cuando ya tienes el nombre exacto del destinatario y un texto que mandar. ## Gotchas - **Accion con efecto: envia un mensaje DE VERDAD.** No es reversible (WhatsApp no permite des-enviar por API ni por CDP). Verifica que `name` es EXACTO antes de llamar. - **Viola los ToS de WhatsApp**: automatizar la web tiene riesgo de ban del numero personal. Usar con cautela y bajo tu responsabilidad. - **Salvaguarda anti-destinatario-equivocado**: antes de escribir, verifica que el composer apunta a `name` (via `whatsapp_open_chat` con `open_first=True`, o leyendo el aria-label con `open_first=False`). Si no coincide, aborta con `sent=False` sin teclear nada. - **Doble salvaguarda de contenido**: tras teclear, re-lee el `innerText` del composer y solo pulsa Enter si coincide EXACTAMENTE con `text`. Si no, devuelve `sent=False`, `reason` y el `composer` real, sin enviar. - **El texto se escribe con teclado CDP real** (`cdp_type_chars`). NO usar `execCommand`/`el.value`: el editor Lexical de WhatsApp los ignora y produce texto duplicado/intercalado (gotcha real observado en pruebas). - **`Enter` envia** (no inserta salto de linea). Para multilinea habria que usar Shift+Enter (no implementado aqui). - **Funciona con la ventana minimizada o sin foco**: CDP opera la pestana sin necesidad de que Chrome este en primer plano. - El `name` debe ser **EXACTO**; un contacto no cargado en la lista lateral puede no encontrarse al abrir (ver gotchas de `whatsapp_open_chat`).