# WhatsApp y bot

El sistema nació como bot de WhatsApp y esa integración sigue siendo central.

## Servicios

{{servicios}}

## `WhatsAppService`

Envuelve la Cloud API de Meta. Se elige la línea al construirlo:

```php
$wa = new WhatsAppService();           // principal
$wa = new WhatsAppService('turnero');  // turnero
```

Ambas líneas comparten cuenta (WABA) y token; **lo único que cambia es el `phone_number_id`**. Si un mensaje sale por la línea equivocada, casi siempre falta el argumento.

Métodos principales:

```php
$wa->sendTextMessage($telefono, $texto, $meta);
$wa->sendTemplateMessage($telefono, $plantilla, $idioma, [], [], $componentes, $meta);
```

`$meta` acompaña el registro del mensaje: `['canal' => 'turnero', 'operator_id' => adminId()]`.

## Plantillas

Fuera de la ventana de 24 horas hay que usar plantilla aprobada. `message_templates` guarda una copia local con sus `components`, pero **la copia no manda**: la versión real vive en Meta.

Consecuencia importante:

> Las URL de los botones están **en la plantilla**, no en el código. Nuestro código solo envía los parámetros (`{{1}}`). Cambiar el código no altera el enlace que recibe el paciente.

Ejemplo — botón de `consentimiento_turno_v2`:

```
https://erp.laboratorioximenacaicedo.com/form_cliente.php?t={{1}}
```

Y así se arman los componentes al enviar:

```php
$rawComps = [
    ['type' => 'body',   'parameters' => [['type' => 'text', 'text' => $codigo]]],
    ['type' => 'button', 'sub_type' => 'url', 'index' => '0',
     'parameters' => [['type' => 'text', 'text' => $token]]],
];
```

### Respaldo

Si el envío por plantilla falla, se manda un texto plano con el enlace armado desde el dominio del servidor. Ese texto **no** pasa por Meta, así que su URL puede diferir de la del botón.

## `BotService`

Decide qué hacer con cada mensaje entrante:

1. ¿Aceptó los términos? Si no, se los pide y no avanza.
2. ¿Está en horario? (`BusinessHoursService`)
3. ¿La conversación la tomó un operador humano? El bot no interrumpe.
4. Si no, responde según el estado de la conversación (`ConversationStateService`) y el menú (`MenuService`).

## Términos y condiciones

| Tabla | Contenido |
|---|---|
| `terms_versions` | Versión activa, URL del documento, mensajes |
| `terms_acceptance` | Historial de aceptaciones |
| `system_config.terms_message` | Texto de bienvenida, **repite la URL dentro** |

Se vuelve a pedir la aceptación si nunca aceptó, si pasaron más de 6 meses, o si hay versión nueva con `forzar_reenvio`.

> La URL del documento está en **dos** lugares. Cambiar solo uno deja al otro sirviendo un enlace viejo.

## Configuración

Todo en `system_config`: `whatsapp_token`, `whatsapp_api_url`, `whatsapp_business_account_id`, `whatsapp_phone_number_id`, `whatsapp_phone_number_id_turnero`, `webhook_verify_token`.

## Diagnóstico

| Síntoma | Dónde mirar |
|---|---|
| Sale por el número equivocado | Falta `'turnero'` en el constructor |
| Enlace roto en un botón | La plantilla en Meta |
| No llega ninguna plantilla | Estado de aprobación en WhatsApp Manager |
| Llega texto plano en vez de plantilla | El respaldo actuó: la plantilla falló |
| El bot no responde | `webhook_logs`, horario, estado de la conversación |
