Integración de Asterisk y Bitrix24

Integración de Asterisk y Bitrix24
Hay diferentes opciones para integrar IP-PBX Asterisk y CRM Bitrix24 en la red, pero aun así decidimos escribir la nuestra.

En cuanto a funcionalidad, todo es estándar:

  • Al hacer clic en un enlace con el número de teléfono de un cliente en Bitrix24, Asterisk conecta el número interno del usuario en cuyo nombre se realizó el clic con el número de teléfono del cliente. En Bitrix24, se graba una grabación de la llamada y al final de la llamada, se abre una grabación de la conversación.
  • Asterisk recibe una llamada del exterior: en la interfaz de Bitrix24 mostramos la tarjeta del cliente al empleado a cuyo número llegó esta llamada.
    Si no existe tal cliente, abriremos la tarjeta para crear un nuevo cliente potencial.
    Tan pronto como se completa la llamada, lo reflejamos en la tarjeta y obtenemos una grabación de la conversación.

Debajo del corte te diré cómo configurar todo tú mismo y te daré un enlace a github. Sí, sí, ¡tómalo y úsalo!

descripción general

Llamamos a nuestra integración CallMe. CallMe es una pequeña aplicación web escrita en PHP.

Tecnologías y servicios utilizados.

  • PHP 5.6
  • Biblioteca PHP AMI
  • Compositor
  • Nginx + php-fpm
  • supervisor
  • AMI (interfaz de administrador de asterisco)
  • Webhooks Bitrix (implementación de API REST simplificada)

Preajuste

En un servidor con Asterisk, necesita instalar un servidor web (para nosotros es nginx+php-fpm), supervisor y git.

Comando de instalación (CentOS):

yum install nginx php-fpm supervisor git

Vamos al directorio accesible para el servidor web, extraemos la aplicación de Git y configuramos los derechos necesarios para la carpeta:


cd /var/www
git clone https://github.com/ViStepRU/callme.git
chown nginx. -R callme/

A continuación, configuremos nginx, nuestra configuración se encuentra en

/etc/nginx/conf.d/pbx.vistep.ru.conf

server {
	server_name www.pbx.vistep.ru pbx.vistep.ru;
	listen *:80;
	rewrite ^  https://pbx.vistep.ru$request_uri? permanent;
}

server {
#        listen *:80;
#	server_name pbx.vistep.ru;


	access_log /var/log/nginx/pbx.vistep.ru.access.log main;
        error_log /var/log/nginx/pbx.vistep.ru.error.log;

    listen 443 ssl http2;
    server_name pbx.vistep.ru;
    resolver 8.8.8.8;
    ssl_stapling on;
    ssl on;
    ssl_certificate /etc/letsencrypt/live/pbx.vistep.ru/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/pbx.vistep.ru/privkey.pem;
    ssl_dhparam /etc/nginx/certs/dhparam.pem;
    ssl_session_timeout 24h;
    ssl_session_cache shared:SSL:2m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers kEECDH+AES128:kEECDH:kEDH:-3DES:kRSA+AES128:kEDH+3DES:DES-CBC3-SHA:!RC4:!aNULL:!eNULL:!MD5:!EXPORT:!LOW:!SEED:!CAMELLIA:!IDEA:!PSK:!SRP:!SSLv2;
    ssl_prefer_server_ciphers on;
    add_header Strict-Transport-Security "max-age=31536000;";
    add_header Content-Security-Policy-Report-Only "default-src https:; script-src https: 'unsafe-eval' 'unsafe-inline'; style-src https: 'unsafe-inline'; img-src https: data:; font-src https: data:; report-uri /csp-report";
	
	root /var/www/callme;
	index  index.php;
        location ~ /. {
                deny all; # запрет для скрытых файлов
        }

        location ~* /(?:uploads|files)/.*.php$ {
                deny all; # запрет для загруженных скриптов
        }

        location ~* ^.+.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
                access_log off;
                log_not_found off;
                expires max; # кеширование статики
        }

	location ~ .php {
		root /var/www/callme;
		index  index.php;
		fastcgi_pass unix:/run/php/php5.6-fpm.sock;
	#	fastcgi_pass 127.0.0.1:9000;
		fastcgi_index index.php;
		fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
		include /etc/nginx/fastcgi_params;
		}
}

Dejaré el análisis de la configuración, los problemas de seguridad, la obtención de un certificado e incluso la elección de un servidor web fuera del alcance del artículo; se ha escrito mucho sobre esto. La aplicación no tiene restricciones, funciona tanto en http como en https.

Usamos https, cifremos el certificado.

Si hizo todo correctamente, al hacer clic en el enlace debería ver algo como esto

Integración de Asterisk y Bitrix24

Configurando Bitrix24

Creemos dos webhooks.

Webhook entrante.

En la cuenta de administrador (con ID 1), siga la ruta: Aplicaciones -> Webhooks -> Agregar webhook -> Webhook entrante

Integración de Asterisk y Bitrix24

Complete los parámetros del webhook entrante como en las capturas de pantalla:

Integración de Asterisk y Bitrix24

Integración de Asterisk y Bitrix24

Y haga clic en guardar.

Después de guardar, Bitrix24 proporcionará la URL del webhook entrante, por ejemplo:

Integración de Asterisk y Bitrix24

Guarde su versión de la URL sin el /perfil/ final; se utilizará en la aplicación para trabajar con las llamadas entrantes.

tengo esto https://b24-xsynia.bitrix24.ru/rest/1/7eh61lh8pahw0fwt/

Webhook saliente.

Aplicaciones -> Webhooks -> Agregar webhook -> Webhook saliente

Los detalles están nuevamente en las capturas de pantalla:

Integración de Asterisk y Bitrix24

Integración de Asterisk y Bitrix24

Guardar y recibir el código de autorización

Integración de Asterisk y Bitrix24

tengo esto xcrp2ylhzzd2v43cmfjqmkvrgrcbkni6. También necesita copiarlo usted mismo; lo necesita para realizar llamadas salientes.

¡Importante!

Se debe configurar un certificado SSL en el servidor Bitrix24 (puede usar letsencrypt); de lo contrario, la API de Bitrix no funcionará. Si tienes una versión en la nube, no te preocupes: ya tiene SSL.

¡Importante!

¡El campo "Dirección del procesador" debe contener una dirección accesible desde Internet!

Y como toque final, instalemos nuestro CallMeOut como aplicación para realizar llamadas (de modo que al pulsar sobre el número en la centralita, el comando para realizar la llamada volará).

En el menú, seleccione: Más -> Telefonía -> Más -> Configuración, configure en “Número de llamada saliente predeterminado” Aplicación: CallMeOut y haga clic en “Guardar”

Integración de Asterisk y Bitrix24

Configurando asterisco

Para una interacción exitosa entre Asterisk y Bitrix24, necesitamos agregar el usuario AMI callme a manager.conf:

[callme]
secret = JD3clEB8_f23r-3ry84gJ
deny = 0.0.0.0/0.0.0.0
permit = 127.0.0.1/255.255.255.0
permit= 10.100.111.249/255.255.255.255
permit = 192.168.254.0/255.255.255.0
read = system,call,log,verbose,agent,user,config,dtmf,reporting,cdr,dialplan
write = system,call,agent,log,verbose,user,config,command,reporting,originate

A continuación, hay varios trucos que deberán implementarse mediante dialplan (para nosotros esto es extensions.ael).

Proporcionaré el archivo completo y luego daré una explicación:

globals {
    WAV=/var/www/pbx.vistep.ru/callme/records/wav; //Временный каталог с WAV
    MP3=/var/www/pbx.vistep.ru/callme/records/mp3; //Куда выгружать mp3 файлы
    URLRECORDS=https://pbx.vistep.ru/callme/records/mp3;
    RECORDING=1; // Запись, 1 - включена.
};

macro recording(calling,called) {
        if ("${RECORDING}" = "1"){
              Set(fname=${UNIQUEID}-${STRFTIME(${EPOCH},,%Y-%m-%d-%H_%M)}-${calling}-${called});
	      Set(datedir=${STRFTIME(${EPOCH},,%Y/%m/%d)});
	      System(mkdir -p ${MP3}/${datedir});
	      System(mkdir -p ${WAV}/${datedir});
              Set(monopt=nice -n 19 /usr/bin/lame -b 32  --silent "${WAV}/${datedir}/${fname}.wav"  "${MP3}/${datedir}/${fname}.mp3" && rm -f "${WAV}/${fname}.wav" && chmod o+r "${MP3}/${datedir}/${fname}.mp3");
	      Set(FullFname=${URLRECORDS}/${datedir}/${fname}.mp3);
              Set(CDR(filename)=${fname}.mp3);
	      Set(CDR(recordingfile)=${fname}.wav);
              Set(CDR(realdst)=${called});
              MixMonitor(${WAV}/${datedir}/${fname}.wav,b,${monopt});

       };
};


context incoming {
888999 => {
	&recording(${CALLERID(number)},${EXTEN});
        Answer();
        ExecIF(${CallMeCallerIDName}?Set(CALLERID(name)=${CallMeCallerIDName}):NoOp()); // выставляем CallerID если узнали его у Битрикс24
        Set(CallStart=${STRFTIME(epoch,,%s)});  
        Queue(Q1,tT);
        Set(CallMeDISPOSITION=${CDR(disposition)}); 
        Hangup();
        }

h => {
    Set(CDR_PROP(disable)=true); 
    Set(CallStop=${STRFTIME(epoch,,%s)}); 
    Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)}); 
    ExecIF(${ISNULL(${CallMeDISPOSITION})}?Set(CallMeDISPOSITION=${CDR(disposition)}):NoOP(=== CallMeDISPOSITION already was set ===));  
}

}


context default {

_X. => {
        Hangup();
        }
};


context dial_out {

_[1237]XX => {
	&recording(${CALLERID(number)},${EXTEN});
        Set(__CallIntNum=${CALLERID(num)})
	Set(CallStart=${STRFTIME(epoch,,%s)});
        Dial(SIP/${EXTEN},,tTr);
        Hangup();
        }

_11XXX => {
	&recording(${CALLERID(number)},${EXTEN});
	Set(CallStart=${STRFTIME(epoch,,%s)});
	Set(__CallIntNum=${CALLERID(num)});
        Dial(SIP/${EXTEN:2}@toOurAster,,t);
        Hangup();
        }

_. => {
	&recording(${CALLERID(number)},${EXTEN});
        Set(__CallIntNum=${CALLERID(num)})
	Set(CallStart=${STRFTIME(epoch,,%s)});
	Dial(SIP/${EXTEN}@toOurAster,,t);
	Hangup();
        }

h => {
        Set(CDR_PROP(disable)=true);
        Set(CallStop=${STRFTIME(epoch,,%s)});
        Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)});
	if(${ISNULL(${CallMeDISPOSITION})}) {
          Set(CallMeDISPOSITION=${CDR(disposition)});
        }
	System(curl -s http://pbx.vistep.ru/CallMeOut.php --data action=sendcall2b24 --data call_id=${CallMeCALL_ID} --data-urlencode FullFname=${FullFname} --data CallIntNum=${CallIntNum} --data CallDuration=${CallMeDURATION} --data-urlencode CallDisposition=${CallMeDISPOSITION});
}

};

Empecemos por el principio: la directiva globales.

Variable URLRECORDS almacena la URL de los archivos de grabación de la conversación, según la cual Bitrix24 los introducirá en la tarjeta de contacto.

A continuación nos interesa la macro macro. vea la grabación.

Aquí además de grabar conversaciones estableceremos la variable Nombre completo.

Set(FullFname=${URLRECORDS}/${datedir}/${fname}.mp3);

Almacena la URL completa en un archivo específico (la macro se llama en todas partes).

Analicemos la llamada saliente:

_. => {
	&recording(${CALLERID(number)},${EXTEN});
        Set(__CallIntNum=${CALLERID(num)})
	Set(CallStart=${STRFTIME(epoch,,%s)});
	Dial(SIP/${EXTEN}@toOurAster,,t);
	Hangup();
        }

h => {
        Set(CDR_PROP(disable)=true);
        Set(CallStop=${STRFTIME(epoch,,%s)});
        Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)});
	if(${ISNULL(${CallMeDISPOSITION})}) {
          Set(CallMeDISPOSITION=${CDR(disposition)});
        }
	System(curl -s http://pbx.vistep.ru/CallMeOut.php --data action=sendcall2b24 --data call_id=${CallMeCALL_ID} --data-urlencode FullFname=${FullFname} --data CallIntNum=${CallIntNum} --data CallDuration=${CallMeDURATION} --data-urlencode CallDisposition=${CallMeDISPOSITION});
}

Digamos que llamamos al 89991234567, primero que nada llegamos aquí:

&recording(${CALLERID(number)},${EXTEN});

aquellos. Se llama a la macro de grabación de conversaciones y se configuran las variables necesarias.

Siguiente

        Set(__CallIntNum=${CALLERID(num)})
	Set(CallStart=${STRFTIME(epoch,,%s)});

Registramos quién inició la llamada y registramos la hora de inicio de la llamada.

Y una vez finalizado, en un contexto especial h

h => {
        Set(CDR_PROP(disable)=true);
        Set(CallStop=${STRFTIME(epoch,,%s)});
        Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)});
	if(${ISNULL(${CallMeDISPOSITION})}) {
          Set(CallMeDISPOSITION=${CDR(disposition)});
        }
	System(curl -s http://pbx.vistep.ru/CallMeOut.php --data action=sendcall2b24 --data call_id=${CallMeCALL_ID} --data-urlencode FullFname=${FullFname} --data CallIntNum=${CallIntNum} --data CallDuration=${CallMeDURATION} --data-urlencode CallDisposition=${CallMeDISPOSITION});
}

deshabilite la entrada a la tabla CDR para esta extensión (no es necesaria allí), establezca la hora de finalización de la llamada, calcule la duración, si no se conoce el resultado de la llamada - configure (variable CallMeDISPOSICIÓN) y, último paso, enviar todo a Bitrix a través del sistema curl.

Y un poco más de magia: una llamada entrante:

888999 => {
	&recording(${CALLERID(number)},${EXTEN});
        Answer();
        ExecIF(${CallMeCallerIDName}?Set(CALLERID(name)=${CallMeCallerIDName}):NoOp()); // выставляем CallerID если узнали его у Битрикс24
        Set(CallStart=${STRFTIME(epoch,,%s)}); // начинаем отсчет времени звонка
        Queue(Q1,tT);
        Set(CallMeDISPOSITION=${CDR(disposition)}); 
        Hangup();
        }

Aquí sólo nos interesa una línea.

ExecIF(${CallMeCallerIDName}?Set(CALLERID(name)=${CallMeCallerIDName}):NoOp());

Ella le dice al PBX que instale Identificador de llamadas (nombre) igual a variable LlámameCallerIDNombre.

La variable CallMeCallerIDName, a su vez, la establece la aplicación CallMe (si Bitrix24 tiene un nombre completo para el número de la persona que llama, configúrelo como Identificador de llamadas (nombre), no, no haremos nada).

Configuración de la aplicación

Archivo de configuración de la aplicación - /var/www/pbx.vistep.ru/config.php

Descripción de los parámetros de la aplicación:

  • LlámameDEBUG — si es 1, entonces todos los eventos procesados ​​por la aplicación se escribirán en el archivo de registro, 0 — no escribimos nada
  • tecnología - SIP/PJSIP/IAX/etc.
  • token de autorización — Token de autorización Bitrix24, código de autorización de webhook saliente
  • bitrizApiUrl — URL del webhook entrante, sin perfil/
  • extensiones — lista de números externos
  • contexto — contexto para originar una llamada
  • tiempo de espera_escucha — velocidad de procesamiento de eventos desde asterisco
  • asterisco — una matriz con configuraciones para conectarse al asterisco:
  • fortaleza — ip o nombre de host del servidor asterisk
  • esquema — diagrama de conexión (tcp://, tls://)
  • Puerto - puerto
  • nombre de usuario - Nombre de usuario
  • secreto - contraseña
  • tiempo de espera de conexión - el tiempo de conexión expiro
  • leer_tiempo de espera - leer el tiempo de espera

archivo de configuración de ejemplo:

 <?php
return array(

        'CallMeDEBUG' => 1, // дебаг сообщения в логе: 1 - пишем, 0 - не пишем
        'tech' => 'SIP',
        'authToken' => 'xcrp2ylhzzd2v43cmfjqmkvrgrcbkni6', //токен авторизации битрикса
        'bitrixApiUrl' => 'https://b24-xsynia.bitrix24.ru/rest/1/7eh61lh8pahw0fwt/', //url к api битрикса (входящий вебхук)
        'extentions' => array('888999'), // список внешних номеров, через запятую
        'context' => 'dial_out', //исходящий контекст для оригинации звонка
        'asterisk' => array( // настройки для подключения к астериску
                    'host' => '10.100.111.249',
                    'scheme' => 'tcp://',
                    'port' => 5038,
                    'username' => 'callme',
                    'secret' => 'JD3clEB8_f23r-3ry84gJ',
                    'connect_timeout' => 10000,
                    'read_timeout' => 10000
                ),
        'listener_timeout' => 300, //скорость обработки событий от asterisk

);

Configuración del supervisor

Supervisor se utiliza para iniciar el proceso de manejo de eventos desde Asterisk CallMeIn.php, que monitorea las llamadas entrantes e interactúa con Bitrix24 (mostrar tarjeta, ocultar tarjeta, etc.).

Archivo de configuración que se creará:

/etc/supervisord.d/callme.conf

[program:callme]
command=/usr/bin/php CallMeIn.php
directory=/var/www/pbx.vistep.ru
autostart=true
autorestart=true
startretries=5
stderr_logfile=/var/www/pbx.vistep.ru/logs/daemon.log
stdout_logfile=/var/www/pbx.vistep.ru/logs/daemon.log

Inicie y reinicie la aplicación:

supervisorctl start callme
supervisorctl restart callme

Visualización del estado operativo de la aplicación:

supervisorctl status callme
callme                           RUNNING   pid 11729, uptime 17 days, 16:58:07

Conclusión

Resultó bastante complicado, pero estoy seguro de que un administrador experimentado podrá implementarlo y complacer a sus usuarios.

Como fue prometido, enlace a github.

Preguntas, sugerencias: déjelas en los comentarios. Además, si te interesa cómo fue el desarrollo de esta integración, escribe, que en el próximo artículo intentaré desvelarte todo con más detalle.

Fuente: habr.com

Añadir un comentario