---
name: whatsapp_open_chat
kind: function
lang: py
domain: browser
version: "1.0.0"
purity: impure
signature: "def whatsapp_open_chat(name: str, *, port: int = 9222, target_url_substr: str = 'whatsapp', wait_s: float = 1.3) -> dict"
description: "Abre un chat de WhatsApp Web por su nombre exacto en una pestana ya logueada del navegador diario via CDP, sin abrir ventana nueva ni darle foco. Busca por nombre, localiza el chat por su ancla estable span[title] dentro de #side, hace click de raton real y verifica que abrio leyendo el aria-label del composer. Base de whatsapp_read_chat y whatsapp_send_message."
tags: [whatsapp, cdp, browser, automation, python, navegator]
uses_functions: [cdp_eval_py_browser, cdp_type_chars_py_browser, cdp_press_key_py_browser, cdp_click_xy_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 tal y como aparece en la lista lateral (match exacto del atributo title del span ancla). Nombres ambiguos abren el primero que matchee."
    - 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: wait_s
      desc: "Segundos de espera tras teclear el nombre para que la lista lateral filtre y renderice los resultados. Default 1.3."
  output: "dict {opened: bool, name: str, composer_label: str (si abrio), reason: str (si no abrio), coords: {x, y} (si encontro el ancla)}. opened=True si el nombre aparece en el aria-label del composer tras el click. Nunca lanza: los fallos se reportan en 'opened' + 'reason'."
tested: true
tests: ["test_golden_abre_chat_y_verifica_composer", "test_edge_ancla_no_encontrada_opened_false", "test_click_usa_coords_devueltas_por_el_ancla"]
test_file_path: "python/functions/browser/whatsapp_open_chat_test.py"
file_path: "python/functions/browser/whatsapp_open_chat.py"
---

## Ejemplo

```python
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from browser.whatsapp_open_chat import whatsapp_open_chat

# Requiere WhatsApp Web abierto y logueado en un Chrome lanzado con
# --remote-debugging-port=9222.
res = whatsapp_open_chat("NOTAS WASAP")
print(res)
# -> {"opened": True,
#     "name": "NOTAS WASAP",
#     "composer_label": "Escribir un mensaje para el grupo NOTAS WASAP",
#     "coords": {"x": 180, "y": 240}}
```

O directo por CLI: `python3 python/functions/browser/whatsapp_open_chat.py "NOTAS WASAP"`.

## Cuando usarla

Cuando necesites **abrir un chat concreto** de WhatsApp Web antes de leerlo
(`whatsapp_read_chat`) o de enviar un mensaje (`whatsapp_send_message`). Es el paso
base de ambas: el chat tiene que estar abierto (composer apuntando a el) para que las
otras funciones operen sobre la conversacion correcta. Util para automatizar el
navegador diario sin abrir ventana nueva ni robar el foco al usuario.

## Gotchas

- **Viola los ToS de WhatsApp**: automatizar la web tiene riesgo de ban del numero. Usar con cautela y bajo tu responsabilidad.
- El `name` debe ser **EXACTO** (match exacto de `span[title]`). Nombres ambiguos (varios chats que matchean) abren el primero que aparezca en la lista.
- El buscador **no filtra de forma fiable contactos NO cargados** en la lista lateral: funciona para chats recientes/visibles. Un contacto sin chat reciente puede no aparecer (limitacion conocida; futura mejora: scroll en la lista lateral antes de buscar).
- Usa **click de raton real** (`cdp_click_xy`). Un `element.click()` JS NO abre el chat porque los handlers de React no reaccionan a eventos sinteticos del DOM.
- **Funciona con la ventana minimizada o sin foco**: CDP opera la pestana sin necesidad de que Chrome este en primer plano.
- **`Escape` no limpia el buscador**: el texto se acumula entre llamadas. La funcion hace `input.select()` + `Backspace` antes de teclear el nombre nuevo.
- Si el ancla existe pero esta fuera del viewport (`b.y<0` o ancho 0), devuelve `opened=False` con `reason="chat fuera de viewport (scroll necesario)"` en vez de clicar a ciegas.
