Asterisk 和 Bitrix24 的集成

Asterisk 和 Bitrix24 的集成
在网络上集成 IP-PBX Asterisk 和 CRM Bitrix24 有不同的选项,但我们仍然决定编写自己的选项。

就功能而言,一切都是标准的:

  • 通过点击 Bitrix24 中客户电话号码的链接,Asterisk 会将代表其进行点击的用户的内部号码与客户的电话号码连接起来。 在 Bitrix24 中,会记录通话记录,并在通话结束时调出对话录音。
  • Asterisk 接到来自外部的电话 - 在 Bitrix24 界面中,我们向接到此电话的号码的员工出示客户卡。
    如果没有这样的客户,我们将开卡创建新的潜在客户。
    通话结束后,我们会将其反映在卡片上并调出对话录音。

在剪辑下方,我将告诉您如何为自己设置一切,并为您提供 github 链接 - 是的,是的,接受并使用它!

一般说明

我们将我们的集成称为 CallMe。 CallMe 是一个用 PHP 编写的小型 Web 应用程序。

使用的技术和服务

  • PHP 5.6的
  • PHP AMI 库
  • 作曲家
  • Nginx + php-fpm
  • AMI(Asterisk 管理器界面)
  • Bitrix webhooks(简化的 REST API 实现)

预设

在带有 Asterisk 的服务器上,您需要安装 Web 服务器(对我们来说是 nginx+php-fpm)、supervisor 和 git。

安装命令(CentOS):

yum install nginx php-fpm supervisor git

我们转到 Web 服务器可访问的目录,从 Git 中提取应用程序并为该文件夹设置必要的权限:


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

接下来,让我们配置nginx,我们的配置位于

/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;
		}
}

我将把解析配置、安全问题、获取证书,甚至选择一个 Web 服务器超出了本文的范围 - 关于这一点已经有很多文章了。 该应用程序没有任何限制,它可以通过 http 和 https 运行。

我们使用https,让我们加密证书。

如果您所做的一切正确,那么通过单击链接您应该看到类似这样的内容

Asterisk 和 Bitrix24 的集成

配置Bitrix24

让我们创建两个 webhook。

传入的网络钩子。

在管理员帐户(id 1)下,遵循以下路径:应用程序 -> Webhooks -> 添加 webhook -> 传入 webhook

Asterisk 和 Bitrix24 的集成

填写传入 webhook 的参数,如屏幕截图所示:

Asterisk 和 Bitrix24 的集成

Asterisk 和 Bitrix24 的集成

然后点击保存。

保存后,Bitrix24 将提供传入 webhook 的 URL,例如:

Asterisk 和 Bitrix24 的集成

保存您的 URL 版本,不带最终的 /profile/ - 它将在应用程序中用于处理来电。

我有这个 https://b24-xsynia.bitrix24.ru/rest/1/7eh61lh8pahw0fwt/

传出网络钩子。

应用程序 -> Webhooks -> 添加 webhook -> 传出 webhook

详细信息再次显示在屏幕截图上:

Asterisk 和 Bitrix24 的集成

Asterisk 和 Bitrix24 的集成

保存并接收授权码

Asterisk 和 Bitrix24 的集成

我有这个 xcrp2ylhzzd2v43cmfjqmkvrgrcbkni6。 您还需要自己复制它;您需要它来拨打电话。

重要的信息!

必须在 Bitrix24 服务器上配置 SSL 证书(可以使用 LetsEncrypt),否则 Bitrix api 将无法工作。 如果您有云版本,请不用担心 - 它已经具有 ssl。

重要的信息!

“处理器地址”字段必须包含可从互联网访问的地址!

作为最后一步,让我们安装 CallMeOut 作为拨打电话的应用程序(这样当您单击 PBX 上的号码时,发起呼叫的命令就会飞走)。

在菜单中选择:更多->电话->更多->设置,在“默认去电号码”中设置应用程序:CallMeOut,然后点击“保存”

Asterisk 和 Bitrix24 的集成

设置星号

为了Asterisk和Bitrix24之间的成功交互,我们需要将AMI用户callme添加到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

接下来,有几个技巧需要通过 dialplan 来实现(对于我们来说,这是 extensions.ael)。

我将提供整个文件,然后给出解释:

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});
}

};

让我们从头开始:指令 全局.

变量 网址记录 存储对话录音文件的 URL,Bitrix24 根据该 URL 将它们拉入联系人卡片。

接下来我们感兴趣的是宏宏 了解.

在这里,除了记录对话之外,我们还将设置变量 全名.

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

它将完整的 URL 存储到特定文件(到处都会调用宏)。

我们来分析一下呼出:

_. => {
	&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});
}

假设我们拨打 89991234567,首先我们到达这里:

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

那些。 调用对话记录宏并设置必要的变量。

下一步

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

我们记录谁发起了呼叫并记录了呼叫的开始时间。

完成后,在特殊的背景下 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});
}

禁用此分机的 CDR 表条目(那里不需要),设置呼叫的结束时间,计算持续时间,如果呼叫结果未知 - set (variable 打电话给我处置),最后一步,通过系统curl将所有内容发送到Bitrix。

还有一点魔力——来电:

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();
        }

这里我们只对一行感兴趣。

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

她告诉 PBX 安装 来电显示(姓名) 等于变量 CallMe来电显示姓名.

CallMeCallerIDName 变量本身由 CallMe 应用程序设置(如果 Bitrix24 有主叫号码的全名,请将其设置为 来电显示(姓名),不 - 我们不会做任何事情)。

应用程序设置

应用程序设置文件 - /var/www/pbx.vistep.ru/config.php

应用参数说明:

  • 呼叫我调试 — 如果为 1,则应用程序处理的所有事件都将写入日志文件,0 — 我们不写入任何内容
  • 科技 - SIP/PJSIP/IAX/等
  • 授权令牌 — Bitrix24 授权令牌,传出 webhook 授权代码
  • bitrixApiUrl — 传入 webhook 的 URL,没有配置文件/
  • 扩展 — 外部号码列表
  • 上下文 — 发起呼叫的上下文
  • 监听器超时 — 星号的事件处理速度
  • 星号 — 具有连接到星号的设置的数组:
  • 主持人 — 星号服务器的 IP 或主机名
  • 方案 — 连接图(tcp://、tls://)
  • 端口 - 港口
  • 用户名 - 用户名
  • 秘密 - 密码
  • 连接超时 - 连接超时
  • 读取超时 - 读取超时

设置文件示例:

 <?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

);

主管设置

Supervisor 用于从 Asterisk CallMeIn.php 启动事件处理程序进程,该进程监视来电并与 Bitrix24 交互(显示卡、隐藏卡等)。

要创建的设置文件:

/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

启动并重新启动应用程序:

supervisorctl start callme
supervisorctl restart callme

查看应用程序的运行状态:

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

结论

事实证明它相当复杂,但我相信经验丰富的管理员将能够实现它并取悦他的用户。

按照承诺, 链接到 github.

问题、建议 - 请在评论中留下。 另外,如果您对这种集成的开发过程感兴趣,请写下来,在下一篇文章中我将尝试更详细地揭示所有内容。

来源: habr.com

添加评论