Интеграция Asterisk (FreePBX) и Битрикс24
Инструкция собрана по крупицам со всего интернета. Т.к. те что имелись были либо неполные, либо неработоспособные.
Автором модуля интеграции FreePBX и Битрикс 24 является Евгений Романенко https://habr.com/ru/users/FessAectan/
Модуль интеграции callme на GitHub https://github.com/ViStepRU/callme
Логика работы SIP-коннектора для Битрикс24
SIP коннектор для Битрикс24 и Asterisk, работает по средствам вебхук. Используются входящие и исходящие вебхуки.
По Входящему вебхуку по средствам REST Битрикс получает от Asterisk'a данные о входящих звонках. Затем всплывает карточка Входящего звонка в Битриксе.
По Исходящему вебхуку, Битрикс обращается к SIP-коннектору и происходит сначала звонок на внутренний номер сотрудника, затем набирается номер клиента.
Исходные данные
Скачана и установлена сборка FreePBX Distro (SNG7-PBX-64bit-1904), дата релиза май 2019г.
В нее входит: FreePBX 14.0.11, CentOS 7.6, Asterisk 13/16, PHP 5.6.40, Python 2.7
В качестве примера: локальный IP АТС 192.168.0.87, + внешний домена pbx.a-ctroy.ru и внешний адрес 37.113.134.80
Телефонный номер 79320111675, он же транк для звонков.
Для удобства работы я использую SHH доступ: к консоли SecureCRT, к файлам WinSCP + редактор NotePad++.
Установка модулей
Первое что сделать, это рекомендую обновить все текущие модули:
yum update
Установим кодировщик WAV-MP3 lame
yum install lame
Устанавливаем PyPi (репозитарий Python’a)
yum install python-pip
Обновляем PyPi
pip install --upgrade pip
Устанавливаем последний (4.0.3) Supervisor из PyPi
pip install supervisor
Установим git
yum install git
Перейдем в папку где будет размещен модуль и скачаем его с GITa
cd /var/www/html
git clone https://github.com/ViStepRU/callme.git
Если проект на гитхабе будет прекращен, либо изменен, прилагаю ссылку с модулем https://smirnov-alexey.ru/callme.zip
wget https://smirnov-alexey.ru/callme.zip -O callme.zip
unzip callme.zip
Теперь наш модуль будет лежать по пути /var/www/html/callme
Если вы успешно его развернули, открываем в браузере http://192.168.0.87/callme и получаем ответ «silence is golden».
Настройка доступа извне к АТС
/ip firewall address-list
add address=bitrix24.ru list=tel
add address=bitrix24.net list=tel
add address=bitrix24.com list=tel
И главное наш/ваш Битрикс24:
add address=a-ctroy.bitrix24.ru list=tel

В версии от 6.26. В качестве IP можно указывать доменное имя.
Вкладка General.
Chain: forward
Protocol: 6(TCP)
Dst. Port: 80
In. Interface: <Здесь указываем WAN интерфейс>, (в моем случае ether1-gateway)
Вкладка Advanced.
Src. Adress List: <Указываем ранее созданный лист>, (в моем случае tel).
Вкладка Action.
Action: accept
Вкладка General.
Chain: forward
Protocol: 6(TCP)
Dst. Port: 80
In. Interface: <Здесь указываем WAN интерфейс>, (в моем случае ether1-gateway)
Вкладка Action.
Action: drop
Открываем IP => Firewall => NAT.
Вкладка General.
Chain: dsnat
Protocol: 6(TCP)
Dst. Port: 80
In. Interface: <Здесь указываем WAN интерфейс>, (в моем случае ether1-gateway)
Вкладка Action.
Action: dst-nat
To Address: 192.168.0.87 (IP нашей АТС)
To Port: 80
Доступ к к веб-морде по доменному имени через SSL (Пункт не обязательный)


/ip firewall address-list
add address=outbound1.letsencrypt.org list=tel
add address=outbound2.letsencrypt.org list=tel
add address=mirror1.freepbx.org list=tel
add address=mirror2.freepbx.org list=tel

Создадим AMI-пользователя в FreePBX
[callme] ; Имя AMI-пользователя
secret = 123456789 ; Пароль для подключения, ставим посложней
deny = 0.0.0.0/0.0.0.0 ; Запрещаем подключение с любого адреса
permit = 127.0.0.1/255.255.255.0 ; И постепенно разрешаем нужные
permit = 192.168.0.87/255.255.255.0 ; Так разрешаем конкретный хост
permit = 192.168.0.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
Настроем вебхуки в Битрикс24
- CRM (crm)
- Телефония (telephony)
- Телефония (совершение звонков) (call)
- Диск (disk)
- Структура компании (department)
- Чат и уведомления (im)
- Создание и управление Чат-ботами (imbot)
- Задачи (task)
- Задачи (расширенные права) (tasks_extended)
- Пользователи (user)
- Мгновенные сообщения системы (без доступа к служебному каналу пользователя) (pull)
- Служебный канал для мгновенных сообщений системы (подписка на информацию об обновлении всех элементов системы доступных пользователю) (pull_channel)
- Списки (lists)
Адрес обработчика: https://pbx.a-ctroy.ru/callme/CallMeOut.php (в вашем случае может быть просто http://ип-внешний/callme/CallMeOut.php)
Название: CallMeOut
Тип события: Инициация звонка через приложение (ONEXTERNALCALLSTART)
Сохраняем и получаем код авторизации: dtng3732h7777777777vmk524a4lvhj7
Настраиваем модуль интеграции Asterisk - Битрикс24

Получится:
if(preg_match('/mp3/',$event->getValue())) $globalsObj->FullFnameUrls[$callUniqueid] = $event->getValue();
mkdir /var/www/html/callme/logs
mkdir /var/www/html/callme/records
mkdir /var/www/html/callme/monitor/
mkdir /var/www/html/callme/records/wav
mkdir /var/www/html/callme/records/mp3
touch /var/www/html/callme/logs/CallMe.log
chown asterisk:asterisk /var/www/html/callme -R
chmod +x /var/www/html/callme/CallMeIn.php
chmod +x /var/www/html/callme/CallMeOut.php
globals {
WAV=/var/www/html/callme/records/wav; //Временный каталог с WAV
MP3=/var/www/html/callme/records/mp3; //Куда выгружать mp3 файлы
URLRECORDS=https://pbx.a-ctroy.ru/records/mp3;
RECORDING=0; // Запись, 1 - включена.
};
[macro-hangupcall]
include => macro-hangupcall-custom
exten => s,1,Set(CDR(userfield)=${CHANNEL(hangupsource)})
exten => s,n,Set(FullFname=https://pbx.a-ctroy.ru/callme/monitor/${YEAR}/${MONTH}/${DAY}/${CALLFILENAME}.mp3)
exten => s,n,Set(CallStart=${UNIQUEID})
exten => s,n,Set(CallStop=${STRFTIME(epoch,,%s)})
exten => s,n,Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)})
exten => s,n,ExecIF(${ISNULL(${CallMeDISPOSITION})}?Set(CallMeDISPOSITION=${CDR(disposition)}):NoOP(=== CallMeDISPOSITION already was set ===))
exten => s,n,System(/usr/bin/lame -h -b 192 /var/spool/asterisk/monitor/${YEAR}/${MONTH}/${DAY}/${CALLFILENAME}.${MON_FMT} /var/spool/asterisk/monitor/${YEAR}/${MONTH}/${DAY}/${CALLFILENAME}.mp3)
exten => s,n,System(/bin/rm -rf /var/spool/asterisk/monitor/${YEAR}/${MONTH}/${DAY}/${CALLFILENAME}.${MON_FMT})
exten => s,n,Set(CDR(recordingfile)=${CALLFILENAME}.mp3)
exten => s,n,Hangup
exten => s,n,MacroExit()
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 {
793230111000=> { //наш номер транка
&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 ===));
System(curl -s https://pbx.a-ctroy.ru/callme/CallMeOut.php --data action=sendcall2b24 --data call_id=${CallMeCALL_ID} --data-urlencode FullFname=${FullFname} --data CallIntNum=${CallIntNum} --data CallDuration=${CallMeDURATION} --data-urlencode CallDisposition=${CallMeDISPOSITION});
}
}
context default {
_X. => {
Hangup();
}
};
context dial_out {
_. => {
&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 https://pbx.a-ctroy.ru/callme/CallMeOut.php --data action=sendcall2b24 --data call_id=${CallMeCALL_ID} --data-urlencode FullFname=${FullFname} --data CallIntNum=${CallIntNum} --data CallDuration=${CallMeDURATION} --data-urlencode CallDisposition=${CallMeDISPOSITION});
}
};
Сконфигурируем Supervisor
[unix_http_server]
file=/tmp/supervisor.sock ; the path to the socket file
chmod=0777 ; socket file mode (default 0700)
[inet_http_server] ; inet (TCP) server disabled by default
port=*:9001 ; ip_address:port specifier, *:port for all iface
chmod=0777
chown=root:supervisor
[supervisord]
logfile=/tmp/supervisord.log ; main log file; default $CWD/supervisord.log
logfile_maxbytes=50MB ; max main logfile bytes b4 rotation; default 50MB
logfile_backups=10 ; # of main logfile backups; 0 means none, default 10
loglevel=debug ; log level; default info; others: debug,warn,trace
pidfile=/tmp/supervisord.pid ; supervisord pidfile; default supervisord.pid
nodaemon=true ; start in foreground if true; default false
minfds=1024 ; min. avail startup file descriptors; default 1024
minprocs=200 ; min. avail process descriptors;default 200
chmod=0777
chown=root:supervisor
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket
chmod=0777
chown=root:supervisor
[program:callme]
command=/usr/bin/php CallMeIn.php
directory=/var/www/html/callme
autostart=true
autorestart=true
startretries=5
stderr_logfile=/var/www/html/callme/logs/daemon.log
stdout_logfile=/var/www/html/callme/logs/daemon.log
[Unit]
Description=Supervisor daemon
Documentation=http://supervisord.org
After=network.target
[Service]
ExecStart=/usr/bin/supervisord -n -c /etc/supervisord/supervisord.conf
ExecStop=/usr/bin/supervisorctl $OPTIONS shutdown
ExecReload=/usr/bin/supervisorctl $OPTIONS reload
KillMode=process
Restart=on-failure
RestartSec=42s
[Install]
WantedBy=multi-user.target
Alias=supervisord.service
ps aux | grep super


Нюансы

Обновление (для меня)
chown asterisk:asterisk /var/www/html/callme -R
<VirtualHost *:81>
ServerName callme.a-ctroy.ru
ServerAlias callme.a-ctroy.ru
DocumentRoot /var/www/callme
ErrorLog /var/www/callme/error.log
CustomLog /var/www/callme/requests.log combined
</VirtualHost>
systemctl restart httpd.service