📞 Confirmación de pedidos COD por voz
Archivo: /Users/user/rifai-agents/agentes/voice/retell-confirm-cod.ts · Plist: com.rifai.voice-confirm-cod · Horario: cada 5 min (StartInterval 300s), pero solo llama dentro de la ventana 10:00–22:00 hora de Madrid y nunca en domingo.
Qué hace
Detecta pedidos contra reembolso (COD) nuevos en Shopify y lanza una llamada telefónica automática con Vapi para que un agente de IA confirme el pedido con el cliente antes de prepararlo y enviarlo. Es la primera pieza del flujo: descubre el pedido, normaliza el teléfono, dispara la llamada y deja el pedido marcado como LLAMANDO en Shopify. El resultado real (confirmado/rechazado/pendiente) lo procesa después el webhook (server.ts), no este agente. Evita llamadas duplicadas usando la base SQLite de llamadas. A pesar del nombre "retell", usa Vapi como proveedor de voz.
Cómo funciona
1. Carga .env con el snippet estándar (sin dotenv).
2. Comprueba isCallableNow(): si es domingo o fuera de 10–22h Madrid, sale sin hacer nada.
3. Pide a Shopify los pedidos status=open, fulfillment_status=unfulfilled creados en las últimas 6h (máx 20).
4. Filtra los que son COD (gateway contiene cod/cash o la nota contiene "contra") y descarta los que ya tengan tag confirmado/rechazado/llamando/problema.
5. Para cada candidato: salta si ya fue llamado en las últimas 24h (isOrderAlreadyCalled); extrae y normaliza el teléfono a E.164 español; convierte nombre, número, total e items a texto pronunciable en español con prepareVoiceVariables.
6. Lanza la llamada con createOutboundCall (Vapi POST /call/phone). Si responde con id, registra el inicio en SQLite (recordCallStart) y añade el tag LLAMANDO al pedido en Shopify.
7. Throttle de 1 llamada cada 2s. Al final, si lanzó alguna, notifica por Telegram (ops_pedido_pending).
Datos/APIs
- Shopify Admin REST (
2024-10): listar pedidos y hacer PUT del tagLLAMANDO. Vars:SHOPIFY_STORE,SHOPIFY_ACCESS_TOKEN. - Vapi (vía
lib/vapi-client.ts): outbound call. Vars:VAPI_API_KEY,VAPI_ASSISTANT_ID_COD_CONFIRM,VAPI_PHONE_NUMBER_ID(oVAPI_FROM_NUMBERcomo fallback Twilio). - SQLite
data/voice-calls.db(víalib/voice-db.ts): dedupe y registro de llamadas. - Telegram vía
tools/notify-router.ts(eventoops_pedido_pending→ Oscar Ops). - Helpers internos:
lib/phone-normalizer.ts,lib/spanish-converter.ts.
Cómo probarlo
cd /Users/user/rifai-agents && npx tsx agentes/voice/retell-confirm-cod.ts
Esperado: imprime la cabecera con timestamp, el nº de pedidos abiertos y de COD pendientes. Si está fuera de horario (domingo o no 10–22h Madrid) sale con ⏸. Si hay COD callables, imprime 📲 Llamando ... por cada uno y un resumen 📊 Lanzadas: N. OJO: no es dry-run puro — si hay pedidos COD válidos, lanza llamadas reales de pago. Para probar sin llamar, ejecútalo fuera de la ventana horaria o sobre una tienda sin pedidos COD recientes.
Si se rompe / recuperar
Recargar el plist:
launchctl unload ~/Library/LaunchAgents/com.rifai.voice-confirm-cod.plist
launchctl load ~/Library/LaunchAgents/com.rifai.voice-confirm-cod.plist
Logs: /Users/user/rifai-agents/logs/voice-confirm-cod.out.log y .err.log. Si "no llama": revisar horario Madrid, que existan pedidos COD recientes, que VAPI_API_KEY/VAPI_PHONE_NUMBER_ID estén en .env, y que no estén ya marcados como llamados en data/voice-calls.db.
Cómo replicarlo
- Snippet de carga de
.env. - Cliente Vapi (
createOutboundCall→POST https://api.vapi.ai/call/phoneconassistantId,customer,assistantOverrides.variableValues/metadata). - Assistant Vapi de confirmación COD ya creado (
VAPI_ASSISTANT_ID_COD_CONFIRM) + número saliente (VAPI_PHONE_NUMBER_ID). - Normalizador de teléfono ES→E.164 y ventana horaria (
phone-normalizer.ts). - Conversor a español hablado (
spanish-converter.ts). - SQLite para dedupe (
voice-db.ts). - Acceso Shopify Admin con
read_orders/write_orders(tags). - Router de notificaciones Telegram.
- Plist launchd con
StartInterval300.