Приложение логирования переводов
Представим ситуацию, в которой у вас нет «умной маршрутизации» и звонок приходит на коллцентер, а далее уже распределяется по отделам и исполнителям. При этом вы будете использовать перевод звонка, а иногда и целую серию – что приведёт к созданию нескольких записей в базе о текущем вызове, и, возможно, нескольким записям разговоров. Это может ввести системы […]
Представим ситуацию, в которой у вас нет «умной маршрутизации» и звонок приходит на коллцентер, а далее уже распределяется по отделам и исполнителям. При этом вы будете использовать перевод звонка, а иногда и целую серию – что приведёт к созданию нескольких записей в базе о текущем вызове, и, возможно, нескольким записям разговоров. Это может ввести системы статистики и анализа в некоторое замешательство. Поэтому сегодня мы рассмотрим приложение, которое автоматически отслеживает и сохраняет в базу историю переводов начиная от номера клиента и заканчивая последним разговаривавшим с ним оператором.
В нашем случае роль хранилища будет играть дополнительная таблица в существующей базе (при использовании freepbx) asteriskcdrdb.
«Демон» будет представлять из себя PHP-скрипт. В его задачи входит подключение и удержание постоянного соединения на время собственной жизни с AMI и MYSQL, это сэкономит ресурсы и некоторое время обработки. Так же демону необходимо постоянно прослушивать попадающие в сокет события в ожидании перевода.
Для написания контроллера будет использоваться стандартный язык командной строки unix-систем: bash. Его задача запустить, корректно остановить, проверить статус приложения.
Теперь рассмотрим все три пункта более детально. Что касается хранилища – тут всё просто если вы уже имели опыт работы с базами данных напрямую или посредственно. Нам нужно подключится к базе данных, например, из консоли:
# mysql –u <username> -p<password>
Далее нам следует задать базу данных с которой мы будем работать. Для нас это штатная для FreePBX – asteriskcdrdb.
> use asteriskcdrdb;
Указав базу, нам требуется создать в ней таблицу для хранения информации о переводах. Сделаем это запросом вида:
> CREATE TABLE IF NOT EXIST transfers (
calldate varchar(100),
event varchar(100),
linkedid varchar(100),
src varchar(100),
trt varchar(100),
dst varchar(100)
);
Далее, пока мы находимся в консоли займёмся доступами к БД и AMI. Для подключения к первому нам достаточно пользователя, которого мы использовали для создания таблицы, а вот с ami-учёткой нужно будет поработать. А именно создадим своего пользователя.
Открываем на редактирование файл:
# nano /etc/asterisk/manager_custom.conf
И добавляем в конец примерно следующее содержимое, с заменой логина и пароля:
[transfer_manager]
secret = 8D******************Ghzy
deny=0.0.0.0/0.0.0.0
permit=127.0.0.1/255.255.255.0
read=call
write=
writetimeout=5000
Сохраняем изменения и перечитываем настройки. Для этого переходим в CLI-консоль:
# asterisk –rvvvvv
И, собственно, даём команду обновления:
CLI > manager reload
Проверяем, что наш новый пользователь появился:
CLI > manager show users
На следующем этапе нам нужно написать bash-скрипт, который будет контролировать процесс «демона», а именно запускать, останавливать и проверять активность. Для этого напишем три соответственные функции:
start(){
echo -n "Try to start <transferlist>..." && /usr/bin/php -f /usr/Nuar/php/example/transferlist.php && echo "successful"
}
Функция запуска «демона». Вызывает консольный интерпретатор php с передачей ему абсолютного пути к скрипту.
stop(){
echo -n "Try to stop <transferlist>..." && kill -9 $( ps aux | grep "[t]ransferlist.php" | awk '{print $2}' ) && echo "successful"
}
Функция остановки. Находит среди активных процессов по имени файла PID и отправляет ему сигнал завершения.
status(){
process=$( ps aux | grep "[t]ransferlist.php" )
if [[ "$process" = "" ]]; then
echo "transferlist is dead"
else
echo "transferlist is alive"
fi
}
Функция частично повторяет предыдущую, с той лишь разницей что в зависимости от результата нахождения или отсутствия имени файла в активных процессах ограничивается выдачей оповещения.
Добавим ещё одну функцию, часто используемую в таких приложениях: перезапуск. Она будет содержать описанные выше: остановку и запуск.
restart(){
stop
start
}
И последнее что здесь должно быть – это вариатор, зависящий от переданного извне аргумента:
case "$1" in
start) start;;
stop) stop;;
status) status;;
restart) restart;;
*) echo "Use: $0 {start|stop|status|restart}"
Esac
Его задача заключается в сопоставлении переданного аргумента (управляющего слова) с одной из описанных выше функцией. На этом контроллер закончен, полный код предоставлен ниже:
#!/bin/bash
#
# <transferlist>
#
# functions
start(){
echo -n "Try to start <transferlist>..." && /usr/bin/php -f /usr/Nuar/php/example/transferlist.php && echo "successful"
}
stop(){
echo -n "Try to stop <transferlist>..." && kill -9 $( ps aux | grep "[t]ransferlist.php" | awk '{print $2}' ) && echo "successful"
}
status(){
process=$( ps aux | grep "[t]ransferlist.php" )
if [[ "$process" = "" ]]; then
echo "transferlist is dead"
else
echo "transferlist is alive"
fi
}
restart(){
stop
start
}
# controller
case "$1" in
start) start;;
stop) stop;;
status) status;;
restart) restart;;
*) echo "Use: $0 {start|stop|status|restart}"
Esac
Получившийся файл сохраняем на АТС в директории /etc/init.d/ под любым наименованием без указания расширения. В нашем случае это «transferlist». При этом важно не забыть и дать права на исполнение:
# chmod a+x /etc/init.d/<scriptname>
И последний, третий блок приложения: сам «демон». Напомним, что в его задачи входит:
- Создание независимого процесса
- Поддержания соединения с mysql и ami
- Получение и анализ событий asterisk
- При событии перевода выхватывание нужных параметров и запись в БД
Рассматривать пошагово не будем, в целях экономии, а сразу выложим готовый вариант:
<?php
// AMI
define('AMI_HOST', '127.0.0.1');
define('AMI_PORT', '5038');
define('AMI_USER', 'f****************er');
define('AMI_PASS', '8D********************zy');
// MySQL
define('BD_HOST','127.0.0.1');
define('BD_USER','fr****************er');
define('BD_PASS','aa****************47a');
// Logfile
define('LOG', '/var/log/asterisk/transferlist.log');
// Локальный часовой пояс
date_default_timezone_set("Europe/Moscow");
function bd_bridge($database='', $query=''){
$link = @mysqli_connect(BD_HOST, BD_USER, BD_PASS, $database) or die("Error: ".mysqli_connect_error($link));
@mysqli_set_charset($link,'utf8') or die("Error: ".mysqli_connect_error($link));
$sqlTable = @mysqli_query($link,$query) or die("Error: ".mysqli_error($link));
}
// Призываем демона
switch ($pid = pcntl_fork()) {
case -1: // error
die('Fork failed');
break;
case 0: // child
// Инициация сокета
$socket = fsockopen(AMI_HOST, AMI_PORT, $errno, $errstr, 10) or die("Error : $errstr ($errno)");
stream_set_timeout($socket, 0, 400000);
fwrite($socket, "Action: Login\r\nUsername: ".AMI_USER."\r\nSecret: ".AMI_PASS."\r\n\r\n");
// файл для логирования
file_put_contents(LOG, 'transferlist was started in '.date("Y-m-d H:i:s")."\r\n");
$callsList = [];
$bridgeList = [];
while (true) {
// Создаём\обнуляем переменные
$stream_buffer = '';
$outArray = [];
$data = [];
// Считываем ВСЕ данные из сокета
do{
$stream_buffer .= fread($socket, 1024);
sleep(0.1);
$status = socket_get_status($socket);
} while ($status['unread_bytes']);
// Делим буфер на блоки...
$blocks = explode("\r\n\r\n", $stream_buffer);
// ...и перебираем каждый из них
foreach ($blocks as $key => $value) {
if(strpos($value,'Event') !== false) {
// Блоки делим на строки...
$lines = explode("\r\n", $value);
// ...а их на партиции
for($i=0; $i<count($lines); $i++){
$partitions=explode(":", $lines[$i]);
if (isset($partitions[1])) $data[trim($partitions[0])] = trim($partitions[1]);
}
// Если массив события не пуст - фильтруем
if(!empty($data) && in_array($data['Event'], ['BlindTransfer','AttendedTransfer'])) {
// file_put_contents(LOG, print_r($data,true), FILE_APPEND);
switch ($data['Event']) {
case 'BlindTransfer':
// file_put_contents(LOG, print_r($data,true), FILE_APPEND);
$sqlStr = "INSERT INTO transfers VALUES ('"
.date("Y-m-d H:i:s")."','"
.$data['Event']."','"
.$data['Linkedid']."','"
.$data['TransfereeCallerIDNum']."','"
.$data['TransfererCallerIDNum']."','"
.$data['Extension']."')".PHP_EOL;
break;
case 'AttendedTransfer':
file_put_contents(LOG, print_r($data,true), FILE_APPEND);
$sqlStr = "INSERT INTO transfers VALUES ('"
.date("Y-m-d H:i:s")."','"
.$data['Event']."','"
.$data['Linkedid']."','"
.$data['TransfereeCallerIDNum']."','"
.$data['OrigTransfererCallerIDNum']."','"
.$data['TransferTargetExten']."')".PHP_EOL;
break;
}
bd_bridge('asteriskcdrdb', $sqlStr);
}
}
}
}
// Закрытие сокета
fwrite($socket, "Action: Logoff\r\n\r\n");
fclose($socket);
break;
default: // parent
exit(0);
break;
}
?>
Данный скрипт готов к использованию в показанном виде. Поправку стоит внести только в указанные доступы AMI и MYSQL – здесь нужно указать ваши данные, которые вы настроили на первом шаге.
Стоит отдельно сказать, что поскольку скрипт относится к категории «демонов», у него отсутствуют стандартные методы вывода – в консоли он не задерживает управление и ничего не отображает. Для отладки внутри кода предусмотрено логирование в строках:
file_put_contents(LOG, 'transferlist was started in '.date("Y-m-d H:i:s")."\r\n");
…
// file_put_contents(LOG, print_r($data,true), FILE_APPEND);
Первая раскомментирована всегда и выполняет две функции:
- Очистка файла и открытие для записи
- Запись даты и времени запуска демона
Если хотите сменить файл логирования сделать это можно в настройках скрипта, в отмеченной строке. Однако, делать это не рекомендуется, поскольку могут возникнуть проблемы доступности и как следствие вы не сможете получать логи приложения.
Если вам нужно что-то вывести из промежуточных значений, добавьте строчку вида:
file_put_contents(LOG, $sqlStr, FILE_APPEND);
И не забудьте перезапустить приложение:
На этом всё, можно переходить к тестированию! Проверим контроллер.
Совершаем несколько звонков и проверяем результат в созданной нами таблице. Если всё настроено корректно, и не возникнет ошибок в процессе, вы получите примерно такой результат:
В итоге мы получили инструмент отслеживания переводов. Полученную информацию можно использовать для прослеживания цепочки переводов, можно по ним восстановить разрозненные файлы записей разговоров, а можно доработать под собственные нужды. В будущем с нашей стороны планируется создание веб-интерфейса для удобного отображения данных из полученной таблицы и поиска в ней.
Остались вопросы?
Я - Виталий Шелест, менеджер компании Voxlink. Хотите уточнить детали или готовы оставить заявку? Укажите номер телефона, я перезвоню в течение 3-х секунд.
категории
- DECT
- Linux
- Вспомогательный софт при работе с Asterisk
- Интеграция с CRM и другими системами
- Интеграция с другими АТС
- Использование Elastix
- Использование FreePBX
- Книга
- Мониторинг и траблшутинг
- Настройка Asterisk
- Настройка IP-телефонов
- Настройка VoIP-оборудования
- Новости и Статьи
- Подключение операторов связи
- Разработка под Asterisk
- Установка Asterisk
VoIP оборудование
ближайшие курсы
Новые статьи
10 доводов в пользу Asterisk
Распространяется бесплатно.
Asterisk – программное обеспечение с открытым исходным кодом, распространяется по лицензии GPL. Следовательно, установив один раз Asterisk вам не придется дополнительно платить за новых абонентов, подключение новых транков, расширение функционала и прочие лицензии. Это приближает стоимость владения станцией к нулю.
Безопасен в использовании.
Любое программное обеспечение может стать объектом интереса злоумышленников, в том числе телефонная станция. Однако, сам Asterisk, а также операционная система, на которой он работает, дают множество инструментов защиты от любых атак. При грамотной настройке безопасности у злоумышленников нет никаких шансов попасть на станцию.
Надежен в эксплуатации.
Время работы серверов некоторых наших клиентов исчисляется годами. Это значит, что Asterisk работает несколько лет, ему не требуются никакие перезагрузки или принудительные отключения. А еще это говорит о том, что в районе отличная ситуация с электроэнергией, но это уже не заслуга Asterisk.
Гибкий в настройке.
Зачастую возможности Asterisk ограничивает только фантазия пользователя. Ни один конструктор шаблонов не сравнится с Asterisk по гибкости настройки. Это позволяет решать с помощью Asterisk любые бизнес задачи, даже те, в которых выбор в его пользу не кажется изначально очевидным.
Имеет огромный функционал.
Во многом именно Asterisk показал какой должна быть современная телефонная станция. За многие годы развития функциональность Asterisk расширилась, а все основные возможности по-прежнему доступны бесплатно сразу после установки.
Интегрируется с любыми системами.
То, что Asterisk не умеет сам, он позволяет реализовать за счет интеграции. Это могут быть интеграции с коммерческими телефонными станциями, CRM, ERP системами, биллингом, сервисами колл-трекинга, колл-бэка и модулями статистики и аналитики.
Позволяет телефонизировать офис за считанные часы.
В нашей практике были проекты, реализованные за один рабочий день. Это значит, что утром к нам обращался клиент, а уже через несколько часов он пользовался новой IP-АТС. Безусловно, такая скорость редкость, ведь АТС – инструмент зарабатывания денег для многих компаний и спешка во внедрении не уместна. Но в случае острой необходимости Asterisk готов к быстрому старту.
Отличная масштабируемость.
Очень утомительно постоянно возвращаться к одному и тому же вопросу. Такое часто бывает в случае некачественного исполнения работ или выбора заведомо неподходящего бизнес-решения. С Asterisk точно не будет такой проблемы! Телефонная станция, построенная на Asterisk может быть масштабируема до немыслимых размеров. Главное – правильно подобрать оборудование.
Повышает управляемость бизнеса.
Asterisk дает не просто набор полезных функций, он повышает управляемость организации, качества и комфортности управления, а также увеличивает прозрачность бизнеса для руководства. Достичь этого можно, например, за счет автоматизации отчетов, подключения бота в Telegram, санкционированного доступа к станции из любой точки мира.
Снижает расходы на связь.
Связь между внутренними абонентами IP-АТС бесплатна всегда, независимо от их географического расположения. Также к Asterisk можно подключить любых операторов телефонии, в том числе GSM сим-карты и настроить маршрутизацию вызовов по наиболее выгодному тарифу. Всё это позволяет экономить с первых минут пользования станцией.