# 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. ## Contenido configurable Buena parte de lo que responde el bot vive en la base, no en el código: respuestas automáticas por palabra clave y menús interactivos. Ver [Menús y respuestas automáticas](?m=soporte&v=documentacion&s=tecnica&d=bot-menus-y-respuestas). ## `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 |