IP ATC Asterisk — это мощный комбайн в области IP-телефонии. А web-интерфейс FreePBX, созданный для Asterisk, значительно упрощает настройку и снижает порог вхождения в систему.
Если вы можете придумать какую-либо задачу, связанную с IP-телефонией, то почти наверняка это можно реализовать в Asterisk. Но будьте уверены, что от вас потребуется упорство и выдержка.
Перед нами встала задача настроить e-mail уведомления о пропущенных вызовах. Точнее говоря, оповещать через e-mail о тех случаях, когда входящий вызов перешёл в очередь, но никто (из агентов) так и не ответил на этот входящий вызов.
На удивление мы не обнаружили штатных средств для решения этой задачи во FreePBX. О том, как мы решили эту задачу, расскажу под катом.
Предисловие
Перед решением задачи «в лоб» мы конечно поискали информацию в интернете, но решения под ключ не нашли (возможно плохо искали, но что поделаешь… ).
Навыков работы непосредственно в Asterisk не так много, как хотелось бы, поэтому решение, предлагаемое
Понравилось решение, предложенное
Прочитав обсуждение
Что у нас есть:
Есть FreePBX 13.0.197, который использует Asterisk 13.12.1. Версия ОС SHMZ release 6.6 (Final). Дистрибутив базируется на CentOS.
В Asterisk настроен IVR (голосовое меню) раскидывающий входящие вызовы на разные Queues (очереди). Каждой очереди назначены Agents (агенты), т. е. операторы.
Теория
Что происходит в Asterisk
Когда на Asterisk поступает входящий вызов, этот вызов попадает на IVR. Звонящий делает выбор, нажав определенную цифру на телефоне, и попадает в определенную очередь. После этого всем свободным агентам очереди одновременно поступает звонок.
Для того чтобы лучше понять, что происходит в этот момент и что происходит дальше, обратимся к Report CDR (Рис.1).
Рис.1
Когда входящий вызов попал в очередь, у всех агентов значение переменной «Disposition» стало равным «NO ANSWER», если агенты в этот момент были не заняты. Переменная «Disposition» могла принять и другие значения (см.
Из Report CDR можно заметить, что когда вызов перешел в очередь (в колонке App значение становится равным «Queue»), то все события фигурируют с одинаковым «uniqueid» (колонка System).
Коротко о CDR
Важно понимать что такое CDR, и в какой именно момент в CDR заносятся данные, которые мы наблюдаем в Report CDR. CDR, относительно операционной системы — это база данных, в которую Asterisk записывает детализированный отчет вызовов (см.
Принцип работы созданного решения
Так как у нас познаний в bash больше, чем познаний в Asterisk, то основная идея получилась следующей. Перед событием hangupcall вызвать bash-скрипт. В этот скрипт передать 3 параметра. Первый параметр «uniqueid», для фильтрации данных, получаемых из CDR. Второй параметр «CALLERID(num)» (номер звонившего), чтобы знать кому перезвонить. Третий параметр «NODEST» (номер очереди), в которую поступил звонок, для того, чтобы знать по какому вопросу был звонок, и кому отправить e-mail уведомление о пропущенном вызове.
Bash-скрипт должен подключиться к базе asteriskcdrdb в mysql и взять все значения переменной «Disposition» с определенным «uniqueid». Из полученных данных нужно исключить значения: «NO ANSWER», «BUSY», «FAILED», «UNKNOWN». В результате останутся либо «ANSWERED» — на входящий вызов ответили, либо вообще ни чего — пропущенный вызов.
Далее, если вызов оказался пропущенным, то скрипт должен отправить e-mail уведомление.
Забегая вперед отмечу важный момент. Asterisk выполняет команды последовательно, дожидаясь их выполнения (что в общем-то логично). А вызывать bash-скрипт мы будем до того, как выполнится команда hangupcall. Таким образом в момент непосредственного выполнения скрипта, в CDR еще не будет внесена информация об искомом нами «uniqueid». Для решения этой проблемы bash-скрипт мы будем вызывать с параметром «&», чтобы Asterisk сразу перешел к выполнению следующего шага, т. е. hangupcall. А внутри bash-скрипта, в самом начале, мы установим небольшую задержку по времени, чтобы дать время для Asterisk внести данные с интересующим нас «uniqueid» в CDR.
Практика
Перед тем как перейти к настройке Asterisk и созданию bash-скрипта, нужно настроить отправку e-mail уведомлений. Для этого мы будем использовать утилиту postfix.
Настройка postfix
У нас есть почтовый домен «lucky.ru», расположенный в Яндексе. Мы настроим postfix в режим smtp-клиента и будем отправлять письма с аккаунта [email protected].
За основу взято решение отсюда:
Сначала установим/обновим/проверим наличие пакетов:
yum install postfix
yum install mailx
yum install cyrus-sasl cyrus-sasl-lib cyrus-sasl-plain
Не будем затирать основной файл конфигурации postfix «/etc/postfix/main.cf», а создадим его резервную копию:
cp /etc/postfix/main.cf /etc/postfix/main.cf.sav
Редактируем файл «/etc/postfix/main.cf» и приводим его к следующему виду:
nano /etc/postfix/main.cf
#####################
relayhost =
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/private/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_sasl_type = cyrus
smtp_sasl_mechanism_filter = login
smtp_sender_dependent_authentication = yes
sender_dependent_relayhost_maps = hash:/etc/postfix/private/sender_relay
smtp_generic_maps = hash:/etc/postfix/generic
smtp_tls_CAfile = /etc/postfix/ca.pem
smtp_use_tls = yes
smtputf8_autodetect_classes = all
#####################
Не каждую строку в «/etc/postfix/main.cf» можно комментировать. Комментарии в некоторых строках не определяются парсером и передаются в обработку, а это приводит к ошибкам. Лучше отказаться от комментариев внутри этого файла. Можете поэкспериментировать с этим запустив в соседнем окне «tail -f /var/log/messages».
Отмечу строку «smtputf8_autodetect_classes = all». Эта запись включает utf-8 по умолчанию, что позволяет использовать кириллицу и в теле письма, и в теме письма без дополнительных манипуляций (См.
Создадим каталог для файлов конфигураций:
mkdir /etc/postfix/private
Редактируем файл «/etc/postfix/private/sender_relay». В нем нужно указать на какой smtp-сервер нужно ссылаться при использовании нашего почтового домена:
nano /etc/postfix/private/sender_relay
#####################
@lucky.ru smtp.yandex.ru
#####################
Редактируем файл «/etc/postfix/private/sasl_passwd». В нем мы укажем e-mail адрес, который мы будем использовать для отправки писем, а так же логин и пароль от этой учетной записи (логин и пароль указываем через двоеточие):
nano /etc/postfix/private/sasl_passwd
#####################
[email protected] [email protected]:password_asterisk
#####################
Редактируем файл «/etc/postfix/generic». В нем мы пропишем правила подмены исходящего адреса (см.
nano /etc/postfix/generic
#####################
root [email protected]
root@localhost [email protected]
[email protected] [email protected]
root@freepbx [email protected]
[email protected] [email protected]
root@asterisk [email protected]
[email protected] [email protected]
asterisk [email protected]
asterisk@localhost [email protected]
[email protected] [email protected]
asterisk@freepbx [email protected]
[email protected] [email protected]
asterisk@asterisk [email protected]
[email protected] [email protected]
[email protected] [email protected]
#####################
Изначальный исходящий адрес зависит от содержимого «/etc/hosts» и «/etc/hostname», а также от имени пользователя, который будет отправлять письмо. Т. е. не смотря на то, что мы используем smtp-клиент и отправляем письма от [email protected], все равно в адрес отправителя postfix изначально подставит «что-то своё» и это нужно исправить правилами из этого файла конфигурации.
Приведу содержимое своего файла «/etc/hosts»:
cat /etc/hosts
#####################
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 asterisk.localdomain
127.0.0.1 localhost.localdomain localhost
::1 asterisk localhost localhost6
#####################
Важно, чтобы сервер имел какой-либо домен (значение после точки), потому что утилита mail «ищет» имя домена в «/etc/hosts» и если «не находит» его сразу, то продолжит это делать в течение еще нескольких минут и только потом отправит письмо. Т. е. если домен не прописан, то письмо будет уходить с задержкой в несколько минут.
Приведу содержимое своего файла «/etc/hostname»:
cat /etc/hostname
#####################
asterisk
#####################
Далее необходимо перевести созданные файлы конфигурации в индексированные базы данных, для этого выполним следующую команду:
postmap /etc/postfix/generic && postmap /etc/postfix/private/{sasl_passwd,sender_relay}
Далее нам необходимо скачать и разместить на сервере сертификат smtp.yandex.ru, для этого выполним следующую команду:
openssl s_client -starttls smtp -crlf -connect smtp.yandex.ru:25 > /etc/postfix/ca.pem
Но после того, как на экран выйдет техническая информация команда будет «продолжать висеть». Нажмите Ctrl+C чтобы прервать её.
Теперь вручную удалим из получившегося файла весь мусор и оставим только сертификат. Должно получиться нечто подобное:
nano /etc/postfix/ca.pem
#####################
-----BEGIN CERTIFICATE-----
MIIGazCCBVOgAwIBAgIQcUU9mJXW4OUs5Gf0JfLtsjANBgkqhkiG9w0BAQsFADBf
...
nRG0DfdqYIuPGApFORYe
-----END CERTIFICATE-----
#####################
И наконец перезапустим postfix:
service postfix restart
Отправляем тестовое письмо:
echo "Это тело письма" | mail -s "Это тема" [email protected]
[email protected] — адрес назначения
На этом настройка posfix закончена.
Пишем bash-скрипт
Создаем директорию для хранения bash-скрипта (тут кому где больше нравится):
mkdir /home/asterisk/scripts
Создаем файл bash-скрипта:
touch /home/asterisk/scripts/noanswer.sh
Выдаем файлу скрипта права на выполнение:
chmod +x /home/asterisk/scripts/noanswer.sh
Если есть сомнения в правах на файл, то на время отладки можно дать полный доступ к файлу. Но это «не безопасно».
chmod 777 /home/asterisk/scripts/noanswer.sh
Текст bash-скрипта:
nano /home/asterisk/scripts/noanswer.sh
#####################
#!/bin/bash
sleep 7
res_sql="SELECT disposition FROM cdr WHERE uniqueid = '$1'"
answer=`mysql -u freepbxuser -pPassword_freepbxuser -D asteriskcdrdb -B -N -e "$res_sql" | grep -E -v "NO ANSWER|BUSY|FAILED|UNKNOWN" | head -n 1`
error_kod=0
if [ "$answer" != "ANSWERED" ]
then
case $3 in
68800)
address="[email protected]"
subject="по важному вопросу"
;;
63100)
address="[email protected]"
subject="по вопросам linux debian"
;;
63200)
address="[email protected]"
subject="по вопросам windows"
;;
63300)
address="[email protected]"
subject="по вопросам freebsd"
;;
63400)
address="[email protected]"
subject="по вопросам linux ubuntu"
;;
63500)
address="[email protected]"
subject="по вопросам linux centos"
;;
*)
address="[email protected]"
error_kod=1
;;
esac
case $error_kod in
0)
echo "Пропущен вызов от абонента $2, звонившего $subject." | mail -s "Пропущен вызов от $2" $address
echo "Пропущен вызов для $address от абонента $2, звонившего $subject. uid=$1" | mail -s "Пропущен вызов от $2" [email protected]
;;
1)
echo "Пропущен вызов от $2. Очередь неизвестна. uid=$1" | mail -s "Пропущен вызов от $2" [email protected]
;;
esac
fi
#####################
Краткий разбор скрипта:
«sleep 7»:
Это та самая задержка по времени, о которой я писал ранее. У нас установлена задержка на 7 сек. Хотя, думаю, и одной секунды вполне хватит.
«res_sql="SELECT disposition FROM cdr WHERE uniqueid = '$1'"»:
Запрос в mysql мы вынесли в отдельную переменную для удобства.
Далее мы делаем запрос в mysql и фильтруем полученный вывод. Удаляем все варианты кроме «ANSWERED», если такой вообще есть. Если же значений «ANSWERED» несколько, то нужно оставить только одно. В конце в переменную «answer» мы получим либо «ANSWERED» либо «».
Если значение переменной «answer» не равно «ANSWERED», то это пропущенный вызов. В зависимости от номера очереди, с помощью оператора case мы зададим адрес, кому именно необходимо отправить e-mail уведомление, и что в этом сообщении написать (изменяемая часть сообщения).
Далее рассмотрен вариант, когда очередь задана в Asterisk, но не описана в скрипте. В этом случае [email protected] получит письмо, о том, что очередь не известна скрипту.
Если же очередь описана, то будет отправлено письмо по назначению и дублирующее письмо на [email protected] с указанием «uniqueid», для того, чтобы можно было отследить события по этому звонку, в случае необходимости.
На этом скрипт заканчивается.
Отмечу, что для подключения к mysql мы использовали логин и пароль, которые заранее узнали. Во FreePBX для того, чтобы узнать логин пользователя Asterisk в mysql выполните следующую команду:
cat /etc/amportal.conf | grep AMPDBUSER
А для того, чтобы узнать пароль пользователя Asterisk в mysql выполните следующую команду:
cat /etc/amportal.conf | grep AMPDBPASS
Настройка Asterisk
Мы используем FreePBX. Во FreePBX есть разные типы конфигурационных файлов (см.
Мы будем работать с файлом конфигурации «extensions_override_freepbx.conf», так как он относится к типу custom.
Для начала убедимся, что в файле «/etc/asterisk/extensions.conf» подключен файл «extensions_override_freepbx.conf». Для этого выполним следующую команду:
cat /etc/asterisk/extensions.conf | grep extensions_override_freepbx.conf
#####################
#include extensions_override_freepbx.conf
#####################
Редактируем файл «/etc/asterisk/extensions_override_freepbx.conf» и приведем его к следующему виду:
nano /etc/asterisk/extensions_override_freepbx.conf
#####################
[ext-queues]
exten => h,1,System(/home/asterisk/scripts/noanswer.sh ${CDR(uniqueid)} ${CALLERID(num)} ${NODEST} &)
exten => h,2,Macro(hangupcall,)
#####################
Как я и писал ранее символ «&» в конце обязателен. Так как мы будем работать в bash-скрипте с данными CDR непосредственно из базы mysql, а эти данные заносятся в mysql только после выполнения «exten => h,2,Macro(hangupcall,)», то необходимо не ждать окончания отработки bash-скрипта, а перейти к выполнению следующего шага в Asterisk. А сам bash-скрипт должен содержать задержку по времени, перед выполнением основной своей части.
Для того, чтобы изменения в конфигурационном файле «/etc/asterisk/extensions_override_freepbx.conf» вступили в силу необходимо перезагрузить ядро Asterisk следующей командой:
/usr/sbin/asterisk -rx "core restart now"
Это нужно сделать после того как bash-скрипт будет создан.
Заключение
Наверное, это 1001-й способ «отлова пропущенных вызовов» в Asterisk. Поделитесь в комментариях как эту задачу решаете вы. И что, по вашему мнению, можно доработать/переделать/оптимизировать. Будем признательны за конструктивные идеи.
Источник: habr.com