- packages/telephony: TelephonyProvider interface (agente.md secao 25) and
FreeSwitchTelephonyProvider implementation over the 'esl' library
(actively maintained, TypeScript-native, built-in reconnect-with-backoff
satisfying secao 195); normalizeEslEvent() translates raw ESL events into
the internal vocabulary (secao 24)
- apps/freeswitch-events (b2bcall-fs-events): permanent ESL connection,
resubscribes on every reconnect, publishes normalized events to the
'b2bcall:events' Redis pub/sub channel; containerized (Dockerfile +
docker-compose service) since its whole job is reaching the freeswitch
container by internal hostname
- packages/shared: reusable createLogger() (structured JSON per secao 189),
fixed a BigInt serialization crash surfaced by the esl library's error
stats
- found and fixed a real FreeSWITCH 1.11 default: without an explicit
apply-inbound-acl, mod_event_socket silently rejects any non-loopback
connection ('Access Denied, go away.') even with the correct password —
added a dedicated ACL (loopback + the Docker Compose network range, never
0.0.0.0/0) in infrastructure/freeswitch/overrides/autoload_configs/
- verified end-to-end with a local loopback test call: CALL_CREATED ->
CALL_ANSWERED -> CALL_ENDED observed on the Redis channel with the
correct callUuid and hangup cause
- docs/EVENT_SOCKET.md
3.6 KiB
Event Socket
b2bcall-fs-events (apps/freeswitch-events) mantém a conexão ESL permanente
com o FreeSWITCH (agente.md secao 21) — nenhum outro serviço deve rodar
fs_cli via shell pra ações operacionais.
Biblioteca
Usa esl (v11, mantida ativamente,
TypeScript nativo, zero dependências de libesl). A classe FreeSwitchClient
já resolve reconexão com backoff sozinha (agente.md secao 195) — só precisamos
reagir a connect/reconnecting/error e resubscrever a cada connect
(a lib entrega um objeto de chamada novo a cada reconexão).
packages/telephony
TelephonyProvider(interface, agente.md secao 25) +FreeSwitchTelephonyProvider(implementação sobreesl). Métodos testados manualmente contra o FreeSWITCH rodando:originate,killCall,getChannels,getCalls,getGateways,getRegistrations,reloadXml. Os métodos de fila/agente (setAgentStatus,addAgentToQueue, ...) seguem a sintaxe documentada domod_callcentermas não foram exercitados contra uma fila real ainda — não existe nenhuma (fase Queues).normalizeEslEvent(): traduz eventos ESL crus pro vocabulário interno (agente.md secao 24). Mapeamento decallcenter::info→AGENT_STATUS_CHANGEDé best-effort (nomes de campo inferidos da documentação, não testados — revisar na fase Queues/Agents).
Achados durante os testes
- ACL implícita do Event Socket: sem
apply-inbound-aclexplícito, o FreeSWITCH 1.11 rejeita ("Access Denied, go away.") qualquer conexão que não seja loopback — mesmo com a senha certa. Descoberto porqueb2bcall-fs-events(outro container) não conseguia conectar. Corrigido criando uma ACL própria (b2bcall_internal, emoverrides/autoload_configs/acl.conf.xml) cobrindo loopback + a rede interna do Docker Compose (172.16.0.0/12, nunca0.0.0.0/0). - Nessa mesma correção, um erro de digitação inicial (usar só
localnet.auto, que cobre a rede Docker mas não loopback) quebrou até ofs_clilocal — corrigido combinando as duas faixas na mesma ACL. - O logger JSON de
packages/sharedquebrava (TypeError: Do not know how to serialize a BigInt) porque a libeslusabigintnos campos de estatística de erro. Corrigido com umreplacernoJSON.stringify.
Eventos consumidos e publicados
Lista completa em apps/freeswitch-events/src/main.ts
(SUBSCRIBED_EVENTS), cobrindo a secao 23 do agente.md. HEARTBEAT só é
logado em debug, nunca normalizado. Eventos normalizados são publicados em
JSON no canal Redis b2bcall:events (pub/sub simples — vira a base pra
WebSocket multi-tenant na fase Realtime Monitoring, que ainda não existe).
Verificado ponta a ponta
Sem SIP real disponível ainda, a verificação usou uma chamada loopback local:
docker exec b2bcall-freeswitch fs_cli -p "$ESL_PASSWORD" -x "originate null/_test_ &park()"
# ... uuid_kill pra encerrar
Resultado observado no canal Redis: CALL_CREATED → CALL_ANSWERED →
CALL_ENDED (com hangupCause), todos com o callUuid correto.
Limitações desta fase
- Reconciliação pós-reconexão (secao 195: "reconcile calls, agents, queues,
registrations, gateways") não é possível ainda — não existem tabelas de
calls/agents/queuespersistidas pra reconciliar contra. Só a resubscrição de eventos está implementada. Revisitar quando essas tabelas existirem. b2bcall-fs-eventsroda viatsxdireto (sem etapa de build/dist) — simples mas ~99MB de RAM em runtime (razoável no orçamento atual, mas vale revisar se muitos workers assim rodarem juntos mais pra frente).