feat(dialplan): IVR — play_and_get_digits + branching por dígito, testado com DTMF real
Continuação de "montar o IVR e as rotas de entrada": com a fundação de
roteamento por DID já funcionando, faltava o menu em si.
`play_and_get_digits` adicionado ao allowlist de applications (só coleta
dígitos numa variable, nunca executa nada — mesmo risco zero dos outros
já permitidos). `ALLOWED_CONDITION_FIELDS` ganhou um segundo modo:
`${variavel}` simples além dos 6 campos fixos, com a mesma garantia
anti-RCE de `data` (o regex exige identificador puro, sem parênteses,
então nunca vira `${funcname(...)}`).
Achado real construindo o primeiro menu de teste: FreeSWITCH resolve
TODAS as condições de um contexto antes de executar qualquer ação — uma
variable setada por `play_and_get_digits` nunca afeta o casamento de
OUTRA extension na mesma passada. Descoberto porque o primeiro desenho
(2 extensions separadas, uma coletando o dígito, outra checando
`${ivr_choice}`) nunca bateu num teste real com DTMF — o regex era
avaliado com a variable ainda vazia. A forma que funciona: um `transfer`
explícito, na MESMA extension, usando o dígito coletado como novo
destination_number — isso dispara uma consulta de dialplan nova (as
variables já setadas persistem), e as extensions seguintes casam por
destination_number normal.
Testado ponta a ponta com DTMF de verdade: como linphonec não tem um
comando de enviar DTMF interativo, usei `fs_cli uuid_recv_dtmf` (API do
FreeSWITCH que simula o dígito chegando como input do chamador, mesmo
caminho que RFC2833 usaria). Softphone externo ligou pro DID, o IVR
atendeu, tocou o prompt, esperou o dígito: "1" bridged com um ramal real
(atendeu, áudio PCMU bidirecional confirmado); "2" foi pra uma resposta
alternativa (tom diferente) — confirma que a ramificação distingue de
verdade, não só cai sempre na primeira opção.
Detalhes completos em docs/INBOUND_ROUTES.md. Falta só a UI dedicada de
autoria de menu (hoje construído à mão no editor genérico de dialplan) —
backend já suporta tudo que ela precisaria gerar.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
32
TODO.md
32
TODO.md
@@ -2063,13 +2063,33 @@ mostrar quantos ramais estao online e o status de cada ramal criado")
|
||||
`INCOMPATIBLE_DESTINATION` — isolado como limitação do canal
|
||||
`loopback` sem SDP real, não um bug da resolução; ver
|
||||
docs/INBOUND_ROUTES.md pro isolamento completo)
|
||||
- [ ] IVR em si (menu com `play_and_get_digits` + branching por dígito
|
||||
coletado) ainda não construído — é a próxima fase. Precisa
|
||||
widening de `ALLOWED_CONDITION_FIELDS` (hoje um enum fixo) pra
|
||||
aceitar `${variavel}` com a MESMA proteção anti-RCE já aplicada em
|
||||
`data` (secao 180), já que `field` também é expandido pelo
|
||||
FreeSWITCH em tempo de chamada. Ver "O que falta" em
|
||||
- [x] **IVR (menu com `play_and_get_digits` + branching por dígito)**:
|
||||
`play_and_get_digits` adicionado ao allowlist de applications;
|
||||
`ALLOWED_CONDITION_FIELDS` ganhou um segundo modo aceitando
|
||||
`${variavel}` simples (regex sem parênteses — nunca vira chamada de
|
||||
API, mesmo risco zero de `${destination_number}` já é hoje)
|
||||
- [x] **achado real construindo o primeiro menu**: FreeSWITCH resolve
|
||||
TODAS as `<condition>` de um contexto ANTES de executar qualquer
|
||||
`<action>` — uma variable setada por `play_and_get_digits` NUNCA
|
||||
afeta o casamento de OUTRA `<extension>` na mesma passada (testado
|
||||
e confirmado com DTMF de verdade: 2 extensions separadas nunca
|
||||
bateram, regex avaliado com a variable ainda vazia). A forma que
|
||||
funciona: dentro da MESMA extension, um `transfer` explícito usando
|
||||
o dígito coletado como novo `destination_number` — dispara uma
|
||||
consulta de dialplan NOVA (variables já setadas persistem), e as
|
||||
extensions seguintes casam por `destination_number` normal
|
||||
- [x] Testado ponta a ponta com DTMF de verdade (`fs_cli uuid_recv_dtmf`
|
||||
— `linphonec` não tem comando de enviar DTMF interativo, usada a
|
||||
API do FreeSWITCH que simula o dígito chegando como input do
|
||||
chamador): softphone externo liga pro DID → IVR atende, toca
|
||||
prompt, espera dígito → dígito `1` bridged com ramal real
|
||||
(atendeu, áudio PCMU bidirecional); dígito `2` foi pra resposta
|
||||
alternativa (tom diferente) — confirma ramificação de verdade, não
|
||||
só "sempre cai na primeira opção". Detalhes completos em
|
||||
docs/INBOUND_ROUTES.md
|
||||
- [ ] Tela de frontend dedicada de autoria de IVR (menu com opções) —
|
||||
hoje é construído à mão no editor genérico de dialplan; backend já
|
||||
suporta tudo que uma UI assim precisaria gerar
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user