Elastix — Asterisk как система экстренного голосового оповещения

Опубликовано lynx - пн, 10/03/2016 - 10:03


Требуется сделать систему экстренного голосового оповещения в случае аварий и прочих происшествий. Схема работы следующая: 
Ответственное лицо звонит на определенный номер телефона, набирает код и попадает в голосовое меню системы оповещения. Там ему предлагается записать сообщение, прослушать его, сохранить и система должна совершать звонки одновременно используя 5 телефонных линий на заранее заданные в файле номера телефонов. Причем в случае, если не взяли трубку — перезванивать оповещаемому. По окончании оповещения на указанную почту должен приходить файл с номерами телефонов на которые не смогли дозвониться вообще.

Если интересно как это работает прошу под кат.

Для совершения звонков в Астериске есть call файлы. При помещении файла в директорию/var/spool/asterisk/outgoing он автоматически совершает звонок. Давайте для начала разберемся из чего состоят call файлы и что у них внутри.

Внутри каждого файла может быть несколько переменных:
Channel: - Указывает канал для исходящего вызова
CallerID: Name - Соответственно имя, от кого будет исходить вызов
MaxRetries: - Вот как раз очень нужный параметр количества попыток дозвона, если установить 0, то будет считаться что это 1 попытка.
RetryTime: - Время между попытками неудачного вызова, задается в секундах, противоречивый на практике параметр, если поставить мало - то вероятность дозвониться падает, а если много - то сильно может увеличить общее время обзвона (при большом количестве номеров). Приходится выбирать золотую середину.
WaitTime: - Время в секундах сколько будет звонить телефон у каждого абонента из списка телефонов. Думаю в районе 60 секунд вполне удобно, больше смысла нету, а меньше в учреждениях могут не успеть дойти до телефона.
Account: 
- Должно использоваться для установки поля “account code”для записи в CDR, но я не использовал
Context: <context-name> - Контекст который будет использоваться для совершения дальнейших действий(когда прошел дозвон)
Extension: - Название Extension от которого будет совершаться звонок.
Priority: - Номер приоритета для Extension, с которого нужно начать выполнение.
Set: 
- Установка переменных канала для использования их в логике обработки вызова на заданный екстеншен.
Application: - Имя приложения Asterisk, которое необходимо выполнить. Если используется приложение - то не будет использованы context, extension и priority.
Data: - Параметры для запускаемого приложения. Тоже не использовал.
Archive: Yes/No – Переносить или нет .call файл в поддиректорию "outgoing_done" с установленным значением поля "Status: значение", где значение может быть: Completed, Expired или Failed. Тоже не использовал.

Основные поля в файле посмотрели, теперь я приведу как выглядит файл у меня:

 

Channel: Local/11%1%@from-internal/n
CallerID: <5102>
MaxRetries: 4
RetryTime: 60
WaitTime: 60
Context: startmessage
Extension: 5102
Priority: 1



Теперь по порядку. В переменной канала используется Local для того чтобы все эти звонки присутствовали в CDR логах(не забываем про необходимость анализировать обзвон - для формирования списка недоступных телефонов). Имя звонящего, потом установлено 5 попыток дозвона через каждые 60 секунд, и звонить номер будет тоже 60 секунд. После того как Астериск дозвонится абоненту - будет использоваться самописный контекст startmessage. Звонить будет extension 5102, приоритет 1.

Перед номером телефона который будет вставлен в файл вставляется 11, это сделано для удобства маршрутизации звонка через нужный транк. То есть в FreePBX создается Outbound route в котором из номера вырезается вначале 11 и ставим выход через нужный транк. Парсер который в конце обзвона формирует список файлов "кому не дозвонились" тоже вырезает эти две единицы.

Раз уж мы коснулись контекстов можно заглянуть в /etc/asterisk/extensions_custom.conf (стоит Elastix)

Вначале стоит строчка exten => 9876,1,Goto(testcontext,s,1) , которая закидывает при звонке на 9876 на наш контекст, в конце статьи опишу для чего.
Далее у меня прописано 2 контекста. Первый отвечает за запись сообщения, и начало обзвона. Второй за действия, которые будет совершать система, после дозвона абоненту.

Первый контекст :

[testcontext]
exten => s,1,Answer
exten => s,n,Wait(2)
exten => s,n,Playback(/var/lib/asterisk/sounds/custom/privet-zapis)
exten => s,n,Record(obzvon-message.wav)
exten => s,n,Playback(/var/lib/asterisk/sounds/obzvon-message)
exten => s,n,Playback(/var/lib/asterisk/sounds/custom/zapusk)
exten => s,n,WaitExten(10)
exten => s,n,Hangup()
exten => 999,1,System(echo "" > /var/log/asterisk/cdr-custom/Simple.csv)
exten => 999,n,System(/mnt/script/parser.bash /mnt/CallCenter/telefony.txt /mnt/script/main.call)
exten => 999,n,System(/mnt/script/startcall.bash)
exten => 0,1, Hangup()


Описываю построчно - снимаем трубку, ждём 2 секунды, воспроизводим приветствие из файла/var/lib/asterisk/sounds/custom/privet-zapis , записываем сообщение obzvon-message.wav, сразу после записи мы воспроизводим его в трубку и запускаем звуковой файл с дальнейшими инструкциями/var/lib/asterisk/sounds/custom/zapusk. На данном моменте предлагается ввести пароль для запуска системы оповещения и ждём 10 скунд для ввода пароля. Если будет набрано 999, то запускаем скрипты запуска, если нет - вешаем трубку. 

После того как набрали 999, первым делом Астериск чистит содержимое файла с кастомным логом/var/log/asterisk/cdr-custom/Simple.csv (Позже рассмотрим как он сделан), потом запускает скрипт/mnt/script/parser.bash с двумя аргументами, первый это /mnt/CallCenter/telefony.txt - путь к файлу телефонов(по одному телефону на строку в нужном формате), второй это /mnt/script/main.call- файл с шаблоном call файла. Шаблон call файла приведен выше. Далее запускается скрипт/mnt/script/startcall.bash.

Хочу отметить что блокнотом windows лучше этот файл с телефонами не редактирова, т.к. он вставляет символы из за которых нарушается работоспособность, я пользовался Notepad ++, когда делал это из под Windows.

Приведу свой файл для формирования CDR - /etc/asterisk/cdr_custom.conf

 

[mappings]
Simple.csv => ${CSV_QUOTE(${CDR(clid)})},${CSV_QUOTE(${CDR(src)})},${CSV_QUOTE(${CDR(dst)})},${CSV_QUOTE(${CDR(dcontext)})},${CSV_QUOTE(${CDR(channel)})},$$

 


Теперь разберемся что находится в скриптах.
Вот содержимое файла /mnt/script/parser.bash :

#!/bin/bash
#первый аргумент - файл с телефонами, второй файл шаблона
FILE=`cat $1 | sort -u`

for I in $FILE
do
if [ -n "$I" ]
then
sed -e 's/\%1\%/'$I'/g' < $2 >> /mnt/script/tmp/$I.call
fi
done;



Из файла с телефонами вырезается первая строка и вставляется в call файл вместо %1%, создающийся в папке /mnt/script/tmp/ с именем "номер телефона".call .

Содержимое скрипта /mnt/script/startcall.bash :

#!/bin/bash
# Папка в которой лежат свеже-сформированные call файлы
OutDir="/mnt/script/tmp/"
# Папка в которую будут помещаться call файлы
inDir="/var/spool/asterisk/outgoing/"
# Количество call файлов одновременно находящихся в папке (равняется количеству используемых для звонка линий)
CountIn=5;

movefile ()
# Считаем количество файлов в inDir
{
CounFileInDir=$(find $inDir -name "*.call" | wc -l);
# Если количество файлов в inDir - меньше значения CountIn, то перемещаем файл в эту директорию.
if (("$CounFileInDir" < "$CountIn")); then
mv $I $inDir
# Иначе ждём 30 секунд и повторяем функцию заново.
else
sleep 30;
movefile;
fi
}
# В переменную помещаются имена всех файлов *.call в директории OutDir.
FILE=`find $OutDir -name "*.call"`
# Пробегаем по всем значениям в переменной FILE, если файл существует, то выполняем movefile.
for I in $FILE
do
if [ -n "$I" ]
then
movefile;
fi
done;
# По окончании работы скрипта - запускаем следующий скрипт.
/mnt/script/result.bash



Содержимое скрипта /mnt/script/result.bash :

#!/bin/bash
# Файл со списком телефонов
telefoni=/mnt/CallCenter/telefony.txt
# Лог файл из которого будем парсить.
logfile=/var/log/asterisk/cdr-custom/Simple.csv;

# Создаём временный файл.
cp $telefoni telefoni.tmp
# В переменную FILE заносим отсортированный временный файл только с уникальными значениями.
FILE=`cat telefoni.tmp | sort -u | uniq`
# Пробегаем по всем значениям переменной FILE , если номер телефона существует - то в переменной logfile ищутся все значения содержащие ANSWERED.
# startmessage, ищется значение текущего номера телефона, если все значения в строке сошлись то из строки берется номер 
#телефона, в котором спереди вырезаются 11 (использующиеся для маршрутизации вызова) и всё что находится после @ сортируется по уникальным записям и передается во временный файл temp

for I in $FILE
do
if [ -n "$I" ]
then
rm temp.temp
cat $logfile | grep "ANSWERED" | grep "startmessage" | grep $I | awk 'BEGIN{FS=","}{print $5}' | sed -e 's/.*\/11//g' -e 's/\@.*//g' | uniq >> temp$
fi
done;

# Переменная FILE1 получает значение файла temp.temp, сортирует и оставляет только уникальные записи.
FILE1=`cat temp.temp | sort -u | uniq`

# Формируем список кому не дозвонились.
for J in $FILE1
do
if [ -n "$J" ]
then
cat telefoni.tmp | grep -v $J > telefoni1.tmp
mv telefoni1.tmp telefoni.tmp
fi
done;
rm temp.temp; 

Отправляем список телефонов кому не дозвонились на почтовый ящик
cat telefoni.tmp | mail -s Ne_Dozvon testmail@example.com



Осталась самая малость - сделать входящую маршрутизацию для звонка. Заходим в Inbound routes и создаем маршрут с CID номером ответственного за запуск системы человека. Тоесть когда он позвонит на любой номер - то попадет на систему обзвона, если не он, то пойдёт по стандартному маршруту. Можно также задать DID чтобы прикрепить к какому то конкретному номеру телефона фирмы. Теперь в destination надо указать Misc Destinations который мы сейчас создадим. Заходим в Misc Destinations, создаём новый и вписываем 9876 в строку dial. (Помните мы добавляли строчку в extensions_custom.conf? )

Вот собственно мы и добились нужного результата, вариантов использования конечно масса, переделывать тоже можно как угодно, но эту статью я решил сделать в виде заметки для себя же, чтобы не забыть что и как работает. То что многое криво и возможно не совсем правильно я знаю, посему конструктивную критику только поддерживаю.

via


Похожие материалы: