Root cause: WpWebhook called OutboundWorker::processQueue() after every
incoming message. BotRouter already sends synchronously in enqueueResponse();
failed messages stayed status='queued'. On the next incoming message
processQueue() would re-send ALL previously-failed messages, causing
the bot to repeat old responses.
- Remove OutboundWorker::processQueue() from WpWebhook::processEvent()
BotRouter handles immediate delivery; retries should be manual/cron only
- Fix chatSend(): instead of queuing + processQueue() (which drains ALL
queued messages), call WhatsAppSender::sendText() directly for the
one message being sent
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Text/interactive/button messages → NormalBot only, never escalates to AI
- Image/audio/video/document → AiBot.processMedia() if ai_for_media enabled,
else falls back to category greeting menu
- AiBot.processMedia() uses permission_type context so AI acknowledges uploads
for perm 3 and redirects others to their menu
- WpWebhook.handleMedia now calls BotRouter so the bot responds to media
- Admin UI: checkbox toggle "Procesar imágenes y archivos con IA" in AI tab
- categoryMenuFallback() extracted in BotRouter as shared helper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Saves a raw entry to webhook_logs immediately on any POST to the webhook,
before HMAC check or JSON parsing. Shows HMAC status (ok/invalida/sin-firma).
Live feed now shows all traffic including rejected and malformed payloads.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- resolveCompanyByNumber(): busca el número en company_phones, identifica empresa
- sendRejectionMessage(): responde "su número no se encuentra habilitado" via WA API
- handleMessages(): por cada mensaje resuelve empresa por remitente; si no está
registrado, guarda log y envía rechazo automático
- permission_type disponible en context para BotRouter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>