- 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
75 lines
3.6 KiB
Markdown
75 lines
3.6 KiB
Markdown
# 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`](https://www.npmjs.com/package/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 sobre `esl`). 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 do
|
|
`mod_callcenter` mas 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 de `callcenter::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
|
|
|
|
1. **ACL implícita do Event Socket**: sem `apply-inbound-acl` explícito, o
|
|
FreeSWITCH 1.11 rejeita ("Access Denied, go away.") qualquer conexão que
|
|
não seja loopback — mesmo com a senha certa. Descoberto porque
|
|
`b2bcall-fs-events` (outro container) não conseguia conectar. Corrigido
|
|
criando uma ACL própria (`b2bcall_internal`, em
|
|
`overrides/autoload_configs/acl.conf.xml`) cobrindo loopback + a rede
|
|
interna do Docker Compose (`172.16.0.0/12`, nunca `0.0.0.0/0`).
|
|
2. 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é o `fs_cli` local
|
|
— corrigido combinando as duas faixas na mesma ACL.
|
|
3. O logger JSON de `packages/shared` quebrava (`TypeError: Do not know how
|
|
to serialize a BigInt`) porque a lib `esl` usa `bigint` nos campos de
|
|
estatística de erro. Corrigido com um `replacer` no `JSON.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:
|
|
|
|
```bash
|
|
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`/`queues` persistidas pra reconciliar contra. Só a
|
|
resubscrição de eventos está implementada. Revisitar quando essas tabelas
|
|
existirem.
|
|
- `b2bcall-fs-events` roda via `tsx` direto (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).
|