Saltar al contenido principal

Servicio de Buzón de Voz y Llamadas Perdidas

📖 Volver a la Documentación Principal

OmniTAS proporciona su propio servicio de buzón de voz para depósito (dejar un mensaje) y recuperación (escuchar mensajes). El flujo interactivo es impulsado completamente por el TAS a través de la Biblioteca de Sockets de Eventos de FreeSWITCH (ESL), en lugar de por el IVR mod_voicemail integrado de FreeSWITCH. El almacenamiento se mantiene compatible con mod_voicemail, por lo que la interfaz web del buzón de voz, la API REST y las notificaciones SMS de espera de mensajes (MWI) continúan funcionando sin cambios.

Documentación Relacionada

Documentación Principal

Integración de Procesamiento de Llamadas

Servicios Relacionados

Monitoreo


Arquitectura

El TAS ya mantiene conexiones ESL entrantes a FreeSWITCH para eventos, comandos y monitoreo. El buzón de voz agrega un segundo camino independiente: un socket saliente que FreeSWITCH conecta dentro, por llamada, cuando el plan de marcado entrega una llamada al buzón de voz. A través de ese socket por llamada, el TAS reproduce prompts, graba audio y recoge DTMF directamente.

Puntos clave:

  • La ruta ESL entrante (eventos de llamada, facturación en línea, monitoreo) no ha cambiado.
  • El socket saliente se utiliza solo para el IVR interactivo del buzón de voz.
  • El TAS y FreeSWITCH están co-localizados, por lo que el socket saliente se vincula a loopback.
  • El almacenamiento utiliza el esquema de base de datos de mod_voicemail, por lo que vm_boxcount, la interfaz web y la API REST siguen funcionando sin modificación. mod_voicemail debe permanecer cargado en FreeSWITCH para que la base de datos y el comando vm_boxcount permanezcan disponibles.

Enrutamiento de una Llamada al Buzón de Voz

El buzón de voz se agrega en el plan de marcado XML según sea necesario; no está activo a menos que su plan de marcado enrute una llamada a él. El plan de marcado establece tres variables de canal y luego entrega la llamada al socket saliente, que el TAS lee para decidir qué hacer.

VariableEstablecida cuandoDescripción
tas_vm_modeSiempredeposit para dejar un mensaje, o retrieve para escuchar mensajes. Grabar un saludo personal es una opción dentro del IVR de recuperación, no un modo/número separado.
tas_vm_mailboxSiempreMSISDN del propietario del buzón. Para depósito, el suscriptor llamado; para recuperación, el suscriptor llamante (el propietario del buzón).
tas_vm_callerDepósitoNúmero de la parte que llama, almacenado con el mensaje y utilizado en el texto de notificación.
default_languageOpcionalIdioma utilizado para la fecha/hora "recibida en" hablada durante la recuperación. Establecido por suscriptor en el plan de marcado; vuelve al valor de configuración say_language cuando no está establecido.

La aplicación socket hace que FreeSWITCH se conecte al escuchador de buzón de voz del TAS. La IP y el puerto deben coincidir con la configuración de outbound_socket. Los argumentos son estándar de FreeSWITCH: async permite que el TAS reciba eventos mientras se ejecutan aplicaciones, y full otorga acceso completo a eventos.

Ejemplo de Depósito

Coloque esto después de un comando bridge para que se ejecute cuando el puente falle (sin respuesta / inalcanzable):

  <action application="log"
data="INFO Falló el puente de la llamada - Enrutando a la Destinación de Reenvío de Llamada Sin Respuesta" />
<action application="set"
data="sip_h_History-Info=<sip:${destination_number}@${ims_domain}>;index=1.1" />
<action application="set" data="sip_call_id=${sip_call_id};CALL_FORWARD_NO_ANSWER" />
<action application="log" data="DEBUG Llamado al Número de Depósito de Buzón de Voz para ${msisdn}" />
<action application="set" data="default_language=fr"/>
<action application="answer" />
<action application="sleep" data="500"/>
<!-- Entregar la llamada al IVR de buzón de voz del TAS (depósito). El TAS graba el mensaje,
escribe la fila de mod_voicemail y dispara el MWI por sí mismo.
ip:port DEBE coincidir con :tas voicemail.outbound_socket en runtime.exs. -->
<action application="set" data="tas_vm_mode=deposit"/>
<action application="set" data="tas_vm_mailbox=${msisdn}"/>
<action application="set" data="tas_vm_caller=${effective_caller_id_number}"/>
<action application="socket" data="127.0.0.1:8084 async full"/>

Cuando una llamada se reenvía al buzón de voz, se deposita al partido original llamado utilizando el valor de History-Info (por ejemplo, tas_vm_mailbox=${history_info_value}), no el número del servicio de buzón de voz. Consulte Configuración del Plan de Marcado para el manejo de History-Info.

Ejemplo de Recuperación

  <extension name="Static-Route-Voicemail-Check">
<condition field="${tas_destination_number}" expression="^(2222|55512411520)$">
<action application="log" data="DEBUG Llamado al Número de Verificación del Buzón de Voz" />
<action application="set" data="default_language=fr"/>
<action application="answer" />
<!-- Entregar la llamada al IVR de buzón de voz del TAS (recuperación). El TAS reproduce mensajes,
maneja el menú escuchar/eliminar/guardar y limpia el MWI.
ip:port DEBE coincidir con :tas voicemail.outbound_socket en runtime.exs. -->
<action application="set" data="tas_vm_mode=retrieve"/>
<action application="set" data="tas_vm_mailbox=${msisdn}"/>
<action application="socket" data="127.0.0.1:8084 async full"/>
</condition>
</extension>

Grabar un saludo personal es no un número de acceso separado — es una opción ofrecida dentro del IVR de recuperación (ver Saludos Personales). La misma ruta de recuperación 6892222 anterior es todo lo que se necesita.

Flujo de Depósito

  1. Responder la llamada.

  2. Reproducir el saludo. Si el suscriptor ha grabado un saludo personal se reproduce; de lo contrario, se utiliza el predeterminado prompts.deposit_greeting.

  3. Reproducir el beep (prompts.beep).

  4. Grabar en storage_dir, terminando con la tecla de terminación (#), silencio, la longitud máxima, o colgar del llamador.

  5. Menú de revisión (prompts.deposit_review_menu) — ofrecido cuando el llamador aún está en la línea y la toma es al menos record.min_seconds:

    • 1 — escuchar la grabación, luego escuchar el menú nuevamente.
    • 2 — re-grabar (beep + grabar), reemplazando la toma, luego escuchar el menú nuevamente.
    • 3 (o un tiempo de espera / colgar) — guardar. El mensaje se escribe y prompts.deposit_saved ("mensaje guardado") se reproduce.

    El mensaje se almacena solo al guardar, por lo que una re-grabación sobrescribe limpiamente la toma anterior. Un llamador que simplemente cuelga después de dejar un mensaje utilizable aún lo tiene guardado: nada se pierde si no espera al menú. Una toma demasiado corta se descarta y se trata como una llamada perdida.

  6. Enviar la notificación MWI.

La notificación se envía inmediatamente después de que se almacena el mensaje. Este orden es deliberado: el mensaje debe escribirse antes de que se genere la notificación, para que el conteo de mensajes en espera sea exacto.

Flujo de Recuperación

Menú por mensaje:

TeclaAcciónEfecto
1Escuchar de nuevoReproduce el mensaje actual desde el principio.
2EliminarElimina el mensaje y su grabación, reproduce el prompt "eliminado", avanza.
3GuardarMantiene el mensaje (ya marcado como leído), reproduce el prompt "guardado", avanza.
4ReenviarReenvía la grabación a otro buzón (ver Reenviando un Mensaje). Disponible cuando el reenvío está habilitado.
(ninguno / tiempo de espera)Mantiene el mensaje y avanza.

Reproducir un mensaje lo marca como leído, por lo que ya no cuenta como nuevo. Una vez que el suscriptor ha revisado sus mensajes, el buzón no tiene mensajes no leídos y el MWI se limpia. El MWI solo se limpia cuando no quedan mensajes no leídos en todo el clúster, por lo que colgar a mitad de camino con mensajes no leídos no borra erróneamente el indicador.

Menú del buzón (saludos): cuando los saludos personales están habilitados, después de los mensajes (o inmediatamente si el buzón está vacío), se ofrece al suscriptor una opción de buzón para grabar su saludo presionando la tecla greetings.menu_key configurada (predeterminado 5). Así es como se graba un saludo: no hay un número de acceso separado.

El anuncio "recibido en" combina un prompt fijo (prompts.retrieve_received_at, por ejemplo, un archivo de audio que dice "Buzón de voz recibido en") con una fecha/hora hablada sobre la marcha por el módulo say de FreeSWITCH, utilizando la marca de tiempo almacenada del mensaje. El idioma hablado es el default_language del canal (establecido por suscriptor en el plan de marcado), volviendo al valor de configuración say_language cuando el canal no establece uno. Esto evita necesitar un archivo pregrabado para cada posible fecha/hora.

Indicación de Mensaje en Espera (MWI)

Las notificaciones se envían a través del notificador MWI, que publica en la API MWI del SMSc. Elige entre un mensaje de "llamada perdida" y un mensaje de "buzón de voz esperando" según el número de mensajes no leídos en el buzón. El conteo proviene de las propias filas voicemail_msgs del TAS (no de vm_boxcount de FreeSWITCH), agregadas en todo el clúster a través de todos los TAS (ver Multi-TAS Voicemail).

EventoIndicaciónTexto de notificación utilizado
Depósito, no se dejó mensajeInactivo (llamada perdida)voicemail_notification_text.not_left
Depósito, un mensaje esperandoActivovoicemail_notification_text.single_voicemail
Depósito, múltiples mensajes esperandoActivovoicemail_notification_text.multiple_voicemails
Recuperación finalizadaLimpiadovoicemail_notification_text.cleared (o un valor predeterminado)

Variables disponibles en las plantillas de notificación:

VariableDisponible enDescripción
callerTodosNúmero de la parte que llama que dejó el mensaje / llamada perdida.
dayTodosDía del mes, en la timezone configurada.
monthTodosNúmero del mes, en la timezone configurada.
hourTodosHora (24h), en la timezone configurada.
minuteTodosMinuto, con ceros a la izquierda, en la timezone configurada.
message_countmultiple_voicemailsNúmero de mensajes no leídos. Solo se establece cuando el conteo es mayor que 1.

Almacenamiento de Mensajes

Los mensajes depositados se almacenan de dos maneras, reflejando mod_voicemail:

  • Archivos de audio se escriben bajo storage_dir, organizados por buzón.
  • Metadatos (propietario, llamador, marca de tiempo, ruta del archivo, longitud, estado leído/no leído) se escriben como una fila en la base de datos SQLite voicemail_default.db de FreeSWITCH, en la tabla estándar voicemail_msgs.

Debido a que el esquema coincide con mod_voicemail, vm_boxcount, la interfaz web del buzón de voz y la API REST del buzón de voz leen estos mensajes sin cambios. Puede ver el uso del buzón de voz y el estado de los mensajes desde la pestaña de buzón de voz del Panel de Control.

Panel de Control (consciente del clúster)

La pestaña Buzones de Voz del Panel de Control lista cada mensaje a través de todo el clúster, no solo el nodo local:

  • Sincronizar y Actualizar se expande a cada TAS par y fusiona los resultados; una columna Nodo muestra qué TAS posee cada mensaje (Este nodo o la URL del par).
  • Reproducir transmite la grabación desde su nodo propietario (archivo local o HTTP del par), y Eliminar se dirige a ese propietario — por lo que los mensajes remotos son completamente gestionables desde la interfaz de cualquier nodo.
  • Un panel de Configuración del Buzón de Voz resume la configuración activa: saludos (y tecla de menú / forzar-primer-uso), reenvío, caducidad y el modo de federación + lista de pares.

Saludos Personales

Por defecto, los llamadores que llegan al buzón de voz escuchan el saludo del sistema (prompts.deposit_greeting). Opcionalmente, los suscriptores pueden grabar su propio saludo, que se reproduce en lugar del predeterminado para las llamadas a su buzón.

Los saludos personales son opcional (greetings.enabled). Cuando está deshabilitado, o cuando un suscriptor no ha grabado uno, el flujo de depósito vuelve al saludo predeterminado configurado, por lo que el comportamiento es sin cambios.

Grabando un saludo

La grabación de saludos es una opción dentro del IVR de recuperación — no hay un número de acceso separado. Cuando un suscriptor llama al buzón de voz y los saludos están habilitados, después de sus mensajes (o de inmediato si el buzón está vacío) se les ofrece un menú de buzón: presionar greetings.menu_key (predeterminado 5) reproduce el prompt de grabación, hace beep, graba y almacena el saludo. Una toma demasiado corta se descarta y se mantiene el saludo existente.

El saludo se almacena por buzón como greeting.wav en el directorio de ese buzón en storage_dir (junto a sus grabaciones de mensajes). Re-grabar reemplaza el saludo anterior; para eliminarlo y volver al predeterminado, consulte Limpiar un saludo.

Forzar saludo en la primera llamada

Con greetings.force_first_time: true (desactivado por defecto), se requiere que un suscriptor que no tiene saludo grabe uno en su primera llamada al buzón de voz, antes de que pueda escuchar cualquier mensaje**. Si la toma es demasiado corta se vuelve a intentar (hasta menu.tries). Una vez que existe un saludo, se omite el paso forzado. Déjelo desactivado para mantener la grabación de saludos opcional.

Multi-TAS: un saludo puede grabarse en un TAS pero un depósito puede aterrizar en otro (ver Multi-TAS Voicemail), por lo que el flujo de depósito localiza el saludo en todo el clúster: primero verifica localmente, luego se expande a pares — antes de volver al predeterminado. Para mantener esto sin ambigüedades, grabar un saludo primero limpia cualquier saludo existente en cada TAS, luego escribe el nuevo, por lo que exactamente una copia existe en todo el clúster (un suscriptor regrabando en un nodo diferente nunca deja un saludo obsoleto atrás). El saludo se obtiene y reproduce de la misma manera que se hace con una grabación de mensaje remota.

Reenviando un Mensaje a Otro Buzón

Durante la recuperación, un suscriptor puede reenviar el mensaje que está escuchando a otro buzón de un suscriptor. El reenvío es opcional (forwarding.enabled); cuando está deshabilitado, la tecla 4 se ignora.

  1. El suscriptor presiona 4 y se le solicita (prompts.forward_enter_mailbox) que ingrese el número del buzón objetivo, terminado con #. Los dígitos marcados se procesan a través de traducción de números (el código de país :number_translate del nodo), por lo que un número local se normaliza a la misma forma de MSISDN bajo la que se almacenan los buzones.
  2. La grabación actual se copia en el buzón objetivo como un nuevo mensaje no leído. Los detalles del llamador original se preservan y la columna forwarded_by registra al suscriptor que reenvió, por lo que el objetivo ve quién lo reenviaron.
  3. Se dispara una notificación MWI al buzón objetivo exactamente como lo haría un depósito normal.
  4. El suscriptor escucha una confirmación (prompts.forward_done); un objetivo desconocido/inválido reproduce prompts.forward_invalid y regresa al menú de mensajes.

El mensaje original no se ve afectado: el reenvío lo deja en el buzón del propio suscriptor.

Multi-TAS: la copia reenviada se convierte en un mensaje normal propiedad del nodo que realizó el reenvío (copia el WAV localmente e inserta la fila), y el conteo de espera del objetivo se calcula en todo el clúster, por lo que el objetivo ve el mensaje reenviado desde cualquier TAS en el que se recupere.

Retención y Caducidad de Mensajes

Los buzones de voz no se acumulan para siempre. Un limpiador periódico elimina mensajes (fila y grabación) una vez que superan una edad configurada, manteniendo los buzones y el uso del disco limitados. La caducidad es opcional (expiry.enabled); cuando está deshabilitada, los mensajes se mantienen hasta que se eliminen manualmente.

  • El limpiador se ejecuta cada expiry.sweep_interval_minutes.
  • Un mensaje caduca cuando su edad (desde created_epoch) supera expiry.max_age_days.
  • Opcionalmente, los mensajes ya leídos pueden caducar antes a través de expiry.read_max_age_days (por ejemplo, mantener mensajes no leídos 30 días pero purgar mensajes escuchados después de 7).
  • Caducar un mensaje elimina tanto su fila voicemail_msgs como su archivo de grabación, por lo que la interfaz web, la API REST y el conteo de espera reflejan la eliminación.

Si la caducidad de mensajes elimina el último mensaje no leído de un buzón, el MWI se limpia para ese buzón, de modo que el indicador del dispositivo permanezca preciso.

Multi-TAS: cada nodo limpia su propio almacén según su propio horario. Debido a que cada mensaje tiene un único nodo propietario (ver más abajo), no se necesita coordinación entre nodos: cada propietario caduca los mensajes que posee.

Buzón de Voz Multi-TAS (Buzones Agrupados)

Cuando más de un OmniTAS atiende a los mismos suscriptores, las llamadas de un suscriptor pueden aterrizar en cualquier TAS en cualquier momento — no hay adherencia. Dado que cada TAS graba en su propio almacén local, un mensaje dejado en TAS-A es, por defecto, invisible para TAS-B. La agrupación hace que el buzón de un suscriptor se comporte como un único buzón lógico en todos los TAS, por lo que el conteo de espera refleja los mensajes dejados en cualquier nodo y la recuperación desde cualquier nodo puede listar, reproducir y eliminar cada mensaje.

Esto se logra sin agrupación de Erlang, Mnesia distribuido o una base de datos compartida/central. Cada TAS mantiene su propio almacén local; los nodos simplemente se preguntan entre sí a través de HTTP cuando necesitan la imagen completa. La agrupación es opcional: cuando la clave de configuración cluster está ausente, el TAS se comporta exactamente como un nodo único.

Propiedad particionada, lecturas de dispersión-reunión

Cada buzón de voz permanece donde fue grabado. El nodo que tomó el depósito es el nodo propietario del mensaje y mantiene tanto la fila de metadatos como el WAV de grabación. Nada se replica y no hay cambio de esquema: la propiedad es simplemente el nodo que devuelve la fila. Cuando un nodo necesita la vista completa de un buzón, consulta su propio almacén y expande una solicitud HTTP a cada otro TAS, luego fusiona los resultados. Debido a que cada mensaje tiene exactamente un propietario, marcar como leído y eliminar siempre se dirigen de regreso a ese único propietario, por lo que no hay conflictos de escritura distribuidos.

Descubrimiento de nodos

Un TAS encuentra a sus pares de dos maneras (establecido por cluster.discovery); en ambos casos, se excluye a sí mismo para que nunca se consulte a sí mismo a través de HTTP.

  • Estático (:static) — la lista completa de miembros se configura en runtime.exs y se despliega idénticamente a cada nodo. Cada nodo se identifica con self_id y elimina esa entrada. Predecible y sin dependencias; agregar/quitar un nodo significa editar la configuración en todas partes.
  • DNS (:dns) — el nodo resuelve un nombre DNS que devuelve cada dirección TAS (registros A/AAAA, uno por TAS) y construye una URL de par por dirección, eliminando la propia. Un corto TTL de DNS mantiene el conjunto de pares actual sin redeplegar la configuración; un registro SRV puede llevar el puerto por objetivo.

De cualquier manera, el conjunto de pares es solo una lista de URLs base; todo lo demás es idéntico.

Obtener el conteo (dispersión)

El conteo de mensajes en espera se calcula expandiendo a cada otro TAS y sumando: se ejecuta en cada depósito porque el texto MWI depende de ello.

La dispersión se ejecuta concurrentemente con un corto tiempo de espera por solicitud, por lo que un par lento o inalcanzable nunca bloquea el flujo de depósito.

Recuperación desde cualquier nodo (streaming de medios)

En la recuperación, el nodo que maneja construye el buzón fusionado — sus propios mensajes más los de cada par — ordenados de más antiguo a más reciente, cada mensaje etiquetado con su nodo propietario. Para reproducir un mensaje remoto, transmite el WAV desde el propietario a través de GET /api/voicemail/media, lo escribe en un archivo temporal bajo su storage_dir local (para que el FreeSWITCH co-localizado pueda leerlo), lo reproduce y luego elimina el archivo temporal. Las grabaciones se obtienen perezosamente, solo cuando un mensaje está a punto de reproducirse, por lo que un suscriptor que cuelga temprano no activa más transferencias. Marcar como leído y eliminar se dirigen al nodo propietario (POST /api/voicemail/mark_read / .../delete).

API HTTP Inter-TAS

La dispersión se ejecuta sobre el escuchador HTTP existente del TAS. Los puntos finales de lectura/escritura única operan en el almacén local del nodo solo: la vista en todo el clúster se ensambla por el nodo que llama.

Método y RutaPropósito
GET /api/voicemail/count?mailbox=<msisdn>Conteo no leído en el almacén de este nodo.
GET /api/voicemail/allCada mensaje en el almacén de este nodo (todos los buzones) — respalda la lista en el Panel de Control a nivel de clúster.
GET /api/voicemail/messages?mailbox=<msisdn>Filas de mensajes de este nodo para el buzón.
GET /api/voicemail/media?mailbox=<msisdn>&uuid=<uuid>Transmite el WAV de grabación que posee este nodo.
GET /api/voicemail/greeting?mailbox=<msisdn>Transmite el saludo personal de este nodo para el buzón, si lo hay.
POST /api/voicemail/mark_read {mailbox, uuid}Marca un mensaje como leído en el almacén de este nodo.
POST /api/voicemail/delete {mailbox, uuid}Elimina un mensaje (fila + grabación) de este nodo.
DELETE /api/voicemail/greeting?mailbox=<msisdn>Limpia el saludo personal (vuelve al predeterminado). Consciente del clúster — ver gestión.
DELETE /api/voicemail/mailbox?mailbox=<msisdn>Elimina todos los mensajes (filas + grabaciones) para el buzón. Consciente del clúster — ver gestión.

Los puntos finales GET/POST anteriores actúan sobre el almacén local únicamente: la vista en todo el clúster se ensambla por el nodo que llama. Las dos acciones de gestión DELETE en cambio se expanden a pares; la parte de par lleva una bandera ?scope=local para que una solicitud expandida se aplique localmente y no se vuelva a expandir, previniendo la recursión.

Manejo de fallas (mejor esfuerzo, nunca silencioso)

Un solo par inalcanzable no debe romper el buzón de voz para todos:

  • Conteo — un par que se agota contribuye 0. Esto puede brevemente subcontar, pero cada tal falla se registra y se registra como una métrica; nunca se traga silenciosamente.
  • Recuperación — los mensajes de un par inalcanzable se omiten (registrados); una obtención de medios que falla a mitad de sesión se registra y se omite en lugar de cortar la llamada.
  • Un par en recuperación simplemente se vuelve a unir a la siguiente dispersión: no hay un paso de re-sincronización, porque nada fue replicado.

Seguridad

Las solicitudes inter-TAS llevan contenido del buzón de voz, por lo que los puntos finales del clúster requieren un secreto compartido encabezado (verificado por el nodo receptor) y una lista de permitidos de IP de origen opcional (el mismo patrón de lista de permitidos que otras interfaces TAS). Se aceptan certificados autofirmados entre nodos como para la integración de SMSc.

API de Gestión: Limpieza de Saludos y Buzones

Además de los primitivos por nodo anteriores, dos acciones de gestión permiten a un operador (o interfaz de autoayuda) restablecer el buzón de voz de un suscriptor a través de HTTP. Ambas son conscientes del clúster: dado que un saludo o un mensaje pueden vivir en cualquier nodo, la solicitud se expande a cada TAS y aplica la acción en cada nodo que posee los datos relevantes. En un solo nodo (sin cluster configurado) la acción simplemente se aplica localmente.

Limpiar un saludo

Elimina el saludo personal de un suscriptor para que los llamadores vuelvan al saludo predeterminado deposit_greeting.

DELETE /api/voicemail/greeting?mailbox=<msisdn>

El nodo receptor elimina su propio greeting.wav para el buzón (una no-op si no posee ninguno) y, en un clúster, expande el mismo DELETE a pares para que un saludo grabado en cualquier nodo sea eliminado. Devuelve éxito una vez que cada nodo alcanzable lo ha aplicado; se informa un nodo inalcanzable para que el llamador sepa que la limpieza fue parcial.

Limpiar un buzón

Elimina todos los mensajes (filas y grabaciones) para un buzón — por ejemplo, al desprovisionar a un suscriptor o a solicitud del operador.

DELETE /api/voicemail/mailbox?mailbox=<msisdn>

El nodo receptor elimina cada mensaje que posee para el buzón y expande el DELETE a pares, por lo que los mensajes mantenidos en cualquier nodo son eliminados. Debido a que esto limpia el último mensaje no leído en todas partes, el MWI se limpia para el buzón. Al igual que con las limpiezas de saludos, se informa un resultado parcial si algún nodo es inalcanzable.

Estos puntos finales de gestión están protegidos por el mismo secreto compartido (y lista de permitidos de IP de origen opcional) que el resto de la API inter-TAS.

Configuración

Todas las configuraciones del buzón de voz viven bajo la clave voicemail de la aplicación :tas en runtime.exs.

outbound_socket, record y menu son opcionales: tienen valores predeterminados en el código y se muestran aquí comentados. Los prompts de voz son generados por TTS por el bloque de grabaciones compartido :tas, :prompts (vea Archivos de Prompt); el mapa prompts aquí solo apunta a las rutas resultantes.

config :tas,
voicemail: %{
timezone: "Pacific/Tahiti", # Zona horaria utilizada en marcas de tiempo

say_language: "fr", # Idioma de respaldo para la fecha/hora hablada
# (gana el default_language del canal por llamada)
storage_dir: "/usr/local/freeswitch/storage/voicemail", # Donde se escriben las grabaciones

# Opcional — valores predeterminados mostrados; omitir para usarlos:
# outbound_socket: %{listen_ip: "127.0.0.1", listen_port: 8084},
# record: %{max_seconds: 120, silence_threshold: 200, silence_seconds: 5, min_seconds: 2},
# menu: %{tries: 3, timeout_ms: 5000, terminators: "#"},

# Archivos de prompt que FreeSWITCH reproduce (rutas ABSOLUTAS; $${base_dir} NO se expande sobre ESL).
# Los prompts de voz son generados por el bloque de grabaciones :tas, :prompts; `beep` es un tono.
prompts: %{
deposit_greeting: "/usr/local/freeswitch/sounds/tas/vm/deposit_greeting.wav",
deposit_review_menu: "/usr/local/freeswitch/sounds/tas/vm/deposit_review_menu.wav",
deposit_saved: "/usr/local/freeswitch/sounds/tas/vm/deposit_saved.wav",
beep: "tone_stream://%(500,0,800)",
retrieve_received_at: "/usr/local/freeswitch/sounds/tas/vm/received_at.wav",
retrieve_menu: "/usr/local/freeswitch/sounds/tas/vm/menu.wav",
retrieve_no_messages: "/usr/local/freeswitch/sounds/tas/vm/no_messages.wav",
retrieve_deleted: "/usr/local/freeswitch/sounds/tas/vm/deleted.wav",
retrieve_saved: "/usr/local/freeswitch/sounds/tas/vm/saved.wav",
retrieve_goodbye: "/usr/local/freeswitch/sounds/tas/vm/goodbye.wav",

# Saludos personales (solo necesarios cuando greetings.enabled)
retrieve_greeting_menu: "/usr/local/freeswitch/sounds/tas/vm/greeting_menu.wav",
greeting_record: "/usr/local/freeswitch/sounds/tas/vm/greeting_record.wav",
greeting_saved: "/usr/local/freeswitch/sounds/tas/vm/greeting_saved.wav",

# Reenvío (solo necesario cuando forwarding.enabled)
forward_enter_mailbox: "/usr/local/freeswitch/sounds/tas/vm/forward_enter_mailbox.wav",
forward_done: "/usr/local/freeswitch/sounds/tas/vm/forward_done.wav",
forward_invalid: "/usr/local/freeswitch/sounds/tas/vm/forward_invalid.wav"
},

# Opcional — saludos por suscriptor, grabados desde el IVR de recuperación (presionar menu_key).
# force_first_time hace que un suscriptor sin saludo grabe uno antes de escuchar mensajes.
# greetings: %{enabled: true, menu_key: "5", force_first_time: false},

# Opcional — reenviar un mensaje a otro buzón desde el menú de recuperación (tecla 4).
# forwarding: %{enabled: true, menu_key: "4"},

# Opcional — caducar mensajes antiguos automáticamente. Omitir para mantener mensajes hasta que se eliminen manualmente.
# expiry: %{
# enabled: true,
# max_age_days: 30, # eliminar mensajes más antiguos que esto
# read_max_age_days: 7, # opcional: purgar mensajes ya leídos antes
# sweep_interval_minutes: 60
# },

# Opcional — agrupación multi-TAS. Omitir completamente para ejecutar en nodo único (sin dispersión).
# cluster: %{
# discovery: :static, # :static (lista de nodos) o :dns (dns_name)
# self_id: "tas-a",
# nodes: [
# %{id: "tas-a", base_url: "https://10.8.82.60:8080"},
# %{id: "tas-b", base_url: "https://10.8.82.61:8080"}
# ],
# # discovery: :dns, dns_name: "omnitas-vm.internal.example.com", scheme: "https", port: 8080,
# shared_secret: System.get_env("VM_CLUSTER_SECRET"),
# request_timeout_ms: 1500
# },

smsc: %{
smsc_url: "https://10.80.14.219:8443", # URL base de la API SMSc / Omnimessage
source_msisdn: "2222" # Remitente para mensajes de notificación
},

# Para el uso de variables en esta sección, consulte la tabla MWI anterior.
voicemail_notification_text: %{
not_left:
"Vous avez 1 appel manqué du <%= caller %> le <%= day %>/<%= month %> à <%= hour %>:<%= minute %>",
single_voicemail:
"Vous avez un nouveau message vocal du <%= caller %> le <%= day %>/<%= month %> à <%= hour %>:<%= minute %>. Pour le consulter, composez le 2222.",
multiple_voicemails:
"Vous avez <%= message_count %> nouveaux messages vocaux. Pour les consulter, composez le 2222."
}
}

Parámetros

ParámetroTipoRequeridoPredeterminadoDescripción
timezoneStringNo"Etc/UTC"Zona horaria IANA para la fecha/hora sustituida en el texto de notificación.
outbound_socket.listen_ipStringNo"127.0.0.1"IP a la que se vincula el escuchador del buzón de voz. Loopback cuando está co-localizado. Debe coincidir con la acción socket del plan de marcado.
outbound_socket.listen_portIntegerNo8084Puerto TCP al que se vincula el escuchador del buzón de voz. Debe coincidir con la acción socket del plan de marcado.
say_languageStringNo"en"Código de idioma de respaldo pasado a FreeSWITCH say para la fecha/hora del mensaje hablada. Se utiliza solo cuando el default_language del canal no está establecido; default_language tiene prioridad por llamada.
storage_dirString-Directorio donde se escriben las grabaciones. Debe ser escribible por FreeSWITCH y legible por el TAS. Se crea a demanda por buzón.
record.max_secondsIntegerNo120Longitud máxima del mensaje en segundos.
record.silence_thresholdIntegerNo200Nivel de energía por debajo del cual el audio se trata como silencio.
record.silence_secondsIntegerNo5Segundos de silencio continuo que terminan la grabación.
record.min_secondsIntegerNo2Las grabaciones más cortas que esto se descartan y se tratan como una llamada perdida.
menu.triesIntegerNo3Veces que se reproduce el prompt del menú mientras se espera un dígito válido.
menu.timeout_msIntegerNo5000Milisegundos a esperar por un dígito en cada intento.
menu.terminatorsStringNo"#"Tecla(s) DTMF que terminan la entrada de dígitos.
prompts.*String-Rutas absolutas a archivos de audio de prompt (ver tabla a continuación).
smsc.smsc_urlString-URL base de la API SMSc / Omnimessage. El endpoint /api/mwi se añade automáticamente.
smsc.source_msisdnString-Dirección del remitente mostrada en los mensajes de notificación.
voicemail_notification_text.not_leftString-Cuerpo enviado cuando una llamada llegó al buzón de voz pero no se dejó mensaje.
voicemail_notification_text.single_voicemailString-Cuerpo enviado cuando exactamente un mensaje no leído está esperando.
voicemail_notification_text.multiple_voicemailsString-Cuerpo enviado cuando hay más de un mensaje no leído esperando.
voicemail_notification_text.clearedStringNo(predeterminado)Cuerpo opcional utilizado al limpiar el indicador después de la recuperación. Se requiere un cuerpo no vacío para que el SMSc construya un mensaje entregable.
greetings.enabledBooleanNofalsePermitir a los suscriptores grabar un saludo personal (reproducido a los llamadores en lugar de deposit_greeting), desde el IVR de recuperación.
greetings.menu_keyStringNo"5"Tecla DTMF en el menú del buzón de recuperación que graba un saludo personal.
greetings.force_first_timeBooleanNofalseObliga a un suscriptor sin saludo a grabar uno en su primera llamada antes de escuchar mensajes.
forwarding.enabledBooleanNofalseOfrecer "reenviar a otro buzón" en el menú de recuperación.
forwarding.menu_keyStringNo"4"Tecla DTMF que activa el reenvío durante la recuperación.
expiry.enabledBooleanNofalseHabilitar el limpiador periódico que elimina mensajes envejecidos.
expiry.max_age_daysIntegerNo-Eliminar mensajes más antiguos que este número de días. Requerido cuando expiry.enabled.
expiry.read_max_age_daysIntegerNo(no establecido)Si se establece, los mensajes ya leídos se purgan después de este número de días (retención más corta que los no leídos).
expiry.sweep_interval_minutesIntegerNo60Con qué frecuencia se ejecuta el limpiador de caducidad.
clusterMapNo(no establecido)Habilita el buzón de voz multi-TAS. Cuando no se establece, el nodo es único (sin dispersión).
cluster.discoveryAtomSí (si se establece cluster)-:static para una lista de nodos configurada, :dns para descubrimiento basado en DNS.
cluster.self_idStringSí (:static)-ID de este nodo; su entrada se excluye del conjunto de pares.
cluster.nodesLista de mapasSí (:static)-Miembros del clúster, cada uno %{id, base_url}. Despliegue la misma lista a cada nodo.
cluster.dns_nameStringSí (:dns)-Nombre DNS que resuelve a cada dirección TAS. El nodo excluye su propia dirección.
cluster.schemeStringNo (:dns)"https"Esquema de URL utilizado para construir URLs base de pares.
cluster.portIntegerNo (:dns)8080Puerto utilizado para construir URLs base de pares (ignorando cuando los registros SRV llevan el puerto).
cluster.shared_secretStringSí (si se establece cluster)-Secreto presentado y verificado en las solicitudes inter-TAS.
cluster.request_timeout_msIntegerNo1500Tiempo de espera por solicitud para la dispersión. Un par que exceda esto no contribuye nada (registrado).

Archivos de Prompt

Los valores de prompt son rutas absolutas a archivos de audio legibles por FreeSWITCH (el atajo $${base_dir} no se expande para archivos reproducidos sobre ESL), o un URI tone_stream:// para tonos.

Los prompts de voz son generados al inicio por el bloque de grabaciones compartido :tas, :prompts (el mismo paso de TTS de OpenAI utilizado para los prompts de crédito/anuncio). Agregue una grabación allí con el texto y una ruta relativa base, luego apunte la entrada correspondiente de voicemail.prompts a la ruta absoluta (el generador escribe bajo /usr/local/freeswitch). Solo se generan archivos faltantes.

ParámetroDescripción
deposit_greetingReproducido al llamador antes del beep durante el depósito.
beepReproducido antes de grabar, y antes de cada mensaje durante la recuperación. Un URI tone_stream:// (por ejemplo, tone_stream://%(500,0,800)) genera el tono sin archivo.
retrieve_received_atIntroducción fija "buzón de voz recibido en", seguida de la fecha/hora hablada.
retrieve_menuEl menú de opciones, por ejemplo, "para escuchar esto de nuevo presione 1, para eliminar presione 2, para guardar presione 3".
retrieve_no_messagesReproducido cuando el buzón está vacío.
retrieve_deletedConfirmación reproducida después de que se elimina un mensaje.
retrieve_savedConfirmación reproducida después de que se guarda un mensaje.
retrieve_goodbyeReproducido al final de la sesión de recuperación.
retrieve_greeting_menuMenú del buzón que anuncia la opción "grabar saludo" (solo cuando greetings.enabled).
greeting_recordPrompt para grabar un saludo personal (solo cuando greetings.enabled).
greeting_savedConfirmación después de que se guarda un saludo personal.
forward_enter_mailboxPrompt para ingresar el MSISDN del buzón objetivo (solo cuando forwarding.enabled).
forward_doneConfirmación después de que se reenvía un mensaje.
forward_invalidReproducido cuando el buzón objetivo de reenvío es desconocido/inválido.

Solución de Problemas

Las llamadas al buzón de voz se cortan inmediatamente

Síntomas: Una llamada enrutada a una extensión de buzón de voz se cuelga antes del saludo o menú.

Causas posibles:

  • La dirección/puerto socket del plan de marcado no coincide con outbound_socket.
  • El escuchador de buzón de voz no está en ejecución.
  • Un firewall está bloqueando el puerto de loopback (inusual cuando está co-localizado).

Resolución:

  1. Confirme que la acción socket del plan de marcado utiliza la misma IP y puerto que outbound_socket.
  2. Verifique que el TAS esté en ejecución y que el escuchador de buzón de voz se haya iniciado.
  3. Confirme que FreeSWITCH pueda alcanzar listen_ip:listen_port.

Mensajes no almacenados o no visibles en la UI

Síntomas: Se deja un mensaje pero no aparece en la UI/API REST, o vm_boxcount devuelve 0.

Causas posibles:

  • mod_voicemail no está cargado, por lo que la tabla voicemail_msgs / vm_boxcount no están disponibles.
  • La grabación fue más corta que record.min_seconds y se trató como una llamada perdida.
  • storage_dir no es escribible por FreeSWITCH.

Resolución:

  1. Asegúrese de que mod_voicemail permanezca cargado en FreeSWITCH.
  2. Verifique la longitud de la grabación en comparación con record.min_seconds.
  3. Verifique los permisos de storage_dir.

No se escuchan prompts

Síntomas: Silencio donde debería reproducirse un saludo, beep o menú.

Causas posibles:

  • Una ruta de prompts es incorrecta o no es legible por FreeSWITCH.
  • Una ruta de prompts utilizó $${base_dir}, que no se expande sobre ESL.

Resolución:

  1. Confirme que cada valor de prompts sea una ruta absoluta a un archivo que FreeSWITCH pueda leer.
  2. Reemplace cualquier ruta de estilo $${base_dir} con rutas absolutas.

El indicador de mensajes en espera no se limpia

Síntomas: Después de escuchar todos los mensajes, el dispositivo aún muestra mensajes en espera.

Causas posibles:

  • El SMSc rechazó la limpieza porque el cuerpo del mensaje estaba vacío.
  • Quedan mensajes no leídos (la sesión terminó antes de que se escucharan todos los mensajes).

Resolución:

  1. Asegúrese de que se haya configurado un cuerpo "limpiado" (o predeterminado).
  2. Confirme que el suscriptor escuchó todos los mensajes; solo los mensajes reproducidos se marcan como leídos.

El conteo es demasiado bajo / faltan mensajes en un clúster

Síntomas: El indicador de un suscriptor muestra menos mensajes de los que se dejaron, o los mensajes dejados en otro TAS no se escuchan durante la recuperación.

Causas posibles:

  • Un TAS par está inactivo o inalcanzable, por lo que no contribuyó nada a la dispersión (registrado + métrica).
  • El cluster.shared_secret no coincide entre nodos, por lo que los pares rechazan las solicitudes.
  • El descubrimiento está mal configurado (lista de nodes incorrecta, o dns_name que no devuelve todas las direcciones).
  • Un firewall bloquea el puerto del escuchador HTTP entre nodos TAS.

Resolución:

  1. Verifique los registros/métricas de errores de dispersión del clúster para ver qué par falló.
  2. Confirme que shared_secret sea idéntico en cada nodo y que el descubrimiento devuelva cada nodo.
  3. Verifique que cada nodo pueda alcanzar el puerto del escuchador HTTP de cada otro nodo.

No se reproduce un saludo personal

Síntomas: Los llamadores escuchan el saludo predeterminado a pesar de que el suscriptor grabó el suyo.

Causas posibles:

  • greetings.enabled no está establecido.
  • En un clúster, el saludo fue grabado en un nodo diferente y ese nodo estaba inalcanzable durante la búsqueda del saludo del depósito.

Resolución:

  1. Confirme que greetings.enabled sea verdadero y que el saludo se haya guardado (se escuchó el prompt de confirmación).
  2. Confirme que el nodo que grabó el saludo sea alcanzable desde el nodo que deposita.

Los mensajes antiguos no se están eliminando

Síntomas: Los buzones mantienen mensajes indefinidamente / el uso del disco crece.

Causas posibles:

  • expiry.enabled no está establecido, o max_age_days no está establecido.
  • El intervalo de limpieza aún no ha transcurrido.

Resolución:

  1. Confirme que expiry.enabled sea verdadero y que max_age_days esté configurado.
  2. Permita hasta sweep_interval_minutes para la próxima limpieza, luego verifique los registros.