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

от автора


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

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

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

Внутри каждого файла может быть несколько переменных:
Channel: <channel> — Указывает канал для исходящего вызова
CallerID: Name <number> — Соответственно имя, от кого будет исходить вызов
MaxRetries: <number> — Вот как раз очень нужный параметр количества попыток дозвона, если установить 0, то будет считаться что это 1 попытка.
RetryTime: <number> — Время между попытками неудачного вызова, задается в секундах, противоречивый на практике параметр, если поставить мало — то вероятность дозвониться падает, а если много — то сильно может увеличить общее время обзвона (при большом количестве номеров). Приходится выбирать золотую середину.
WaitTime: <number> — Время в секундах сколько будет звонить телефон у каждого абонента из списка телефонов. Думаю в районе 60 секунд вполне удобно, больше смысла нету, а меньше в учреждениях могут не успеть дойти до телефона.
Account: — Должно использоваться для установки поля “account code”для записи в CDR, но я не использовал
Context: <context-name> — Контекст который будет использоваться для совершения дальнейших действий(когда прошел дозвон)
Extension: <ext> — Название Extension от которого будет совершаться звонок.
Priority: <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? )

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

ссылка на оригинал статьи http://habrahabr.ru/post/170849/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *