
IP ATC Asterisk — мощно решение в областта на IP телефонията. Уеб интерфейсът FreePBX, създаден за Asterisk, значително улеснява конфигурацията и намалява бариерата за влизане в системата.
Ако можете да измислите каквато и да е задача, свързана с IP телефонията, почти сигурно може да бъде реализирана в Asterisk. Но бъдете сигурни, че ще ви е нужно упорство и търпение.
Пред нас стоеше задачата да настроим e-mail известия за пропуснати повиквания. По-точно, да уведомяваме чрез e-mail за случаите, когато входящо повикване е преминало в опашка, но никой (от агентите) не е отговорил на това входящо повикване.
Учудващо, не открихме стандартни средства за решаване на тази задача във FreePBX. Как решихме тази задача, ще разкажа по-долу.
Предговор
Преди да решим задачата "в лоб", разбира се, потърсихме информация в интернет, но не намерихме готово решение (възможно е да не сме търсили добре, но каквото и да е...).
Не разполагаме с много умения за работа директно в Asterisk, така че предложеното решение , не беше напълно осмислено и беше отхвърлено.
Хареса ни решението, предложено от , макар и да не проработи. Оттук подчертахме, че работата в Asterisk трябва да бъде в контекста на опашките [ext-queues]. И тъй като работим с FreePBX, трябва да работим в конфигурационния файл „extensions_override_freepbx.conf“. Обърнахме внимание, че е удобно да се "похващат пропуснати повиквания" преди събитието hangupcall (завършване на повикването).
Прочитайки обсъждането на , се роди идея, че трябва да филтрираме в CDR променливата „Disposition“ по всички агенти в опашката. А след прочитане на информацията, се оформиха съвсем конкретни стъпки за решаване на поставената задача.
Какво имаме:
Имаме FreePBX 13.0.197, който използва Asterisk 13.12.1. Версия на ОС SHMZ release 6.6 (Final). Дистрибуцията е базирана на CentOS.
В Asterisk е настроен IVR (гласово меню), разпределящ входящите повиквания на различни опашки (Queues). На всяка опашка са назначени агенти.
Теория
Какво се случва в Asterisk
Когато в Asterisk постъпи входящо повикване, затова повикване попада на IVR. Звънецът прави избор, натискайки определена цифра на телефона, и попада в определена опашка. След това на всички свободни агенти в опашката едновременно постъпва повикване.
За да разберем по-добре какво се случва в този момент и какво се случва след това, ще се обърнем към Report CDR (Рис.1).

Рис.1
Когато входящият повик е попаднал в опашката, стойността на променливата «Disposition» на всички агенти е станала равна на «NO ANSWER», ако агентите в този момент не са били ангажирани. Променливата «Disposition» може да приеме и други стойности (вж. ), освен стойността «ANSWERED». В момента, когато един от агентите отговаря на входящото повикване, стойността на променливата «Disposition» на този агент става равна на «ANSWERED».
От Report CDR може да се забележи, че когато повикването е преминало в опашката (в колоната App стойността става равна на «Queue»), всички събития се появяват с идентичен «uniqueid» (колонка System).
Накратко за CDR
Важно е да разберем какво е CDR и в кой момент в CDR се записват данните, които наблюдаваме в Report CDR. CDR, относно операционната система, е база данни, в която Asterisk записва подробен отчет за повикванията (вж. ). В нашия случай това е база под името asteriskcdrdb, която се намира в mysql. Чрез опити установихме, че данните за повикване с определен «uniqueid» се записват в asteriskcdrdb не веднага след възникването на дадено събитие, а след събитието hangupcall (приключване на повикването).
Принцип на работа на създаденото решение
Тъй като имаме повече познания по 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-клиент и ще изпращаме писма от акаунта asterisk@lucky.ru.
За основа е взето решението оттук: .
Първо ще инсталираме/обновим/проверим наличието на пакети:
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». В него ще посочим имейл адреса, който ще използваме за изпращане на писма, а също така логина и паролата на тази учетна запис (логина и паролата посочваме с двоеточие):
nano /etc/postfix/private/sasl_passwd
#####################
asterisk@lucky.ru asterisk@lucky.ru:password_asterisk
#####################Редактирайте файла «/etc/postfix/generic». В него ще формулираме правилата за замяна на изходящия адрес (вижте. ):
nano /etc/postfix/generic
#####################
root asterisk@lucky.ru
root@localhost asterisk@lucky.ru
root@localhost.localdomain asterisk@lucky.ru
root@freepbx asterisk@lucky.ru
root@freepbx.localdomain asterisk@lucky.ru
root@asterisk asterisk@lucky.ru
root@asterisk.localdomain asterisk@lucky.ru
asterisk asterisk@lucky.ru
asterisk@localhost asterisk@lucky.ru
asterisk@localhost.localdomain asterisk@lucky.ru
asterisk@freepbx asterisk@lucky.ru
asterisk@freepbx.localdomain asterisk@lucky.ru
asterisk@asterisk asterisk@lucky.ru
asterisk@asterisk.localdomain asterisk@lucky.ru
root@localdomain.localdomain asterisk@lucky.ru
#####################Началният изходящ адрес зависи от съдържанието на «/etc/hosts» и «/etc/hostname», а също така и от потребителя, който ще изпрати писмото. Т.е. макар да използваме SMTP клиент и изпращаме писма от asterisk@lucky.ru, все пак в адреса на подателя 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 "Това е темата" admin@lucky.ruadmin@lucky.ru — получателят
С това настройката на postfix приключва.
Пишем 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="big_boss@lucky.ru"
subject="по важен въпрос"
;;
63100)
address="debian@lucky.ru"
subject="по въпросите на linux debian"
;;
63200)
address="windows@lucky.ru"
subject="по въпросите на windows"
;;
63300)
address="freebsd@lucky.ru"
subject="по въпросите на freebsd"
;;
63400)
address="ubuntu@lucky.ru"
subject="по въпросите на linux ubuntu"
;;
63500)
address="centos@lucky.ru"
subject="по въпросите на linux centos"
;;
*)
address="admin@lucky.ru"
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" admin@lucky.ru
;;
1)
echo "Пропуснато обаждане от $2. Опашката е неизвестна. uid=$1" | mail -s "Пропуснато обаждане от $2" admin@lucky.ru
;;
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, но не е описана в скрипта. В този случай admin@lucky.ru ще получи имейл, че опашката не е известна на скрипта.
Ако обаче опашката е описана, ще бъде изпратено писмо до назначеното място и дублиращо писмо на admin@lucky.ru с указание „uniqueid“, за да може да се проследят събитията по това обаждане, в случай на необходимост.
На това скриптът завършва.
Ще отбележа, че за свързване с mysql използвахме потребителско име и парола, които предварително научихме. В FreePBX, за да разберете потребителското име на Asterisk в mysql, изпълнете следната команда:
cat /etc/amportal.conf | grep AMPDBUSERА за да разберете паролата на Asterisk в mysql, изпълнете следната команда:
cat /etc/amportal.conf | grep AMPDBPASSНастройка на Asterisk
Използваме FreePBX. В FreePBX има различни типове конфигурационни файлове (виж. ), някои от тях FreePBX презаписва при презареждане, а някои не (те се наричат custom), тъй като са специално предназначени за потребителя.
Ще работим с конфигурационния файл „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
