_,met$$$$$gg. .$$$$$$$$$$$$$$$=..
,g$$$$$$$$$$$$$$$P. .$7$7.. .7$$7:.
,g$$P"" """Y$$.". .$$:. ,$7.7
,$$P' `$$$. .$7. 7$$$$ .$$77
',$$P ,ggs. `$$b: ..$$. $$$$$ .$$$7
`d$$' ,$P"' . $$$ ..7$ .?. $$$$$ .?. 7$$$.
Y$$. `.`"Y$$$$P"' $.$. .$$$7. $$$$7 .7$$$. .$$$.
$$P d$' , $$P .777. .$$$$$$77$$$77$$$$$7. $$$,
$$: $$. - ,d$$' $$$~ .7$$$$$$$$$$$$$7. .$$$.
$$\; Y$b._ _,d$P' .$$7 .7$$$$$$$7: ?$$$.
`$$b "-.__ $$$ ?7$$$$$$$$$$I .$$$7
`Y$$ $$$ .7$$$$$$$$$$$$$$$$ :$$$.
`Y$$. Debian $$$ $$$$$$7$$$$$$$$$$$$ .$$$.
`$$b. $$$ $$$ 7$$$7 .$$$ .$$$.
`Y$$b. $$$$ $$$$7 .$$$.
`"Y$b._ 7$$$7 7$$$$ 7$$$
`"""" $$$$7. Asterisk $$
$$$$$$$. . 7$$$$$$ $$
$$$$$$$$$$$$7$$$$$$$$$.$$$$$$
$$$$$$$$$$$$$$$$.
He desarrollado este tutorial debido a que tenía que hacer uso de Asterisk en una práctica de la universidad y, ya que tenía que hacer una pequeña memoria, decidí hacerlo en modo tutorial (y no ha quedado tan pequeña 😆). Como he consultado bastantes webs y me ha llevado un tiempo aprender cómo configurar Asterisk (aunque siga siendo un novato), cuelgo el tutorial para que no se quede olvidado en mi ordenador y por si a alguien le puede servir.
Quiero dejar claro que es la primera vez que configuro una PBX como es Asterisk, por lo que el tutorial puede tener errores y existir seguro mil formas mejor de hacerlo, pero menos da una piedra, ¿no? 😃
Si detectas cualquier error, tienes cualquier duda, sugerencia o quieres contactar conmigo puedes hacerlo a través de
Para hacer esta guía he usando Typora, Markdown-editor, gh-md-toc y Lightshot.
- Índice
-
- 4.1 Buzón de voz (voicemail)
- 4.2 Redirección/transferencia de llamadas
- 4.3 Centro de mensajes (SIP messages)
- 4.4 Llamadas a grupo (call distribution)
- 4.5 Captura de llamada (call pickup)
- 4.6 Conversión de texto a voz (TTS) con Festival
- 4.7 Segurización de llamadas
- 4.8 Simular llamadas desde el exterior (redirección a un contexto diferente)
- 4.9 Horario de atención
- 4.10 Registro (log)
- 4.11 Cola y distribución de llamadas
Lo he ido desarrollando en pequeños módulos para que me fuese más fácil entender los conceptos y probar lo integrado, con el objetivo final de desarrollar un Call Center con todas las funcionalidades anteriormente probadas.
Los comandos a usar, dependiendo de a qué se refieran, van precedidos por un determinado símbolo:
- $ (dolar): terminal de Linux
$ [comando]
- : (dos puntos): vim
: [comando]
- CLI>: consola de Asterisk
CLI> [comando]
- MariaDB: base de datos MariaDB
MariaDB> [comando]
- Si está citado significa que es texto plano del archivo a modificar
Texto del
archivo
a modificar
Puedes ver el código original, sin modificar, 📁aquí.
Echa un vistazo a algunos enlaces de interés 🔗aquí.
Haremos uso de Debian 9.11 (GNU bash, versión 4.4.12[1]-release [x86_64-pc-linux-gnu]) sin interfaz gráfica y Asterisk 15.17.1. El sistema operativo se encuentra corriendo en el hipervisor VMware® Workstation 14 Player (14.1.8 build-14921873) con modo de conexión de red ‘Bridged’.
Al sistema operativo se le ha instalado el programa ‘sudo’ para que puedan usarse las opciones de súper usuario como en Ubuntu, haciendo más cómoda la instalación de todas las demás herramientas.
$ su
$ apt-get install sudo
$ adduser username sudo
Puedes ver la bibliografía 📚 aquí.
Antes de empezar, conviene asignarle una IP estática a nuestra interfaz de red para no tener que averiguar cada vez que queramos conectarnos vía SSH qué IP tiene asignada.
Vemos cuál es la IP asignada ahora a la interfaz de la máquina virtual (ens33):
$ ip address
Modificamos el fichero de interfaces:
$ sudo vim /etc/network/interfaces
Lo modificamos para que sea estática, asignándole la IP 192.168.0.180:
👀 Ten en cuenta que puede haber errores con la indentación
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).source /etc/network/interfaces.d/*
# The loopback network interface
auto lo
iface lo inet loopback# The primary network interface
allow-hotplug ens33
iface ens33 inet static
address 192.168.1.200
gateway 192.168.1.1
Modificamos los servidores DNS para que no tengamos problemas al descargar los paquetes. En este caso vamos a utilizar el de Google (8.8.8.8) y el de Cloudflare (1.1.1.1):
$ sudo vim /etc/resolv.conf
nameserver 8.8.8.8
nameserver 1.1.1.1
Reiniciamos el servicio de red y reiniciamos el SO:
$ sudo systemctl restart networking
$ sudo reboot
Podemos ver que la IP ha sido asignada correctamente:
$ ip address
Hacemos un ping para probar que la configuración funciona y no hay problemas con los servidores DNS:
$ ping -c3 www.debian.org
Para instalar OpenSSH, si no se ha instalado junto al SO, se puede usar el siguiente comando:
$ sudo apt-get install openssh-server
Para averiguar la IP de nuestra máquina (aunque se recomienda poner una estática para que no haya problemas):
$ ip address
Accedemos al archivo de configuración para indicar el puerto y la dirección IP de escucha:
$ sudo nano /etc/ssh/sshd_config
Port 22
#AddressFamily any
#ListenAddress 0.0.0.0
ListenAddress 192.168.1.180
Con los siguientes comando podemos iniciar, parar, restaurar y ver el estado de la conexión:
$ sudo /etc/init.d/ssh start
$ sudo /etc/init.d/ssh stop
$ sudo /etc/init.d/ssh restart
$ sudo /etc/init.d/ssh status
En nuestra máquina, para conectarnos al servidor donde instalaremos Asterisk, usaremos MobaXterm, creando una nueva sesión:
Comprobamos que estamos conectados a nuestro servidor:
$ ps ax | grep sshd
Puedes ver el código 📁aquí y la bibliografía 📚 aquí.
Antes de instalar el software Asterisk, debemos asegurarnos de que nuestro sistema operativo esté actualizado, para ello ejecutamos los siguientes comandos:
$ sudo apt update && sudo apt upgrade
$ sudo apt install wget build-essential subversion
Descargamos Asterisk 15.7.1 y descomprimimos el paquete:
$ cd /usr/src/
$ sudo wget http://downloads.asterisk.org/pub/telephony/asterisk/releases/asterisk-15.7.1.tar.gz
$ sudo tar zxf asterisk-15.7.1.tar.gz
Procedemos a la instalación de las dependencias de Asterisk:
$ cd asterisk-15.7.1/
$ sudo contrib/scripts/get_mp3_source.sh
$ sudo contrib/scripts/install_prereq install
Preparamos el código para compilar:
$ sudo ./configure
Una vez finalizado se mostrará lo siguiente:
Seleccionamos los módulos a compilar:
$ sudo make menuselect
Compilamos:
$ sudo make -j2
Instalamos Asterisk:
$ sudo make install
Podemos instalar la documentación con:
$ sudo make progdocs
Para instalar los archivos genéricos de configuración*:
$ sudo make samples
Por último, instalaremos el script de inicio y actualizaremos la memoria caché de las bibliotecas compartidas:
$ sudo make config
$ sudo ldconfig
Como Asterisk se ejecuta como usuario administrador, crearemos un nuevo usuario y configuraremos el programa para que se ejecute con el nuevo usuario.
Crearemos un usuario llamado asterisk:
$ sudo adduser --system --group --home /var/lib/asterisk --no-create-home --gecos "Asterisk PBX" asterisk
Abrimos el siguiente archivo y descomentamos las siguientes líneas:
$ sudo vim /etc/default/asterisk
AST_USER="asterisk"
AST_GROUP="asterisk"
Añadimos nuestro nuevo usuario a los grupos dialout y audio:
$ sudo usermod -a -G dialout,audio asterisk
Cambiamos la propiedad de todos los archivos y directorios de Asterisk para que el usuario pueda acceder a esos archivos:
$ sudo chown -R asterisk: /var/{lib,log,run,spool}/asterisk /usr/lib/asterisk /etc/asterisk
$ sudo chmod -R 750 /var/{lib,log,run,spool}/asterisk /usr/lib/asterisk /etc/asterisk
Para iniciar Asterisk:
$ sudo /etc/init.d/asterisk start
Para acceder a las interfaz de comandos de Asterisk (CLI) ejecutamos (vvvvvv es para el modo verboso):
$ sudo asterisk -rvvvvvvc
o
$ sudo asterisk -r
Ya solo queda habilitar el servicio de Asterisk para iniciarse en el arranque:
$ sudo systemctl enable asterisk
Además, podemos usar los siguientes comandos para iniciar, parar, reiniciar, recargar, forzar la recarga y ver el estado, respectivamente:
sudo /etc/init.d/asterisk {start|stop|restart|reload|force-reload|status}
Es conveniente, antes de empezar a modificar a fondo los ficheros, que hagamos una copia de seguridad de los mismos (sustituir fichero por el nombre del archivo que corresponda):
$ cd /etc/asterisk
$ sudo cp fichero.conf fichero.conf.bck
Debido a que tienen muchas líneas de comentarios y puede ser engorroso al modificarlos, vamos a eliminar los comentarios y las líneas en blanco a través de la línea de comandos de vim:
$ sudo vim /etc/asterisk/fichero.conf
Las siguientes expresiones regulares a usar son (la primera para eliminar comentarios y la segunda líneas vacías):
: g/^\s*;/d
: g/^$/d
Para guardar:
: wq
Por otro lado, vamos a configurar el idioma por defecto de Asterisk, que viene con los sonidos en inglés, por lo que instalaremos también los de castellano.
Los sonidos se encuentran en la carpeta /var/lib/asterisk/sounds/ y, antes de de descargar lo sonidos en español, debemos crear una carpeta:
$ mkdir /var/lib/asterisk/sounds/es
Descargamos el paquete core y extra:
$ cd /var/lib/asterisk/sounds/es
$ wget -O core.zip https://www.asterisksounds.org/es-es/download/asterisk-sounds-core-es-ES-sln16.zip
$ wget -O extra.zip https://www.asterisksounds.org/es-es/download/asterisk-sounds-extra-es-ES-sln16.zip
$ unzip core.zip
$ unzip extra.zip
$ sudo chown -R asterisk.asterisk /var/lib/asterisk/sounds/es
Convertimos los archivos de sonido a otros formatos para que Asterisk no tenga que hacerlo cuando los use, por lo que necesitamos instalar el programa sox:
$ sudo apt-get install sox
Creamos un script para convertirlos en bloque:
$ cd /var/lib/asterisk/sounds/es/
$ sudo vim convertir
y copiamos lo siguiente:\
👀 Ten en cuenta que puede haber errores con la indentación
#!/bin/bash
for a in $(find . -name '*.sln16'); do
sox -t raw -e signed-integer -b 16 -c 1 -r 16k $a -t gsm -r 8k `echo $a|sed "s/.sln16/.gsm/"`;\
sox -t raw -e signed-integer -b 16 -c 1 -r 16k $a -t raw -r 8k -e a-law `echo $a|sed "s/.sln16/.alaw/"`;\
sox -t raw -e signed-integer -b 16 -c 1 -r 16k $a -t raw -r 8k -e mu-law `echo $a|sed "s/.sln16/.ulaw/"`;\
done
Damos permisos y ejecutamos el script:
$ sudo chmod +x convertir
$ sudo ./convertir
Modificamos el fichero sip.conf para establecer por defecto el lenguaje a español:
[general]
language=es
Y por último, recargamos sip desde la consola de Asterisk:
$ sudo asterisk -rvvvvvvc
CLI> sip reload
Vamos a probar que toda la instalación ha ido correctamente con una simple llamada entre dos usuarios, para ellos tenemos que crearlos y dotar de funcionalidad sus extensiones. Utilizaremos los softphone (se explican en el 9. Softphones) Pangolin en un PC y ZoiPer en un teléfono Android. Los archivos a modificar con sip.conf y extensions.conf, que explicaremos en el siguiente apartado, por ahora solo es una prueba rápida.
Modificamos el archivo sip.conf para añadir lo siguiente:
$ sudo vim /etc/asterisk/sip.conf
[general]
port=5060
directmedia=no
language=es
context=public[javier]
type=friend
secret=12345678
context=empleado
host=dynamic
canreinvite=no
nat=force_report[belen]
type=friend
secret=12345678
context=empleado
host=dynamic
canreinvite=no
nat=force_rport,comedia
Donde port=5060 es el puerto donde nos va permitir conectarnos a Asterisk y direcmedia=no evita que la secuencia de medios RTP vaya directamente del llamante al llamado, ya que muchos dispositivos no lo soportan. javier y belen son las dos personas que están habilitadas para llamar, es decir, los nombres de los dispositivos y, bajo ellas, se encuentran algunas directrices como type=friend, para permitir enviar y recibir llamadas y que un usuario(extensión) tenga múltiples teléfonos; secrect que es la contraseña para conectarse; context=empleado, el contexto al que pertenecen en el plan de marcación; host=dynamic, para que el dispositivo se registre con Asterisk; canreinvite=no, para detener el envío de las invitaciones una vez se ha establecido las llamadas; y nat=force_rpot,comedia, para trabajar según RFC3581 y obligar a trabajar con soporte simétrico RTP, es decir, un cliente SIP usa el mismo socket/puerto para enviar y recibir el flujo de datos RTP.
Modificamos el fichero extensions.conf, añadiendo el contexto empleado:
$ sudo vim /etc/asterisk/extensions.conf
[empleado]
exten => 4000,1,Dial(SIP/javier)
exten => 4001,1,Dial(SIP/belen)
Como vemos, le hemos asignado el puerto 4000 a javier y el 4001 a belen.
A accedemos a la consola de Asterisk y actualizamos el dialplan y sip:
CLI> dialplan reload
CLI> sip reload
Ahora, ya podemos ir a Pangolin e intentar acceder con el usuario belen:
Y desde ZoiPer con javier:
En la consola de Asterisk podemos ver que se han registrado correctamente:
Para llamar basta con marcar la extensión de la otra persona en uno de los teléfonos, por ejemplo, javier llama a belén:
Desde la consola de Asterisk podemos ver la traza de la llamada:
✏️ Todo se realizará utilizando el protocolo SIP.
Vamos a configurar una serie de serie de servicios (funcionalidades) en un nuestra PBX con un pequeño grupo de usuario para comprobar que todo se implementa correctamente y, en el apartado 7, extenderlo a un Call Center.
Las funcionalidades a implementar son las siguiente:
- 4.1 Buzón de voz (voicemail)
- 4.2 Redirección/transferencia de llamadas
- 4.3 Centro de mensajes (SIP messages)
- 4.4 Llamadas a grupo (call distribution)
- 4.5 Captura de llamada (call pickup)
- 4.6 Conversión de texto a voz (TTS) con Festival
- 4.7 Segurización de llamadas
- 4.8 Simular llamadas desde el exterior (redirección a un contexto diferente)
- 4.9 Horario de atención
- 4.10 Registro (log)
- 4.11 Cola y distribución de llamadas
Vamos a añadir un buzón de voz para que, cuando se llame a una extensión y no responda, se le pueda dejar un mensaje de voz.
Puedes ver el código 📁aquí y la bibliografía 📚 aquí.
Tenemos que modificar el fichero voicemail.conf:
$ sudo vim /etc/asterisk/voicemail.conf
Como vemos, tiene varios contextos:
Eliminaremos [other] y nos quedaremos con [default], donde dicha línea
1234 => 4242, Example Mailbox, root@localhost
significa lo siguiente:
- 1234: número del buzón
- 4242: pin que necesitamos para acceder al buzón
- Example Mailbox: nombre del buzón
- root@localhost: dirección email donde será enviado el mensaje
Añadimos las nuestras para nuestros usuarios de prueba javier y belén:
[default]
4000 => 1234,Javier,[email protected]
4001 => 1234,Belen,[email protected]
En este caso en número de buzón coinciden con el de las extensiones para que sea más fácil de memoriza, pero no tendrían por qué.
Accedemos a la consola de Asterisk y recargamos voicemail:
CLI> voicemail reload
Podemos ver la configuración cargada:
Modificamos el fichero extensions.conf:
$ sudo vim /etc/asterisk/extensions.conf
[empleado]
exten => 4000,1,NoOp(Llamando a Javier)
same => n,Dial(SIP/javier,15)
same => n,VoiceMail(${EXTEN})
same => n,Hangup()exten => 4001,1,NoOp(Llamando a Belen)
same => n,Dial(SIP/belen,15)
same => n,VoiceMail(${EXTEN})
same => n,Hangup()exten => *4000,1,VoiceMailMain(4000)
same => n,Hangup()exten => *4001,1,VoiceMailMain(4001)
same => n,Hangup()
En número al final de la función Dial indica los segundos que va a estar llamando, es decir, a los 15 segundos pasa a la función VoiceMail para que dejen un mensaje de voz.
Con *extension, marcando ese número (asterisco incluido) pueden oír su buzón de voz, para el que se les pedirá la contraseña (pin configurado en voicemail.conf)
Hacemos la prueba y llamamos desde javier a belén, donde, tras no cogerlo, nos habla una locución en español que nos dice que podemos dejar un mensaje. En la consola de Asterisk podemos ver toda la traza de la llamada, incluso la locución que aparece.
Comprobamos que el mensaje se ha guardado en 3 codificaciones distintas (wav49, gsm y wav), donde el nombre de la carpeta default es el nombre del contexto y 4001 el número del buzón en el fichero voicemail.conf:
$ ls -l /var/spool/asterisk/voicemail/default/4001/INBOX
Ahora, desde el número de belén, marcando *4001 e introduciendo su pin (‘1234’), podemos oír su buzón de voz:
La traza la podemos ver en la consola de Asterisk:
La posibilidad de redirigir/transferir llamadas desde a otra extensión recae en el teléfono que utilicemos. Por ello, para probar esta característica, vamos a habilitar una extensión más que lo único que haga es reproducir una canción.
Puedes ver el código 📁aquí y la bibliografía 📚 aquí.
Para añadir una música personaliza, lo más sencillo es convertirla del formato mp3 a sln16 y luego ejecutar el script que usamos en el apartado 3.5 Cambio de idioma por defecto de Asterik.
Para convertir nuestro archivo a sln16 debemos usar el programa sox:
$ sox file.mp3 --type raw --encoding signed-integer --bits 16 --channels 1 --rate 16k file.sln16
Ahora solo hay que mover los archivos de audio obtenidos a la ruta ‘/var/lib/asterisk/sounds/es/[carpeta]’, para lo que, si no los tenemos en la máquina donde se encuentra Aserisk, debemos hacerlo por SFTP. En este caso vamos a utilizar un cliente con mucho reconocimiento como es FileZilla:
Con esto ya podemos usar nuestro audio personalizado en Asterisk.
Modificamos el fichero extensions.conf y bajo el conexto empleado añadimos:
$ sudo vim /etc/asterisk/extensions.conf
exten => 100,1,NoOp(Centralita de distribucion de llamadas)
same => n,Playback(silence/1)
same => n,Playback(mimusica/primavera)
same => n,Hangup()
La extensión añadida es la 100. La función Playback reproduce los sonidos del directorio ‘/var/lib/asterisk/sounds/es/’. En este caso reproduce un silencio de un segundo de la carpeta ‘/es/silence/’ y posteriormente el sonido ‘primavera’ localizado en ‘es/mimusica’.
Actualizamos el dialpan:
CLI> dialplan reload
Vamos a llamar de belén a javier y posteriormente transferiremos la llamada desde belén para que javier pase a establecer conexión a la extensión 100:
Llamamos a *javier* y, cuando descuelgue, pulsamos la tecla *transfer*:
Escribimos la extensión a la que transferir, en este caso la 100:
Podemos ver la traza en la consola de Asterisk:
Por ejemplo, en Blink, para realizar una transferencia de llamada debemos, una vez hemos descolgado el teléfono, realizar una llamada a la extensión a la que queremos transferir dicha llamada y arrastrar la sesión con la persona que se desea transferir hacia la sesión con la persona a la que se transfiere mientras mantenemos presionada la tecla Alt.
Asterisk permite enviar mensajes vía SIP, por lo que vamos a crear un centro de mensajes para que los usuarios puedan enviarse texto, pero no es una mensajería instantánea.
Puedes ver el código 📁aquí y la bibliografía 📚 aquí.
Modificamos el fichero sip.conf:
$ sudo vim /etc/asterisk/sip.conf
;**-----Para los mensajes-----
accept_outofcall_message=yes
outofcall_message_context=mensajes
auth_message_requests=yes
subscribecontext=suscribir
outofcall_message_context=mensajes y subscribecontext=suscribir son los contextos referidos al dialplan (extensions.conf).
Modificamos el fichero extensions.conf:
$ sudo vim /etc/asterisk/extensions.conf
[suscribir]
exten => 4000,hint,SIP/javier
exten => 4001,hint,SIP/belen[mensajes]
exten => _X.,1,NoOp(Mensaje de ${MESSAGE(from)})
same => n,NoOp(Mensaje para ${MESSAGE(to)})
same => n,NoOp(Cuerpo del mensaje: ${MESSAGE(body)})
same => n,Set(dest=${EXTEN})
same => n,Set(remitente=${CUT(MESSAGE(from),<,2)})
same => n,Set(remitente=${CUT(remitente,@,1)})
same => n,Set(remitente=${CUT(remitente,:,2)})
same => n,Set(texto=${MESSAGE(body)})
same => n,Set(MESSAGE(body)=${remitente}: ${texto})
same => n,GotoIf($["$ {EXTEN}" = "4000"]?4000)
same => n,GotoIf($["$ {EXTEN}" = "4001"]?4001)
same => n,MessageSend(${EXTEN}, Centro de Mensajes(CdM))
same => n,Noop(Estado del mensaje ${MESSAGE_SEND_STATUS})
same => n,GotoIf($["$ {MESSAGE_SEND_STATUS}" != "SUCCESS"]?fallo,s,1)
same => n,Hangup()exten => 4000,4000,NoOp(Mensaje a Javier)
same => n,MessageSend(sip:javier, Centro de Mensajes(CdM))
same => n,Noop(Estado del mensaje ${MESSAGE_SEND_STATUS})
same => n,GotoIf($["$ {MESSAGE_SEND_STATUS}" != "SUCCESS"]?fallo,s,1)
same => n,Hangup()exten => 4001,4001,NoOp(Mensaje a Belen)
same => n,MessageSend(sip:belen, Centro de Mensajes(CdM))
same => n,Noop(Estado del mensaje ${MESSAGE_SEND_STATUS})
same => n,GotoIf($["$ {MESSAGE_SEND_STATUS}" != "SUCCESS"]?fallo,s,1)
same => n,Hangup()[fallo]
exten => s,1,Set(MESSAGE(body)=CdM: El mensaje "${texto}" para ${dest} no ha sido enviado)
same => n,Set(remit=${CUT(MESSAGE(from),<,2)})
same => n,Set(remit=${CUT(remit,@,1)})
same => n,MessageSend(${remit},Centro de Mensajes(CdM))
same => n,NoOp(Estado del mensaje ${MESSAGE_SEND_STATUS})
same => n,Hangup()
Bajo el contexto suscribir definimos con ‘hint’ a qué nombre de dispositivo hace referencia y en mensajes todo lo que se hará cuando se vaya a enviar un mensaje.
La explicación al código añadido es la siguiente:
-
_X.: indica cualquier extensión numérica.
-
NoOp: No hace nada en particular, solo escribe los mensajes en la consola de Asterisk.
-
Set: sirve para asignar valores a variables.
-
GotoIf: te lleva al ‘[[contexto|]extension|]prioridad’ si se cumple la condición.
-
Hangup: finaliza la conexión.
Por tanto, en resumen, cuando se recibe un mensaje, se obtiene el remitente, que se asigna al cuerpo del mensaje antes del mensaje de texto que el usuario ha mandado, para, posteriormente chequear a qué extensión se ha enviado el mensaje e ir hacia ella. Se manda el mensaje con remitente ‘Centro de Mensajes(CdM)’ y, si ha habido algún fallo se salta al contexto ‘fallo’ que indica al usuario que el mensaje no ha podido ser enviado.
Por último, actualizamos el dialpan y sip:
CLI> dialplan reload
CLI> sip reload
Enviamos un mensaje de belén a javier, para lo que hay que marcar la extensión e indicar que queremos mandar un mensaje (depende del softphone que se use):
En el softphone de belén:
En el softphone de javier:
Mandamos desde javier un mensaje a una extensión que no está configurada:
En la consola de Asterisk podemos ver las trazas:
No hay que confundirlas con las conferencias, la funcionalidad de una llamada a un grupo radica en que, cuando se marca una extensión, esa misma llamada se replica a varias extensiones para que pueda ser atendida por cualquiera de ellos. El primero que atienda la llamada se queda con ella.
Por ejemplo, una extensión que pertenezca a un departamento y, al marcarla, que suene en todos los terminales de los empleados del departamento.
Puedes ver el código 📁aquí y la bibliografía 📚 aquí.
Modificamos el fichero sip.conf para añadir una nueva persona a nuestra PBX:
$ sudo vim /etc/asterisk/sip.conf
[oscar]
type=friend
secret=201097
context=entrantes
host=dynamic
canreinvite=no
nat=force_rport,comedia
Hemos añadido a óscar en el contexto entrantes.
Modificamos el fichero extensions.conf:
$ sudo vim /etc/asterisk/extensions.conf
[entrantes]
exten => 4040,1,NoOp(Llamando al grupo 'empleados')
same => n,Dial(SIP/javier&SIP/belen,60)
same => n,Hangup()
De esta forma, cuando se marque el número 4040, la llamada será redirigida a javier y belén durante 60 segundos. Si durante 60 segundos ninguno de los dos responde, la llamada finalizará.
Por último, actualizamos el dialpan y sip:
CLI> dialplan reload
CLI> sip reload
Vamos a llamar desde el terminal de óscar a javier y belén y será ella la que responda a la llamada.
Desde el teléfono de óscar:
Desde el teléfono de javier:
Desde el teléfono de belén:
Podemos ver la traza en la consola de Asterisk:
Esta función permite capturar (“robar”) una llamada a otro terminal que aún no ha sido respondida para recibirla en tu terminal. Es muy útil cuando en un departamento hay muchos terminales y llaman a una extensión en particular, pero no hay nadie en el sitio, por lo que otra persona, en lugar de levantarse e ir a responder, puede hacerlo desde su terminal simplemente marcando un prefijo (en nuestro caso *8[extensión]) y el número de la extensión a la que se está llamando.
Para nuestro ejemplo, crearemos un nuevo usuario, director, bajo el contexto dirección y permitiremos que javier y belén puedan capturarse llamadas entre ellos, pero no puedan capturar llamadas del director. Por su parte, el director podrá capturar llamadas tanto de belén como de javier.
Puedes ver el código 📁aquí y la bibliografía 📚 aquí.
Localizado en ‘/etc/asterisk/’. No modificaremos este archivo, ya que usaremos las configuración preestablecida, pero en él se encuentra la configuración de características de llamadas de muestra.
Si le echamos un vistazo, podemos ver qué extensión está por defecto para capturar llamadas:
$ sudo vim /etc/asterisk/features.conf
También la podemos ver desde la consola de Asterisk:
CLI> features show
Creamos el nuevo usuario director que pertenecerá al contexto dirección:
$ sudo vim /etc/asterisk/sip.conf
[director]
type=friend
secret=130261
context=direccion
host=dynamic
canreinvite=no
nat=force_rport,comedia
Ahora debemos añadir a cada dispositivo los valores callgroup, que define el grupo al que pertenece este dispositivo, y pickupgroup, los grupos a los cuales el dispositivo puede capturar las llamadas.
[javier]
type=friend
secret=310195
context=empleado
host=dynamic
canreinvite=no
nat=force_rport,comedia
callgroup=2
pickupgroup=2[belen]
type=friend
secret=120995
context=empleado
host=dynamic
canreinvite=no
nat=force_rport,comedia
callgroup=2
pickupgroup=2[director]
type=friend
secret=130261
context=direccion
host=dynamic
canreinvite=no
nat=force_rport,comedia
callgroup=1
pickupgroup=1,2
De esta forma, director (grupo 1) puede capturar llamadas del grupo 1 y del grupo 2, al que pertenecen javier y belén.
Modificamos el fichero extensions.conf:
$ sudo vim /etc/asterisk/extensions.conf
Añadimos el contexto dirección y le habilitamos para que pueda llamar a javier y belén:
[direccion]
;**####-Extensiones habilitadas-####
;**-----javier
exten => 4000,1,NoOp(Llamando a Javier)
same => n,Dial(SIP/javier,15)
same => n,VoiceMail(${EXTEN})
same => n,Hangup()
;**-----belen
exten => 4001,1,NoOp(Llamando a Belen)
same => n,Dial(SIP/belen,15)
same => n,VoiceMail(${EXTEN})
same => n,Hangup()
;**####-Captura de llamadas-####
exten => _*8.,1,NoOp(Capturando llamada a la extension ${EXTEN:2})
same => n,Pickup(${EXTEN:2}@empleado)
same => n,Hangup()
Además, cuando se marque *8 seguido de un número capturamos la llamada con la función Pickup.
Esto mismo lo debemos poner en el contexto empleado:
;**####-Captura de llamadas-####
exten => _*8.,1,NoOp(Capturando llamada a la extension ${EXTEN:2})
same => n,Pickup(${EXTEN:2})
same => n,Hangup()
Por último, actualizamos el dialpan y sip:
CLI> dialplan reload
CLI> sip reload
Vamos a probar que, cuando se llama a belén, javier puede capturar la llamada:
Desde el teléfono del belén:
Desde el teléfono del javier:
Podemos ver la traza en la consola de Asterisk:
Esta es la traza en la consola de Asterisk cuando javier intenta capturar una llamada al director, lo cual no le está permitido:
Una de las cosas a las que estamos más habituados cuando llamamos a una centralita (atención al cliente, centro de soporte, etc.) es que nos aparezca una locución que nos informe o nos diga qué número marcar dependiendo de con quién nos interese contactar.
Puedes ver el código 📁aquí y la bibliografía 📚 aquí.
Vamos a implementar un conversor de texto a vox (Text-to-Speech, TTS) gratuito llamado Festival, para lo que primero debemos instalarlo en nuestro sistema:
$ sudo apt-get install festival
Paramos Asterisk:
$ sudo /etc/init.d/asterisk stop
Modificamos el fichero festival.scm y añadimos lo siguiente antes de la última línea (provide ‘festival):
$ sudo vim /usr/share/festival/festival.scm
(define (tts_textasterisk string mode)
(let ((wholeutt (utt.synth (eval (list 'Utterance 'Text string)))))
(utt.wave.resample wholeutt 8000)
(utt.wave.rescale wholeutt 5)
(utt.send.wave.client wholeutt)))
Reiniciamos el SO:
$ sudo reboot
Festival debe iniciarse a la vez que el SO, para lo que debemos de crear un servicio, ya que no se crea uno por defecto.
Creamos el servicio:
$ sudo vim /etc/systemd/system/festival.service
Copiamos lo siguiente y guardamos:
Description=Servicio para Festival TTS
Wants=network.target
After=syslog.target network-online.target[Service]
Type=simple
ExecStart=/usr/bin/festival --server
Restart=on-failure
RestartSec=10
KillMode=process[Install]
WantedBy=multi-user.target
Recargamos los servicios:
$ sudo systemctl daemon-reload
Habilitamos el servicio:
$ sudo systemctl enable festival.service
Reiniciamos y comprobamos que está activo:
$ sudo reboot
$ sudo systemctl status festival.service
Podemos iniciar, parar o ver el estado de nuestro servicio de la siguiente forma:
$ sudo systemctl start|stop|status festival.service
Las voces que vamos a usar son de la Junta de Andalucía y se encuentran disponibles en su repositorio de GitHub.
$ cd /home/javier/archivos
$ wget https://github.com/franjvasquezg/festival-spanish-voices/archive/master.zip
$ unzip master.zip
$ cd festival-spanish-voices-master/
$ sudo mkdir /usr/share/festival/voices/spanish
$ sudo cp -r JuntaDeAndalucia_es_* /usr/share/festival/voices/spanish/
Si observamos los archivos descargados hay dos carpetas:
$ ls -l /usr/share/festival/voices/spanish/
JuntaDeAndalucia_es_pa_diphone se corresponde con la voz masculina y JuntaDeAndalucia_es_sf_diphone con la femenina.
Vamos a seleccionar en Festival la voz femenina por defecto:
$ sudo vim /etc/festival.scm
Y añadimos esta línea al final del archivo:
(set! voice_default 'voice_JuntaDeAndalucia_es_sf_diphone)
Reiniciamos el servicio:
$ sudo systemctl restart festival.service
Accedemos a nuestro directorio de Asterisk y lanzamos el menú:
$ cd /usr/src/asterisk-15.7.1/
$ sudo make menuselect
En la pantalla que aparece nos aseguramos de marcar Applications --> app_festival (bajo el apartado Extended):
Guardamos e instalamos:
$ sudo make install
Iniciamos Asterisk:
$ sudo /etc/init.d/asterisk start
Accedemos a la consola de Asterisk y vemos que se ha instalado la aplicación:
$ sudo asterisk -rvvvvvvc
CLI> core show application festival
Si no apareciese nada, habría que cargar el módulo:
CLI> module load app_festival.so
CLI> module show app_festival.so
Haremos que, cuando se llame a la centralita (extensión 100), se escuche “Va a escuchar un fragmento de la primera de Antonio Vivaldi”. Para ello, modificamos el fichero extensions.conf:
$ sudo vim /etc/asterisk/extensions.conf
;**-----centralita
exten => 100,1,NoOp(Centralita de distribucion de llamadas)
same => n,NoOp(Esta llamando ${ODBC_info(${CALLERID(number)})})
same => n,Festival(Va a escuchar un fragmento de la primavera de Antonio Vivaldi)
same => n,Playback(silence/1)
same => n,Playback(mimusica/primavera)
same => n,Hangup()
Actualizamos el dialpan:
CLI> dialplan reload
Llamamos con el usuario javier a la extensión 100 y podemos ver la traza que deja en la consola de Asterisk:
Hoy en día una de las principales preocupaciones es la privacidad, por eso es importante que nuestros sistemas sean lo más seguros posibles. Con la intención de hacer algo más seguras nuestras comunicaciones en Asterisk, vamos a implementar el cifrado de llamadas haciendo uso del protocolo TLS (Transport Layer Security o, en español, seguridad de la capa de transporte).
Puedes ver el código 📁aquí y la bibliografía 📚 aquí.
Lo primero es crear un directorio para almacenar nuestras claves:
$ sudo mkdir /etc/asterisk/keys
Para crear las claves usaremos el script ast_tls_cert que Asterisk nos provee. Ahora, vamos a crear una autoridad de certificación autofirmada y un certificado Asterisk que nos servirá posteriormente para crear los certificados de los clientes:
$ cd /usr/src/asterisk-15.7.1/contrib/scripts/
$ sudo ./ast_tls_cert -C 192.168.1.180 -O "Tutorial Asterisk" -d /etc/asterisk/keys
-
-C: definimos nuestro host (DNS o nuestra dirección IP).
-
-O: definimos el nombre de nuestra organización.
-
-d: el directorio de salida de nuestras claves.
Nos pedirá una frase que debemos recordar, en este caso será “Esto es un tutorial Asterisk”:
Vemos que se han generado las claves:
$ ls -l /etc/asterisk/keys/
Ahora es el turno de generar los certificados para nuestros clientes:
$ sudo ./ast_tls_cert -m client -c /etc/asterisk/keys/ca.crt -k /etc/asterisk/keys/ca.key -C 192.168.1.180 -O "Tutorial Asterisk" -d /etc/asterisk/keys -o javier
-
-m client: indicamos al script que el certificado es para un cliente y no para un servidor.
-
-c: indicamos cuál es la autoridad que usaremos para generar el certificado.
-
-k: proporcionamos la clave para la autoridad de certificación que definimos anteriormente.
-
-C: definimos el nombre de host o la dirección IP de nuestro teléfono SIP.
-
-O: define el nombre de nuestra organización.
-
-d: el directorio de salida de nuestras claves.
-
-o: el nombre de la clave que estamos generando.
Nos pedirá la frase que indicamos anteriormente (“Esto es un tutorial Asterisk”) cuando creamos la autoridad de certificación:
Vemos que se ha generado el certificado correctamente:
$ ls -l /etc/asterisk/keys/
javier.crt y javier.pem son los ficheros que debemos copiar a nuestro teléfono SIP.
Modificamos el fichero sip.conf:
$ sudo vim /etc/asterisk/sip.conf
Bajo el contexto general añadimos:
tlsenable=yes
tlsbindaddr=0.0.0.0
tlscertfile=/etc/asterisk/keys/asterisk.pem
tlscafile=/etc/asterisk/keys/ca.crt
tlscipher=ALL
tlsclientmethod=tlsv1
Y en javier:
[javier]
type=friend
secret=310195
context=empleado
host=dynamic
canreinvite=no
nat=force_rport,comedia
callgroup=2
pickupgroup=2
transport=tls
Recargamos sip:
CLI> sip reload
Vemos el transporte permitido para usuarios con certificado (TLS) y que no posea certificado (UDP):
CLI> sip show peer javier
CLI> sip show peer belen
Utilizaremos el cliente SIP Blink:
Seleccionamos javier.pem:
Podemos ver que se ha conectado correctamente:
CLI> sip show tcp
CLI> sip show peers
Además de las llamadas internas, la principal función de una PBX es permitir las llamadas desde el exterior directamente a una extensión, sin pasar por una centralita que nos redirija, para lo que normalmente se le suele asignar a una empresa un rango para los últimos números finales.
Por ejemplo, ya que en España el plan de marcación es de 9 cifras, vamos a suponer que a nuestra empresa se le asigna en número 995554XXX, es decir, los números comprendidos entre 995554000 y 995554999.
Puedes ver el código 📁aquí y la bibliografía 📚 aquí.
Modificamos el fichero sip.conf para crear un usuario, que pertenecerá al contexto entrantes que será con el que se llamen desde el exterior:
$ sudo vim /etc/asterisk/sip.conf
[exterior]
type=friend
secret=123456
context=entrantes
host=dynamic
canreinvite=no
nat=force_rport,comedia
Modificamos el fichero extensions.conf:
$ sudo vim /etc/asterisk/extensions.conf
Bajo el contexto entrantes añadimos:
;----redireccion a la extension marcada
exten => _995554XXX,1,Set(extension=${EXTEN:5})
same => n,NoOp(Llamando a la extension ${extension})
same => n,Goto(empleado,${extension},1)
same => n,Hangup()
El código añadido implica que, cuando se llame a un teléfono 995554XXX, se capturen los últimos 4 números (4XXX) como extensión y se redirija al contexto empleado a la extensión capturada.
Bajo el contexto empleado añadimos lo siguiente:
;####-Llamadas desde el exterior que no tienen extension asociada-####
exten => _4XXX,1,Answer()
same => n,Festival(La extension ${extension} no se encuentra disponible)
same => n,Hangup()
Esto implica que, si anteriormente la extensión no tiene ningún dispositivo asociado, se escuchará “La extensión 4XXX no se encuentra disponible”.
Por último, actualizamos el dialpan y sip:
CLI> dialplan reload
CLI> sip reload
Llamamos desde el usuario exterior a una extensión disponible como es la 4001 (belen), marcando 995554001 y vemos la traza que deja en la consola de Asterisk:
Llamamos desde el usuario exterior a una extensión no disponible como es la 4022, marcando 995554022 y vemos la traza que deja en la consola de Asterisk:
Generalmente las compañías no suelen recibir llamadas 24/7, por lo que cuando llamas fuera del horario no hay nadie que te atienda. Por ello, vamos a establecer, para cuando se reciban llamadas del exterior, una condición de horario que te permita seguir con la llamadas cuando esta se ha realizado dentro de las horas establecidas o, en caso contrario, se escuche una locución indicando “En estos momentos no hay nadie que te pueda atender. Nuestro horario es de 8 a 20 horas de lunes a viernes”.
Puedes ver el código 📁aquí y la bibliografía 📚 aquí.
Modificamos el fichero extensions.conf:
$ sudo vim /etc/asterisk/extensions.conf
Bajo el contexto entrantes añadimos:
;----redireccion a la extension marcada
;----con horario establecido L-V de 8 a 20h
exten => _995554XXX,1,Set(extension=${EXTEN:5})
same => n,NoOp(Llamando a la extension ${extension})
same => n,GotoIfTime(8:00-20:00,mon-fri,,?empleado,${extension},1)
same => n,Festival(En estos momentos no hay nadie que te pueda atender)
same => n,Festival(Nuestro horario es de 8 a 20 horas de lunes a viernes)
same => n,Hangup()
Hemos sustituido la función Goto por GotoIfTime que funciona de la siguiente forma:
GotoIfTime(<rango de horas>,<días de la semana>,<días del mes>,<meses>?[si es verdadero][:si es falso])
De tal forma, hemos establecido las hora de 8 a 20 (8:00-20:00), los días de la semana de lunes a vienes (mon-fri), cualquier día del mes (*) y cualquier mes (*). Si se corresponde a ese horario salta al contexto empleado a la extensión que corresponda., sino prosigue con la locución.
Por último, actualizamos el dialpan:
CLI> dialplan reload
Llamamos desde el usuario exterior al teléfono 995554001 (extensión 4001, belén) fuera del horario establecido (he modificado el código anterior para hacer la prueba) y vemos la traza que deja en la consola de Asterisk:
Llamamos desde el usuario exterior al teléfono 995554001 (extensión 4001, belén) dentro del horario establecido (con el código correctamente) y vemos la traza que deja en la consola de Asterisk:
Todos los sistemas suelen tener un registros (log) donde se guarda lo que sucede y Asterisk no es menos.
Puedes ver el código 📁aquí y la bibliografía 📚 aquí.
Asterisk posee, además del registro que podemos ver por su consola, un fichero con los datos de las llamadas localizado en /var/log/asterisk/ y con nombre messages.
Podemos ver las últimas líneas del fichero de la siguiente forma:
$ tail /var/log/asterisk/messages
Desde la consola de Asterisk podemos ver la configuración del logger:
CLI> logger show channels
La consola tiene activado el modo verbose, por lo que nos parecen los registros cuando se hace una llamada, pero en el fichero no quedan registrados.
Para que queden registradas todas las llamadas en el fichero messages, debemos añadir al fichero /etc/asterisk/logger.conf el modo verbose():
$ sudo vim /etc/asterisk/logger.conf
Bajo el contexto logfiles:
console => notice,warning,error
messages => notice,warning,error,verbose(3)
Ahora, recargamos el logger desde la consola de Asterisk y comprobamos que se ha aplicado la configuración:
CLI> logger reload
CLI> logger show channels
✏️No se recomienda tener activado esta configuración durante mucho tiempo, ya que incremente notablemente el tamaño del fichero, por lo que se suele usar de modo puntual para localizar errores.
Vamos a realizar una llamada y a comprobar que ahora se escribe en el fichero /var/log/asterisk/messages:
$ tail -n 15 /var/log/asterisk/messages
Asterisk guarda también un fichero en formato CSV con todos los registros de llamadas. Se encuentra en la ruta /var/log/asterisk/cdr-csv y lleva por nombre Master.csv.
$ column -s, -t < /var/log/asterisk/cdr-csv/Master.csv | less -#2 -N -S
Vamos a crear una cola de llamadas para que cuando llamen más personas de las que los operadores puedan atender, permanezcan a la espera hasta ser atendidos. Los operadores serán estáticos, es decir, ya están establecidas qué extensiones serán los operadores.
Puedes ver el código 📁aquí y la bibliografía 📚 aquí.
Vamos a cambiar la música en espera por defecto por la nuestra propia, para ello tenemos que modificar el fichero musiconhold.conf creando un nuevo contexto que se llamará recepción y que apunte a la carpeta donde estaba el sonido primavera:
$ sudo vim /etc/asterisk/musiconhold.conf
[recepcion]
mode=files
directory=/var/lib/asterisk/sounds/es/mimusica/
Actualizamos moh y vemos que se ha cargado correctamente:
CLI> moh reload
CLI> moh show classes
Modificamos el fichero queues.conf donde añadiremos el contexto recepción al que pertenecerán javier y belén:
$ sudo vim /etc/asterisk/queues.conf
[general]
persistentmembers = yes
monitor-type = MixMonitor[recepcion]
strategy = leastrecent
timeout = 30
member => SIP/javier
member => SIP/belen
Las estrategia leastrecent llama al dispositivo que hace más tiempo que colgó una llamada en esta cola y con timeout definimos el tiempo máximo en segundos que se puede llamar al miembro que corresponde al cruzar la cola.
Actualizamos las colas y visualizamos que se ha cargado correctamente:
CLI> queue reload all
CLI> queue show recepcion
Modificamos el fichero extensions.conf:
$ sudo vim /etc/asterisk/extensions.conf
Incluimos bajo el contexto empleado una nueva extensión, la 4444, que será la que redirija las llamadas a la cola durante 120 segundos:
;-----recepcion
exten => 4444,1,NoOp(Recepcion)
same => n,Set(CHANNEL(musicclass)=recepcion)
same => n,Answer()
same => n,Queue(recepcion,,,,120)
same => n,Hangup()
La función Queue tiene diversos parámetros que podemos añadir:
CLI> core show application Queue
Queue(queuename[,options[,URL[,announceoverride[,timeout[,AGI[,macro[,gosub[,rule[,position]]]]]]]]])
No hay que confundir el tiempo que va a estar la llamada en la cola (120 segundos) con el tiempo (timeout) del fichero queues.conf, que es el tiempo que durará la llamada a cada miembro disponible.
Actualizamos el dialpan:
CLI> dialplan reload
Llamamos con el dispositivo exterior al teléfono 995554444:
Vemos que se cambia la música en espera a la que nosotros establecimos y que se llama a javier cada 30 segundos si no responde, esto es así porque fue belén la última en responder una llamada. Cuando javier indica que está ocupado es entonces cuando la llamada pasa a belén.
Una de las opciones que más flexibilidad proporciona a una PBX es la integración con una base de datos, ya que no haría falta modificar el código de la PBX cuando se quieran modificar algunos parámetros (por ejemplo, el nombre de usuario), facilitando enormemente su mantenimiento.
En este caso, la base de datos que vamos a instalar será MariaDB, una bifurcación de MySQL.
Puedes ver el código 📁aquí y la bibliografía 📚 aquí.
Nos aseguramos de actualizar la lista de paquetes disponibles e instalar las nuevas versiones si las hubiera:
$ sudo apt update
$ sudo apt upgrade
Descargamos el paquete del repositorio:
$ cd /home/javier/archivos/
$ wget http://repo.mysql.com/mysql-apt-config_0.8.14-1_all.deb
Instalamos el paquete:
$ sudo dpkg -i mysql-apt-config_0.8.14-1_all.deb
En la ventana que aparece, tenemos habilitar si no los estuviera “MySQL Tools & Connectors”, ya que así se habilitará el odbc que utilizaremos luego para la integración con Asterisk:
Una vez haya terminado, accedemos a la base de datos para probar que todo ha ido bien:
$ sudo mysql -u root
Como podemos observar, nos ha dejado entrar al usuario de administración sin ningún tipo de seguridad. Para establecer una contraseña a nuestro usuario, dentro de la base de datos:
MariaDB> UPDATE mysql.user SET PASSWORD=PASSWORD('root') WHERE USER='root';
MariaDB> flush privileges;
Donde PASSWORD=PASSWORD('nuestra_nueva_contraseña'), que en este caso, por ser de prueba, va a ser la misma que el nombre de usuario.
Ahora, para que siempre nos pida la contraseña para entrar:
MariaDB> UPDATE mysql.user SET plugin = '' WHERE user = 'root';
MariaDB> quit; #para salir
Ya solo tenemos que reiniciar el servicio de la base de datos:
$ sudo systemctl restart mysql
Llevará por nombre asterisk y con contraseña igual al nombre.
MariaDB> use mysql;
MariaDB> CREATE USER 'asterisk'@'localhost' IDENTIFIED BY 'asterisk';
MariaDB> select user, host from user;
Probamos a acceder con nuestro nuevo usuario:
$ sudo mysql -u asterisk -p
Vamos a crear una nueva base de datos para que pueda ser usada por Asterisk.
Accedemos a la consola de MariaDB:
$ sudo mysql -u root -p
MariaDB> CREATE DATABASE asteriskdb;
MariaDB> show databases;
Crearemos una tabla llamada usuarios que tendrá dos campos: nombre y extensión.
MariaDB> use asteriskdb;
MariaDB> CREATE TABLE usuarios(nombre VARCHAR(15), extension INTEGER);
MariaDB> show tables;
Insertamos los usuarios:
MariaDB> insert into usuarios values("javier", 4000);
MariaDB> insert into usuarios values("belen", 4001);
MariaDB> insert into usuarios values("direccion", 5000);
MariaDB> select * from usuarios;
Damos acceso de lectura, inserción, actualización y eliminación de registros a nuestro usuario al esquema asteriskdb para que pueda acceder y modificar el contenido de todas las tablas bajo dicho esquema:
MariaDB> GRANT SELECT, INSERT, UPDATE, DELETE ON asteriskdb.* TO 'asterisk'@'localhost';
ODBC (Open DataBase Connectivity) es estándar para proporcionar una API de acceso a las bases de datos desde cualquier aplicación. En nuestro caso vamos a utilizar unixODBC.
Instalamos el conector unixODBC:
$ sudo apt-get install unixodbc unixodbc-dev
Descargamos el conector de MySQL para ODBC y lo descomprimimos. Podemos buscar la dirección de descarga adecuada para nuestros SO aquí:
$ wget https://dev.mysql.com/get/Downloads/Connector-ODBC/8.0/mysql-connector-odbc-8.0.19-linux-debian9-x86-64bit.tar.gz
$ tar xvzf mysql-connector-odbc-8.0.19-linux-debian9-x86-64bit.tar.gz
Entramos en la carpeta y copiamos los archivos a su ubicación apropiada:
$ cd mysql-connector-odbc-8.0.19-linux-debian9-x86-64bit/
$ sudo cp bin/* /usr/local/bin
$ sudo cp lib/* /usr/local/lib
Por último, registramos la versión del driver usando la herramienta myodbc-installer:
$ sudo myodbc-installer -a -d -n "MySQL ODBC 8.0 Driver" -t "Driver=/usr/local/lib/libmyodbc8w.so"
$ sudo myodbc-installer -a -d -n "MySQL ODBC 8.0" -t "Driver=/usr/local/lib/libmyodbc8a.so"
Podemos ver que han sido registrados correctamente:
$ myodbc-installer -d -l
Estos son los drivers que debemos configurar:
$ sudo vim /etc/odbcinst.ini
[MySQL ODBC 8.0]
Description = Driver ODBC ANSI para MySQL
Driver = /usr/local/lib/libmyodbc8a.so
Setup = /usr/local/lib/libmyodbc8S.so
FileUsage = 1
UsageCount=1[MySQL ODBC 8.0 Driver]
Description = Driver ODBC Unicode para MySQL
Driver = /usr/local/lib/libmyodbc8w.so
Setup = /usr/local/lib/libmyodbc8S.so
FileUsage = 1
UsageCount=2
Ahora configuramos el siguiente fichero para crear un identificador de referencia para usar en Asterisk:
$ sudo vim /etc/odbc.ini
[asterisk-connector]
Description = Conexcion MySQL a la base de datos 'asteriskdb'
Driver = MySQL ODBC 8.0
Database = asteriskdb
Server = localhost
Port = 3306
Socket = /var/run/mysqld/mysqld.sock
Probamos que la conexión se realiza sin problemas (el primer asterisk es el nombre de usuario y el segundo la contraseña):
$ echo "select * from usuarios" | isql -v asterisk-connector asterisk asterisk
Tenemos que recompilar Asterisk para usar ODBC, por lo que vamos a la carpeta donde descomprimimos Asterisk cuando lo descargamos:
$ cd /usr/src/asterisk-15.7.1/
$ sudo ./configure
Lanzamos el menú para seleccionar los siguientes módulos:
- Call Detail Recording -> cdr_odbc , cdr_adaptive_odbc
- Dialplan Functions -> func_odbc, func_realtime
- PBX Modules -> pbx_realtime
- Resource Modules -> res_config_odbc , and res_odbc
$ sudo make menuselect
Pulsamos en “Save & Exit” e instalamos los módulos:
$ sudo make install
Una vez instalado, editamos el fichero res_odbc.conf añadiendo el contexto asterisk:
$ sudo vim /etc/asterisk/res_odbc.conf
[asterisk]
enabled => yes
dsn => asterisk-connector
username => asterisk
password => asterisk
pre-connect => yes
Con enabled activamos la conexión; dns es la referencia a la configuración que habíamos creado; username y password son el nombre de usuario y la contraseña de acceso a nuestra base de datos; y pre-connect para mantener la conexión abierta mientras el módulo está cargado.
Accedemos a la consola de Asterisk y recargamos el módulo:
CLI> module reload res_odbc.so
CLI> odbc show
Vamos a realizar una prueba muy simple, solo para saber que Asterisk puede acceder sin problemas a la base de datos.
Se modificará la extensión 100 para que muestre una traza en la consola que indique “Está llamando {nombre} con extensión {extensión}”
Para ello, modificamos el fichero func_odbc.conf, que es donde pondremos las queries a usar en Asterisk:
$ sudo vim /etc/asterisk/func_odbc.conf
[info]
dsn=asterisk
readsql=SELECT CONCAT(nombre,' con extension ', extension) from usuarios WHERE nombre='${SQL_ESC(${ARG1})}'
Modificamos el fichero extensions.conf:
$ sudo vim /etc/asterisk/extensions.conf
Bajo el contexto empleado, en la parte de centralita, añadimos la segunda línea:
;-----centralita
exten => 100,1,NoOp(Centralita de distribucion de llamadas)
same => n,NoOp(Esta llamando ${ODBC_info(${CALLERID(number)})})
same => n,Playback(silence/1)
same => n,Playback(mimusica/primavera)
same => n,Hangup()
Recargamos el módulo func_odbc.so y actualizamos el dialpan:
CLI> module reload func_odbc.so
CLI> dialplan reload
Llamamos desde el dispositivo de javier a la centralita que pone música:
De esta forma comprobamos que Asterisk accede perfectamente a la base de datos.
He clonado la máquina virtual con Asterisk en otro ordenador bajo la misma red. La IP asignada a esta nueva máquina es la 192.168.1.200 y he modificado los usuarios, siendo manuel (2000) y maría (2001) en lugar de javier (4000) y belén (4001), aunque se mantienen los mismos contextos. A la máquina con la IP 192.168.1.180 (javier y belén) nos referiremos como PBX180 y a la de 192.168.1.200 (manuel y maría) como PBX200.
Para realizar la conexión entre dos PBX, utilizaremos DUNDi (Distributed Universal Number Discovery), un protocolo de enrutamiento VoIP para compartir el dialplan entre dos sistemas, similar a ENUM, y las llamadas seguirán usando el protocolo SIP.
Puedes ver el código 📁aquí y la bibliografía 📚 aquí.
Modificamos el fichero extensions.conf para ambas máquinas:
$ sudo vim /etc/asterisk/extensions.conf
En la PBX180:
;**####-INI-DUNDI-####
[dundi-priv-canonical]
exten => _4XXX,1,Dial(Zap/g1/${EXTEN},20,rtT)[dundi-priv-customers]
[dundi-priv-via-pstn]
[dundi-priv-local]
include => dundi-priv-canonical
include => dundi-priv-customers
include => dundi-priv-via-pstn[dundi-priv-switch]
switch => DUNDi/priv>[dundi-priv-lookup]
include => dundi-priv-local
include => dundi-priv-switch[macro-dundi-priv]
exten => s,1,NoOp(Macro dundi-prinv de PBX180)
same => n,Goto(${ARG1},1)
include => dundi-priv-lookup
;**####-FIN-DUNDI-####
En la PBX200:
;**####-INI-DUNDI-####
[dundi-priv-canonical]
exten => _2XXX,1,Dial(Zap/g1/${EXTEN},20,rtT)[dundi-priv-customers]
[dundi-priv-via-pstn]
[dundi-priv-local]
include => dundi-priv-canonical
include => dundi-priv-customers
include => dundi-priv-via-pstn[dundi-priv-switch]
switch => DUNDi/priv[dundi-priv-lookup]
include => dundi-priv-local
include => dundi-priv-switch[macro-dundi-priv]
exten => s,1,NoOp(Macro dundi-prinv de PBX200)
same => n,Goto(${ARG1},1)
include => dundi-priv-lookup
;**####-FIN-DUNDI-####
En ambas PBXs, bajo el contexto empleado añadimos:
;**####-Conexion DUNDi-####
exten => _99999XXXX,1,NoOP(Llamando a ${EXTEN} - ${EXTEN:5})
same => n,Macro(dundi-priv,${EXTEN:5})
De esta forma, cuando marquemos la extensión precedida por 99999 conducirá a la macro dundi-priv que conectará con la otra PBX.
Modificamos el fichero sip.conf en ambas PBXs:
$ sudo vim /etc/asterisk/sip.conf
[priv]
type=peer
context=dundi-priv-local
disallow=all
allow=ilbc
Accedemos a /var/lib/asterisk/keysk, lanzamos el programa astgenkey y copiamos la clave pública en la otra máquina. Aunque es altamente recomendable ponerle una contraseña, en este caso no lo vamos a hacer.
En la PBX180:
$ cd /var/lib/asterisk/keys
$ sudo astgenkey -n PBX180
$ sudo scp PBX180.pub [email protected]:/var/lib/asterisk/keys
$ ls -l
En la PBX200:
$ cd /var/lib/asterisk/keys
$ sudo astgenkey -n PBX200
$ sudo scp PBX180.pub [email protected]:/var/lib/asterisk/keys
Modificamos el fichero dundi.conf:
$ sudo vim /etc/asterisk/dundi.conf
En la PBX180:
[general]
ttl=32
autokill=yes[mappings]
priv => dundi-priv-canonical,0,SIP,192.168.1.180/${NUMBER},nopartial
priv => dundi-priv-customers,100,SIP,192.168.1.180/${NUMBER},nopartial
priv => dundi-priv-via-pstn,400,SIP,192.168.1.180/${NUMBER},nopartial[00:0C:29:52:7C:20] *;*Direccion MAC de la PBX200
model = symmetric
host = 192.168.1.200 *;*IP de la PBX200
inkey = PBX200 *;*Clave publica de la PBX200
outkey = PBX180 *;*Clave privada de la PBC180
include = priv
permit = priv
qualify = yes
order = primary
En la PBX200:
[general]
ttl=32
autokill=yes[mappings]
priv => dundi-priv-canonical,0,SIP,192.168.1.200/${NUMBER},nopartial
priv => dundi-priv-customers,100,SIP,192.168.1.200/${NUMBER},nopartial
priv => dundi-priv-via-pstn,400,SIP,192.168.1.200/${NUMBER},nopartial[00:0C:29:CB:EB:C6] *;*Direccion MAC de la PBX180
model = symmetric
host = 192.168.1.180 *;*IP de la PBX180
inkey = PBX180 *;*Clave publica de la PBX180
outkey = PBX200 *;*Clave privada de la PBX200
include = priv
permit = priv
qualify = yes
order = primary
En cada PBX debemos de poner la dirección MAC de la interfaz de red de la otra PBX, para averiguarla:
$ ip a
Se encuentra junto a la etiqueta ether:
Por último, en cada máquina, actualizamos el dialpan, sip y los módulos:
CLI> dialplan reload
CLI> sip reload
CLI> module reload pbx_dundi.so
CLI> module reload res_crypto.so
Podemos ver que las claves se han cargado correctamente las claves:
CLI> keys show
Y los módulos:
CLI> dundi show mappings
CLI> dundi show peers
Para ver que la conexión se realiza correctamente:
En la PBX180:
CLI> dundi lookup 2000@priv bypass
En la PBX200:
CLI> dundi lookup 4000@priv bypas
Ahora llamamos de javier (PBX180) a maría (PBX200)y vemos las trazas en la consola de Asterisk.
En la PBX180:
En la PBX200:
Vamos a emular el Call Center de un Centro de salud, para lo que eliminaremos las implementaciones anteriores, dejando los archivos nuevos lo más limpios posibles. No iremos paso por paso, sino que se muestran las especificaciones y todas las implementaciones ya realizadas se encuentran en la carpeta 7_call_center.
Puedes ver el código 📁aquí y la bibliografía 📚 aquí.
-
El teléfono del centro es el 757112233.
-
El horario de atención del centro será de 08:00 a 20:00 de lunes a viernes.
-
Si se llama fuera del horario se escuchará una locución que indique que el centro está cerrado y el horario de apertura.
-
-
Cuando un usuario llama se le pedirá que pulse 1 si está registrado o 2 si no lo está.
-
Si está registrado se le pedirá que marque su número de identificación y se verificará en la base de datos.
-
Si no lo está pasa directamente a recepción.
-
Se escuchará música mientras espera a que le atiendan.
-
-
Habrá 3 agentes de recepción para los usuarios registrados y 1 para los no registrados.
-
La distribución será leastrecent, es decir, se llama al dispositivo que hace más tiempo que colgó una llamada de la cola.
-
Serán los encargados de pasar la llamada a los diferentes equipos.
-
-
Los dispositivos se identificarán por su extensión y el nombre asociado al dispositivo se encontrará en la base de datos (asteriskdb).
-
Existirán 4 equipos: recepción (1XX), dirección (2XX), doctores (3XX) y enfermería (4XX) que pueden llamarse. Todos tendrán:
-
Certificado.
-
Buzón de voz, accesible con *[extensión] y como clave su número de identificación.
-
Podrán capturar llamadas dentro de su equipo, excepto dirección que podrán capturar llamadas de cualquier equipo.
-
Podrán llamar a números de fuera del centro.
-
-
Todos los equipos tendrán habilitadas la primera extensión (X00) para la llamada a grupo de todos los componentes del equipo.
-
Podrán enviarse mensajes (no mensajería instantánea).
-
Base de datos [tabla(columnas)]:
- personal(extensión, nombre, equipo, id): personal del centro de salud con su extensión asociada, equipo al que pertenece y su identificación.
-
usuarios(nombre, id): personas que pertenecen al centro de salud.
Aquí están los archivos principales, para ver todos los ficheros 📁aquí
[general]
port=5060
directmedia=no
language=es
context=public ; Default context for incoming calls. Defaults to 'default'
allowoverlap=no ; Disable overlap dialing support. (Default is yes)
udpbindaddr=0.0.0.0 ; IP address to bind UDP listen socket to (0.0.0.0 binds to all)
tcpenable=no ; Enable server for incoming TCP connections (default is no)
tcpbindaddr=0.0.0.0 ; IP address for TCP server to bind to (0.0.0.0 binds to all interfaces)
transport=udp ; Set the default transports. The order determines the primary default transport.
srvlookup=yes ; Enable DNS SRV lookups on outbound calls
tlsenable=yes
tlsbindaddr=0.0.0.0
tlscertfile=/etc/asterisk/keys/asterisk.pem
tlscafile=/etc/asterisk/keys/ca.crt
tlscipher=ALL
tlsclientmethod=tlsv1
;-----Para los mensajes-----
accept_outofcall_message=yes
outofcall_message_context=mensajes
auth_message_requests=yes
subscribecontext=suscribir
;-----Recepcion-----
[101]
type=friend
secret=100101
context=recepcion
host=dynamic
canreinvite=no
nat=force_rport,comedia
callgroup=1
pickupgroup=1
transport=tls
[102]
type=friend
secret=100102
context=recepcion
host=dynamic
canreinvite=no
nat=force_rport,comedia
callgroup=1
pickupgroup=1
transport=tls
[103]
type=friend
secret=100103
context=recepcion
host=dynamic
canreinvite=no
nat=force_rport,comedia
callgroup=1
pickupgroup=1
transport=tls
[110]
type=friend
secret=100110
context=recepcion
host=dynamic
canreinvite=no
nat=force_rport,comedia
callgroup=1
pickupgroup=1
transport=tls
;-----Direccion-----
[201]
type=friend
secret=200201
context=direccion
host=dynamic
canreinvite=no
nat=force_rport,comedia
callgroup=2
pickupgroup=1-4
transport=tls
[202]
type=friend
secret=200202
context=direccion
host=dynamic
canreinvite=no
nat=force_rport,comedia
callgroup=2
pickupgroup=1-4
transport=tls
[203]
type=friend
secret=200203
context=direccion
host=dynamic
canreinvite=no
nat=force_rport,comedia
callgroup=2
pickupgroup=1-4
transport=tls
;-----Doctores-----
[301]
type=friend
secret=300301
context=doctores
host=dynamic
canreinvite=no
nat=force_rport,comedia
callgroup=3
pickupgroup=3
transport=tls
[302]
type=friend
secret=300302
context=doctores
host=dynamic
canreinvite=no
nat=force_rport,comedia
callgroup=3
pickupgroup=3
transport=tls
[303]
type=friend
secret=300303
context=doctores
host=dynamic
canreinvite=no
nat=force_rport,comedia
callgroup=3
pickupgroup=3
transport=tls
;-----Enfermeria-----
[401]
type=friend
secret=400401
context=enfermeria
host=dynamic
canreinvite=no
nat=force_rport,comedia
callgroup=4
pickupgroup=4
transport=tls
[402]
type=friend
secret=400402
context=enfermeria
host=dynamic
canreinvite=no
nat=force_rport,comedia
callgroup=4
pickupgroup=4
transport=tls
[403]
type=friend
secret=400403
context=enfermeria
host=dynamic
canreinvite=no
nat=force_rport,comedia
callgroup=4
pickupgroup=4
transport=tls
;-----Exterior-----
[exterior]
type=friend
secret=123456
context=exterior
host=dynamic
canreinvite=no
nat=force_rport,comedia
;-----Para la conexion con otra PBX-----
[priv]
type=peer
context=dundi-priv-local
disallow=all
allow=ilbc
[general]
static=yes
writeprotect=no
clearglobalvars=no
[public]
include => exterior
;####-INI-LLAMADAS-####
[exterior]
;redireccion a la extension marcada
;con horario establecido L-V de 8 a 20h
exten => 757112233,1,GotoIfTime(8:00-20:00,mon-fri,*,*?abierto,1,1)
same => n,Festival(En estos momentos no hay nadie que te pueda atender)
same => n,Festival(Nuestro horario es de 8 a 20 horas de lunes a viernes)
same => n,Hangup()
[abierto]
;el centro de salud esta abierto
exten => 1,1,Answer()
same => n,Festival(Bienvenido al centro de salud)
same => n(loop),Festival(Presione)
same => n,SayNumber(1)
same => n,Festival(si es un usuario registrado o)
same => n,SayNumber(2)
same => n,Festival(en caso contrario)
same => n,Read(opcion,,1,3)
same => n,SayNumber(${opcion})
same => n,GotoIf($["$ {opcion}" = "1"]?10,1)
same => n,GotoIf($["$ {opcion}" = "2"]?20,1)
same => n,Festival(Opcion incorrecta)
same => n,Goto(1,loop)
same => n,Hangup()
;usuario registrado
exten => 10,1(id),Festival(Por favor marque su numero de identificacion o presione)
same => n,SayNumber(9)
same => n,Festival(para volver al menu)
same => n,Read(id,,4,3,5)
same => n,SayNumber(${id})
same => n,GotoIf($["$ {id}" = "9"]?1,loop)
same => n,Set(nombre=${ODBC_usuario(${id})})
same => n,GotoIf($["$ {nombre}" != ""]?recepcion,1,1)
same => n,Festival(Usario no encontrado)
same => n,Goto(10,id)
same => n,Hangup()
;usuario sin registrar
exten => 20,1,Goto(recepcion,2,1)
same => n,Hangup()
[recepcion]
;llamada a grupo
exten => _X00,1,Festival(Llamada a grupo ${EXTEN})
same => n,Goto(llamar-grupo,${EXTEN},1)
same => n,Hangup()
;usuarios registrados
exten => 1,1,Set(CHANNEL(musicclass)=recepcion)
same => n,Answer()
same => n,Festival(Hola ${nombre})
same => n(reg),Festival(Por favor espere a que le atiendan)
same => n,Queue(registrados,,,,60)
same => n,Goto(1,reg)
same => n,Hangup()
;usuarios no registrados
exten => 2,1,Set(CHANNEL(musicclass)=recepcion)
same => n,Answer()
same => n(noreg),Festival(Por favor espere a que le atiendan)
same => n,Queue(noregistrados,,,,60)
same => n,Goto(2,noreg)
same => n,Hangup()
;llamada a otra extension
exten => _XXX,1,Set(extension=${EXTEN})
same => n,Set(nombre=${ODBC_extension(${extension})})
same => n,GotoIf($["$ {nombre}" != ""]?llamar,${extension},1)
same => n,Festival(La extension ${extension} no se encuentra disponible)
same => n,Hangup()
;buzon de voz
exten => _*XXX,1,Set(llamante=${CALLERID(num)})
same => n,Goto(buzon-voz,1,1)
same => n,Hangup()
;captura de llamada
exten => _*8.,1,Set(extension=${EXTEN:2})
same => n,Set(pref=${extension:0:1})
same => n,GotoIf($["$ {pref}" = "1"]?llamar,8,1)
same => n,Festival(No tiene permiso para capturar una llamada a la extension ${extension})
same => n,Hangup()
;llamada a exterior
exten => _XXXXXXXXX,1,Goto(llamar,${EXTEN},1))
same => n,Hangup()
;telefono no reconocido
exten => _X!,1,Festival(El telefono o extension)
same => n,SayNumber(${EXTEN})
same => n,Festival(es incorrecto)
same => n,Hangup()
[direccion]
;llamada a grupo
exten => _X00,1,Festival(Llamada a grupo ${EXTEN})
same => n,Goto(llamar-grupo,${EXTEN},1)
same => n,Hangup()
;llamada a otra extension
exten => _XXX,1,Set(extension=${EXTEN})
same => n,Set(nombre=${ODBC_extension(${extension})})
same => n,GotoIf($["$ {nombre}" != ""]?llamar,${extension},1)
same => n,Festival(La extension ${extension} no se encuentra disponible)
same => n,Hangup()
;buzon de voz
exten => _*XXX,1,Set(llamante=${CALLERID(num)})
same => n,Goto(buzon-voz,1,1)
same => n,Hangup()
;captura de llamada
exten => _*8.,1,Set(extension=${EXTEN:2})
same => n,Goto(llamar,8,1)
same => n,Hangup()
;llamada a exterior
exten => _XXXXXXXXX,1,Goto(llamar,${EXTEN},1))
same => n,Hangup()
;telefono no reconocido
exten => _X!,1,Festival(El telefono o extension)
same => n,SayNumber(${EXTEN})
same => n,Festival(es incorrecto)
same => n,Hangup()
[doctores]
;llamada a grupo
exten => _X00,1,Festival(Llamada a grupo ${EXTEN})
same => n,Goto(llamar-grupo,${EXTEN},1)
same => n,Hangup()
;llamada a otra extension
exten => _XXX,1,Set(extension=${EXTEN})
same => n,Set(nombre=${ODBC_extension(${extension})})
same => n,GotoIf($["$ {nombre}" != ""]?llamar,${extension},1)
same => n,Festival(La extension ${extension} no se encuentra disponible)
same => n,Hangup()
;buzon de voz
exten => _*XXX,1,Set(llamante=${CALLERID(num)})
same => n,Goto(buzon-voz,1,1)
same => n,Hangup()
;captura de llamada
exten => _*8.,1,Set(extension=${EXTEN:2})
same => n,Set(pref=${extension:0:1})
same => n,GotoIf($["$ {pref}" = "3"]?llamar,8,1)
same => n,Festival(No tiene permiso para capturar una llamada a la extension ${extension})
same => n,Hangup()
;llamada a exterior
exten => _XXXXXXXXX,1,Goto(llamar,${EXTEN},1))
same => n,Hangup()
;telefono no reconocido
exten => _X!,1,Festival(El telefono o extension)
same => n,SayNumber(${EXTEN})
same => n,Festival(es incorrecto)
same => n,Hangup()
[enfermeria]
;llamada a grupo
exten => _X00,1,Festival(Llamada a grupo ${EXTEN})
same => n,Goto(llamar-grupo,${EXTEN},1)
same => n,Hangup()
;llamada a otra extension
exten => _XXX,1,Set(extension=${EXTEN})
same => n,Set(nombre=${ODBC_extension(${extension})})
same => n,GotoIf($["$ {nombre}" != ""]?llamar,${extension},1)
same => n,Festival(La extension ${extension} no se encuentra disponible)
same => n,Hangup()
;buzon de voz
exten => _*XXX,1,Set(llamante=${CALLERID(num)})
same => n,Goto(buzon-voz,1,1)
same => n,Hangup()
;captura de llamada
exten => _*8.,1,Set(extension=${EXTEN:2})
same => n,Set(pref=${extension:0:1})
same => n,GotoIf($["$ {pref}" = "4"]?llamar,8,1)
same => n,Festival(No tiene permiso para capturar una llamada a la extension ${extension})
same => n,Hangup()
;llamada a exterior
exten => _XXXXXXXXX,1,Goto(llamar,${EXTEN},1))
same => n,Hangup()
;telefono no reconocido
exten => _X!,1,Festival(El telefono o extension)
same => n,SayNumber(${EXTEN})
same => n,Festival(es incorrecto)
same => n,Hangup()
[llamar]
;llamada a otra extension
exten => _XXX,1,Festival(Llamando a ${nombre})
same => n,Dial(SIP/${extension},15)
same => n,VoiceMail(${extension})
same => n,Hangup()
;captura de llamada
exten => 8,1,Festival(Capturando llamada a la extension ${extension})
same => n,Set(equipo=${ODBC_equipo(${extension})})
same => n,Pickup(${extension}@${equipo})
same => n,Hangup()
;conexion DUNDi
exten => _XXXXXXXXX,1,Macro(dundi-priv,${EXTEN:5})
same => n,Hangup()
[llamar-grupo]
;recepcion
exten => 100,1,Dial(SIP/101&SIP/102&SIP/103&SIP/110,60))
same => n,Hangup()
;;direccion
exten => 200,1,Dial(SIP/201&SIP/202&SIP/203,60)))
same => n,Hangup()
;;doctores
exten => 300,1,Dial(SIP/301&SIP/302&SIP/303,60)))
same => n,Hangup()
;enfermeros
exten => 400,1,Dial(SIP/401&SIP/402&SIP/403,60)))
same => n,Hangup()
;otra
exten => s,1,Festival(Grupo ${extension} no se encuentra disponible)
same => n,Hangup()
[buzon-voz]
exten => 1,1,Festival(Para acceder a su buzon de voz)
same => n(pin),Festival(Marque su numero de identificacion privado)
same => n,Read(pin,,4,3,5)
same => n,Set(idpin=${ODBC_pin(${llamante})})
same => n,GotoIf($["$ {pin}" = "${idpin}"]?2,1)
same => n,Festival(Numero de identificacion erroneo)
same => n,Goto(1,pin)
same => n,Hangup()
exten => 2,1,Festival(Marque 1 para continuar)
same => n,VoiceMailMain(${llamante})
same => n,Hangup()
;####-FIN-LLAMADAS-####
;####-INI-MENSAJES-####
[suscribir]
exten => 101,hint,SIP/101
exten => 102,hint,SIP/102
exten => 103,hint,SIP/103
exten => 110,hint,SIP/110
exten => 201,hint,SIP/201
exten => 202,hint,SIP/202
exten => 203,hint,SIP/203
exten => 301,hint,SIP/301
exten => 302,hint,SIP/302
exten => 303,hint,SIP/303
exten => 401,hint,SIP/401
exten => 402,hint,SIP/402
exten => 403,hint,SIP/403
[mensajes]
exten => _X.,1,Set(dest=${EXTEN})
same => n,Set(remitente=${CUT(MESSAGE(from),<,2)})
same => n,Set(remitente=${CUT(remitente,@,1)})
same => n,Set(remitente=${CUT(remitente,:,2)})
same => n,Set(texto=${MESSAGE(body)})
same => n,Set(MESSAGE(body)=${remitente}: ${texto})
same => n,Set(nombre=${ODBC_extension(${remitente})})
same => n,Set(nombre=${IF($[ "$ {nombre}" = ""]?desconocido)})
same => n,MessageSend(sip:${dest}, Centro de Mensajes(CdM))
same => n,Noop(Estado del mensaje ${MESSAGE_SEND_STATUS})
same => n,GotoIf($["$ {MESSAGE_SEND_STATUS}" != "SUCCESS"]?fallo,s,1)
same => n,Hangup()
[fallo]
exten => s,1,Set(MESSAGE(body)=CdM: El mensaje "${texto}" para ${dest} no ha sido enviado)
same => n,Set(remit=${CUT(MESSAGE(from),<,2)})
same => n,Set(remit=${CUT(remit,@,1)})
same => n,MessageSend(${remit},Centro de Mensajes(CdM))
same => n,NoOp(Estado del mensaje ${MESSAGE_SEND_STATUS})
same => n,Hangup()
;####-FIN-MENSAJES-####
;####-INI-DUNDI-####
[dundi-priv-canonical]
exten => _XXXXXXXXX,1,Dial(Zap/g1/${EXTEN:6},20,rtT)
[dundi-priv-customers]
[dundi-priv-via-pstn]
[dundi-priv-local]
include => dundi-priv-canonical
include => dundi-priv-customers
include => dundi-priv-via-pstn
[dundi-priv-switch]
switch => DUNDi/priv
[dundi-priv-lookup]
include => dundi-priv-local
include => dundi-priv-switch
[macro-dundi-priv]
exten => s,1,NoOp(Macro dundi-prinv de PBX180)
same => n,Goto(${ARG1},1)
include => dundi-priv-lookup
;####-FIN-DUNDI-####
[general]
persistentmembers = yes
monitor-type = MixMonitor
[registrados]
strategy = leastrecent
timeout = 30
member => SIP/101
member => SIP/102
member => SIP/103
[noregistrados]
strategy = leastrecent
timeout = 30
member => SIP/110
[general]
[SQL]
dsn=mysql1
readsql=${ARG1}
[ANTIGF]
dsn=mysql1,mysql2 ; Use mysql1 as the primary handle, but fall back to mysql2
readsql=SELECT COUNT(*) FROM exgirlfriends WHERE callerid='${SQL_ESC(${ARG1})}'
syntax=<callerid>
synopsis=Check if a specified callerid is contained in the ex-gf database
[PRESENCE]
dsn=mysql1
readsql=SELECT location FROM presence WHERE id='${SQL_ESC(${ARG1})}'
writesql=UPDATE presence SET location='${SQL_ESC(${VAL1})}' WHERE id='${SQL_ESC(${ARG1})}'
[info]
dsn=asterisk
readsql=SELECT CONCAT(nombre,' con extension ', extension) from usuarios WHERE nombre='${SQL_ESC(${ARG1})}'
[usuario]
dsn=asterisk
readsql=SELECT nombre from usuarios WHERE id='${SQL_ESC(${ARG1})}'
[extension]
dsn=asterisk
readsql=SELECT nombre from personal where extension='${SQL_ESC(${ARG1})}'
[pin]
dsn=asterisk
readsql=SELECT id from personal where extension='${SQL_ESC(${ARG1})}'
[equipo]
dsn=asterisk
readsql=SELECT lower(equipo) from personal where extension='${SQL_ESC(${ARG1})}'
[general]
format=wav49|gsm|wav
serveremail=asterisk
attach=yes
skipms=3000
maxsilence=10
silencethreshold=128
maxlogins=3
emaildateformat=%A, %B %d, %Y at %r
pagerdateformat=%A, %B %d, %Y at %r
sendvoicemail=yes ; Allow the user to compose and send a voicemail while inside
[zonemessages]
eastern=America/New_York|'vm-received' Q 'digits/at' IMp
central=America/Chicago|'vm-received' Q 'digits/at' IMp
central24=America/Chicago|'vm-received' q 'digits/at' H N 'hours'
military=Zulu|'vm-received' q 'digits/at' H N 'hours' 'phonetic/z_p'
european=Europe/Copenhagen|'vm-received' a d b 'digits/at' HM
[default]
;;para entrar se verificara en el dialplan y la clave
;coincidira con el id de la tabla personal
101 => 1,101,[email protected]
102 => 1,102,[email protected]
103 => 1,103,[email protected]
110 => 1,110,[email protected]
201 => 1,201,[email protected]
202 => 1,202,[email protected]
203 => 1,203,[email protected]
301 => 1,301,[email protected]
302 => 1,302,[email protected]
303 => 1,303,[email protected]
401 => 1,401,[email protected]
402 => 1,402,[email protected]
403 => 1,403,[email protected]
CREATE TABLE personal(extension INTEGER NOT NULL, nombre VARCHAR(20) NOT NULL, equipo VARCHAR(10) NOT NULL, id INTEGER NOT NULL, PRIMARY KEY (id));
CREATE TABLE usuarios(nombre VARCHAR(20) NOT NULL, id INTEGER NOT NULL, PRIMARY KEY (id));
insert into personal values(101, "MARIA", "RECEPCION", 1001);
insert into personal values(102, "IGNACIO", "RECEPCION", 1002);
insert into personal values(103, "MANUEL", "RECEPCION", 1003);
insert into personal values(110, "CRISTINA", "RECEPCION", 1004);
insert into personal values(201, "BELEN", "DIRECCION", 2001);
insert into personal values(202, "JAVIER", "DIRECCION",2002);
insert into personal values(203, "OSCAR", "DIRECCION", 2003);
insert into personal values(301, "LAURA", "DOCTORES", 3001);
insert into personal values(302, "ANA", "DOCTORES", 3002);
insert into personal values(303, "JORGE", "DOCTORES", 3003);
insert into personal values(401, "ISABEL", "ENFERMERIA", 4001);
insert into personal values(402, "ANTONIO", "ENFERMERIA", 4002);
insert into personal values(403, "RAQUEL", "ENFERMERIA", 4003);
insert into usuarios values("JULIA", 50);
insert into usuarios values("MARIO", 51);
insert into usuarios values("PAULA", 52);
insert into usuarios values("NATIVIDAD", 53);
insert into usuarios values("MARIA JESUS", 54);
insert into usuarios values("MANUEL", 55);
insert into usuarios values("JUAN", 56);
insert into usuarios values("CARLOS", 57);
insert into usuarios values("GRACIA", 58);
insert into usuarios values("AMELIA", 59);
GRANT SELECT, INSERT, UPDATE, DELETE ON asteriskdb.* TO 'asterisk'@'localhost';
Puedes ver la bibliografía 📚 aquí.
Cuando te aparece este error al lazar la consola de Asterisk, lo más probable es que se deba a problemas de permisos con el usuario asterisk. Para solucionarlo:
$ sudo chown -R asterisk: /var/{lib,log,run,spool}/asterisk /usr/lib/asterisk /etc/asterisk
$ sudo chmod -R 750 /var/{lib,log,run,spool}/asterisk /usr/lib/asterisk /etc/asterisk
Y reiniciar el sistemas operativo.
$ sudo reboot
Si con eso no funcionara, podemos lanzarlo manualmente con:
$ sudo asterisk -&
Un softphone (combinación de software y telephone) es un software utilizado para comunicaciones VoIP, ya sea entre softphones o a un hardphones (teléfonos físicos para comunicaciones VoIP). Vienen a ser muy útiles porque podemos probar en local, sin necesidad de hardware adicional (que suelen tener un precio elevado), las funcionalidad de la PBX. Aunque hay infinidad de ellos, yo he probado los siguiente:
-
Pangolin: disponible para Windows, tiene una desventaja clave y es la inestabilidad. Es gratuito.
-
ZoiPer: disponible para todo tipo de plataformas, ya sean de escritorio o de smartphone, es bastante estable y fácil de utilizar. Es gratuito para realizar llamadas, pero otras funcionalidades más avanzadas son de pago.
-
3CX: disponible solo para Windows, es rápido, estable, sencillo y cómodo de usar. Simula uno de los primeros iPhone. Es gratuito.
-
Blink: disponible para Windows, es cómodo de usar sobre todo cuando tienes que utilizar certificados. Es gratuito.
-
Sipnetic: disponible para Android, me ha parecido el cliente SIP más cómodo de usar en el smartphone. Es gratuito para realizar llamadas y enviar mensajes, pero otras funcionalidades más avanzadas son de pago.
-
MizuDroid: disponible para Android, es útil cuando tienes que enviar mensajes ya que lo implementa
Como en mi tierra dicen es de bien nacidos ser agradecidos, aquí están todas los recursos gracias a los cuales he podido desarrollar todo, punto por punto:
-
-
1. Rackspace
2. Snel
-
3. phoenixNAP
-
-
-
7. pascom GmbH & Co. KG (YouTube)
-
10. minestron
-
11. VozToVoice
-
12. pascom
-
13. VozToVoice
-
15. Tim Leland
-
17. Wiki Asterisk
-
20. pascom GmbH & Co. KG (YouTube)
21. Stack Overflow
-
22. pascom GmbH & Co. KG (YouTube)
23. Asterisk Wiki
-
-
-
24. TecAdmin
25. Robbi Nespu
26. Rackspace
-
27. Dev MySQL
-
28. OpenWebinars
-
29. OpenWebinars
-
-
-
30. VozToVoice
31. ElastixTech
-
-
-
32. VOIP-Info
33. Asterisk Wiki
-