Files
B2BCall-dialer/docs/QUEUES.md
Matheus 6628578c42 feat: implement Call Center queues (mod_callcenter)
- Investigated the real callcenter_config command surface via
  'help callcenter_config' on the running FreeSWITCH before writing any
  code: queues only have load/unload/reload (static XML + reload, no
  'queue add' exists), while agents and tiers are fully dynamic via ESL
  commands (agent add, tier add) -- no file involved. This shapes the next
  phase (Agents/Tiers) differently from this one.
- queues table (tenant-scoped, RLS): strategy, moh/announce, wait times,
  tier rules, discard/abandoned handling, skip-agents-with-external-calls,
  recording_enabled
- packages/telephony: buildQueueXml()
- infrastructure/freeswitch: our own callcenter.conf.xml override (empties
  the vanilla static agents/tiers -- those become fully dynamic in the next
  phase) that includes callcenter_queues.conf.d/*.xml via X-PRE-PROCESS,
  same pattern as the Sofia gateway directory
- apps/api/src/queues: CRUD (POST/GET/GET:id/DELETE) using the queues.view/
  .manage permissions already in the seed
- b2bcall-fs-config (queue-sync.ts): one XML file per queue on a shared
  volume, synced via Redis pub/sub (b2bcall:queues:sync) on create/delete
  and once at boot -- same shape as trunk-sync.ts
- confirmed manually against the real FreeSWITCH, before coding the sync
  logic: 'queue load <name>' fails ('Invalid Queue not found!') for a file
  added after boot -- needs 'reloadxml' first to repopulate the in-memory
  XML tree from disk; after that, 'queue reload <name>' alone handles both
  create and update, no need to distinguish load vs reload
- verified end-to-end: created a queue (ROUND_ROBIN, maxWaitTime=120,
  discardAbandonedAfter=90), 'callcenter_config queue list' showed the
  correct values on the FreeSWITCH side; deleted it, list went back empty

docs/QUEUES.md
2026-08-28 09:15:11 -03:00

3.7 KiB

Filas (mod_callcenter)

Agente.md secao 37 (mod_callcenter como ACD) e 50-51 (Filas/Estratégias). Primeira peça do Call Center — Agentes/Tiers/Pausas ficam pra próxima fase (dependem do fluxo de login do agente, seção 45-49, escopo maior).

Descoberta real sobre callcenter_config

Antes de escrever qualquer código, rodei help callcenter_config no FreeSWITCH real pra ver a sintaxe exata — e ela é bem diferente do que os Trunks fizeram supor:

  • Filas: só queue load/unload/reload/listnão existe queue add nem queue set param. Filas só podem vir de XML estático, carregado/recarregado por nome.
  • Agentes: agent add/del/set status/set state/... — 100% dinâmico via comando ESL, sem XML.
  • Tiers: tier add/del/set state/set level/set position — também 100% dinâmico.

Ou seja, filas usam o mesmo padrão "arquivo + reload" dos Trunks; agentes e tiers (próxima fase) vão usar comandos ESL diretos, sem arquivo nenhum.

Mecanismo

Mesmo padrão dos gateways Sofia: um arquivo XML por fila (autoload_configs/callcenter_queues.conf.d/<queue_id>.xml, volume Docker compartilhado), incluído via X-PRE-PROCESS no nosso callcenter.conf.xml próprio (agente.md secao 15 — "carregar somente o necessário": zeramos os <agents>/<tiers> estáticos da vanilla, já que essas partes vão ser 100% dinâmicas).

Sequência de comandos confirmada manualmente contra o FreeSWITCH real (testei cada passo antes de escrever o código):

  1. queue load <nome> falha ("Invalid Queue not found!") se o arquivo foi adicionado depois do boot — a árvore XML em memória não sabe do arquivo novo ainda.
  2. reloadxml primeiro repopula essa árvore a partir do disco.
  3. Depois disso, queue reload <nome> sozinho já serve tanto pra criar quanto atualizar — não precisa distinguir load de reload.
  4. Fila removida: apagar o arquivo, reloadxml, queue unload <nome>.

b2bcall-fs-config (queue-sync.ts) reescreve todos os arquivos de fila habilitada (todos os tenants) a cada sync, remove os obsoletos, roda reloadxml uma vez, depois queue reload por fila desejada e queue unload por fila removida. Disparado por Redis pub/sub (b2bcall:queues:sync, mesmo mecanismo dos Trunks) a cada create/delete via API, e uma vez no boot do serviço.

Nome da fila no FreeSWITCH

<queue.id>@<tenant.telephonyDomain> — mesma convenção UUID dos gateways. Como o telephonyDomain hoje é compartilhado entre tenants (limitação já documentada em docs/EXTENSIONS.md), o unload de uma fila apagada usa o domain de "qualquer tenant ativo" como aproximação, já que o registro no banco já não existe mais nesse ponto pra sabermos o domain exato.

Verificado ponta a ponta

POST /queues {"name":"Suporte","strategy":"ROUND_ROBIN","maxWaitTime":120,"discardAbandonedAfter":90}
→ fs-config sincroniza (desired:1) → reloadxml + queue reload

callcenter_config queue list
→ <id>@b2bcall.local|round-robin|...|90|false|120|...  (parâmetros batem)

DELETE /queues/:id
→ fs-config sincroniza (removed:1) → reloadxml + queue unload
→ callcenter_config queue list volta vazia

O que falta

  • Agentes, Tiers, Pausas (secao 45-49) — fase separada, mecanismo é puramente via comando ESL (agent add, tier add), sem arquivo.
  • Monitoramento em tempo real das filas (secao 54) — depende de WebSocket multi-tenant, que ainda não existe.
  • tier-rule-wait-multiply-level e tier-rule-no-agent-no-wait (vistos no queue list da config vanilla) não são expostos como campos próprios ainda — ficaram de fora do escopo desta fase.
  • Quota de filas (max_queues) — depende de Plans/Entitlements.