К основному контенту

Автообзвон (auto-dial out) и обратный звонок (callback) с использованием AGI

Автообзвон

Есть задачи реализация которых возможна через автоматический обзвон. Например ?
Например вы сис.админ, у вас есть локальная сеть и куча оборудования в ней, но вы же не 24/7 на работе, а раз так, то в сети может что то произойти, например событие в системе мониторинга, а вас нет на рабочем месте. Конечно можно отправлять себе SMS, если такая вожможность есть, а имея сервер Asterisk можно и позвонить и самому себе рассказать что же случилось ;)
Или вам необходима система callback, что бы позвонив на номер заведенный на Asterisk, вы могли получить с него обратный звонок и набрать другой номер.
Итак, считаем что у вас у уже есть установленный и настроенный сервер Asterisk, который имеет выход в город.
Что бы совершить автоматический звонок нам потребуется следующие вещи:
  • сформировать call файл
  • иметь в диалплане (dialplan (определяется в /usr/local/etc/asterisk/extensions.conf)) контекст (context) со списком действий
Начнем с call файла, его основной синтаксис:
  • Channel: <channel> — Какой канал использовать для звонка
  • CallerID: «name» <number> — Установить Caller ID, проще сказать тут мы можем выставить АОН (номер с которого мы звоним).
  • MaxRetries: <number> — Максимальное кол-во попыток дозвониться, по умолчанию 0, что является одной попыткой.
  • RetryTime: <number> — Время в секундах между попытками дозвона, по умолчанию 300 (5 минут).
  • WaitTime: <number> — Сколько секунд ожидаем поднятия трубки, по умолчанию 45.
  • Context: <context-name> — Какой контекст используем из extensions.conf
  • Extension: <ext> - Какой экстеншн используем в выбранном контексте (так же в extensions.conf).
  • Priority: <priority> — С какого приоритета начинаем.
Я привел не полный список, полный список доступен тут. Лично я, для каждого номера, создаю свой контекст в extensions.conf.
Соответственно зная синтаксис мы теперь сможем сформировать call файл, на примере звонка через SIP канал:
Channel: SIP/89161112233@sip-provider
Callerid: 9998877
MaxRetries: 2
RetryTime: 20
WaitTime: 60
Context: outgoing_to_89161112233
Extension: s
Priority: 1
Для тех кто в танке :) разберем call файл построчно:
  • позвонить на SIP номер 89161112233 через провайдера sip-provider (имя из /usr/local/etc/asterisk/sip.conf для выхода в город)
  • использовать АОН 9998877
  • Максимальное кол-во попыток дозвона 2
  • Пауза между попытками 20 секунд
  • Ожидать поднятия трубки 60 секунд
  • Использовать контекст (context) с именем outgoing_to_89161112233
  • Начинать с экстеншена (ext) s
  • И начинать с приоритета 1 в экстеншене s
Сформировав call файл, его необходимо положить в папку:  /var/spool/asterisk/outgoing/ сервер Asterisk обнаружив в этой папке файл сразу же попытается отработать его.
Сразу скажу, что можно и задержать обработку файла, т.е. задержать исх. звонок и отложить его на определенное вами время.
Сервер Asterisk смотрит на дату создания call файла и если изменить дату создания на дату в будущем, то Asterisk не будет отрабатывать call файл, пока не наступит эта самая дата.
Как это сделать ? Очень просто, для этого воспользуемся командой touch, которая есть во всех unix системах.
После того как создали сам call файл берем и меняем дату его создания, шаблон даты такой:
ГОДМЕСЯЦДЕНЬЧАСЫМИНУТЫ.СЕКУНДЫ (внимание: перед секундами стоит символ точки, это так и нужно, а не опечатка).
Для примера выставим дату в будущем, скажем это 09.07.2010 19:18:55:
/usr/bin/touch -t 1007091918.55 /path/to/call/file.call
Дата создания call файла изменится и если переместить файл в /var/spool/asterisk/outgoing/, то соответственно Asterisk его не обработает до тех пор пока не наступит выставленная дата.
Совет: генерить call файл лучше в какой то tmp директории, выполнять над ними все необходимые и действия и затем мувить (переносить) в /var/spool/asterisk/outgoing/.
Теперь посмотрим на сам контекст (context), для системы мониторинга он может быть таким:
[outgoing_to_89161112233]
exten => s,1,Answer
exten => s,n,Wait(1)
exten => s,n,Background(/usr/local/share/asterisk/sounds/monitoring/welcome)
exten => s,n,Background(/usr/local/share/asterisk/sounds/monitoring/Down)
exten => s,n,Background(/usr/local/share/asterisk/sounds/monitoring/provider-a)
exten => s,n,Background(/usr/local/share/asterisk/sounds/monitoring/bye)
exten => s,n,hangup
Исходя из данного контекста Asterisk дозвонившись до 89161112233 (человек взял трубку) выполнит следущее:
  • поднимет трубку со своей стороны
  • секунду подождет
  • проиграет записанные вами звуковые файлы (полные пути к файлам указаны)
  • повесит трубку
Как записать голосовые файлы (то что вы будете проигрывать) вы можете узнать из этой статьи.
После того как Asterisk полностью отработает call файл, он его удаляет из директории /var/spool/asterisk/outgoing/.
Специальный экстеншен ‘failed
Если на вызов не ответили, и существует стандартный экстеншен с именем failed и команда с приоритетом 1 в заданном (в .call файле) контексте, управление будет передано этой команде.
exten => failed,1,Set(NumberDialed=${CUT(PassedInfo,,1)})
exten => failed,n,SetCDRUserField(${NumberDialed})
[Добавлено 11.02.2011]
Может появится ещё одна задача, а именно: если при автодозвоне трубку так и не подняли, то сразу набрать другой номер. Пример такой конструкции:
  • сначала набираем внутренний SIP  номер (6003) человека
  • если внутренний SIP номер не отвечает/занят/недоступен — набираем мобильный номер
Кажется, ну что может быть проще, написать:
exten => failed,1,Dial(SIP/89161112233@sip-provider)
А вот тут то и выяснилось, что не все так просто. На такое действо вы в консоле (выставив core set verbose 3) увидите подобное сообщение:
— Executing [failed@dialsip:1] Dial(«OutgoingSpoolFailed», «SIP/6003″) in new stack
[Feb 11 16:57:44] WARNING[94371]: channel.c:3441 ast_request: No translator path exists for channel type SIP (native 65535) to 0
[Feb 11 16:57:44] WARNING[94371]: app_dial.c:1296 dial_exec_full: Unable to create channel of type ‘SIP’ (cause 58 — Bearer capability not available)
== Everyone is busy/congested at this time (1:0/0/1)
== Auto fallthrough, channel ‘OutgoingSpoolFailed’ status is ‘CHANUNAVAIL’
[Feb 11 16:57:54] NOTICE[94371]: pbx_spool.c:356 attempt_thread: Call failed to go through, reason (8) Congestion (circuits busy)
Усе… приехали…. Лазая по результатам поиска в гугле я натыкался на сообщения:
Канал получается OutgoingSpoolFailed и именно в него пытается звонить и Dial и играть Playback. Как мне находясь в failed сделать звонок на SIP и проиграть сообщение?
Собственно точно такой же вопрос задавал себе и я сам, но ответа на него ни в Инете ни у меня не было.
Так же как и я народ пытался реализовать подобное через failed c Goto, System и т.п., но результат получался таким же — никаким :(
Получается из starndart extention failed сделать набор (Dial) другого номера не получится…..
Я уже было подумал, что не судьба и мне придется ловить DIALSTATUS, передавать его AGI скрипту (который ещё и писать придется) и тот уже будет решать что с этим делать (создавать новый call файл или ещё что то), но тут родился ещё один вариант:
core*CLI> core show channeltypes
Type        Description                              Devicestate  Indications  Transfer
----------  -----------                              -----------  -----------  --------
Skinny      Skinny Client Control Protocol (Skinny)  no           yes          no
SIP         Session Initiation Protocol (SIP)        yes          yes          yes
Console     OSS Console Channel Driver               no           yes          no
MGCP        Media Gateway Control Protocol (MGCP)    yes          yes          no
Local       Local Proxy Channel Driver               yes          yes          no
Zap         DAHDI Telephony Driver w/PRI             no           yes          no
Agent       Call Agent Proxy Channel                 yes          yes          no
----------
7 channel drivers registered

core*CLI> core show channeltype Local
-- Info about channel driver: Local --
Device State: yes
Indication: yes
Transfer : no
Capabilities: -1
Digit Begin: yes
Digit End: yes
Send HTML : yes
Image Support: no
Text Support: yes
core*CLI>
Создаем call-файл уже чуть другого содержания:
Channel: Local/1@autocaller
Callerid: 9998877
Context: outgoing_to_89161112233
Extension: s
Priority: 1
Где autocaller это название контекста, а 1-ца это приоритет в этом контексте. Соответственно делаем в extensions.conf этот контекст:
[autocaller]
exten => 1,1,Macro(call6003user,6003)
Тем самым мы как бы набираем уже не номер, а конкретный exten  в контексте и в нем передаем вызов в макро, создаем и макро, которое используется в этом контексте:
[macro-call6003user]
exten => s,1,Dial(SIP/${ARG1},15,t)
exten => s,n,Goto(s-${DIALSTATUS},1)
exten => s-NOANSWER,1,Dial(SIP/89161112233@sip-provider)
exten => s-NOANSWER,n,VoiceMail(${ARG1},u)
exten => s-NOANSWER,n,Hangup()
exten => s-BUSY,1,VoiceMail(${ARG1},b)
exten => s-BUSY,n,Hangup()
exten => _s-.,1,Goto(s-NOANSWER,1)
Собственно этот макро дает нам возможность уже управлять куда и в каком случае пойдет вызов. Пример конечно «грубый», но главное ведь принцип, а дальше уже ваше творчество ;)
И вот таким образом мы получаем искомый результат.
[Конец добавленного 11.02.2011]

Callback

Он работает по тому же принципу, что и описанный выше автообзвон, за некоторыми исключениеми:
  • генерация call файла происходит при звонке человека на номер выделенный под callback.
  • в контексте (context) мы не просто проигрываем звуковые, а предлагаем пользователю ввести номер, на который он хочет позвонить
Что нам потребуется в этом случае:
  • выделить номер для callback и создать контекст (context) под него в  extensions.conf.
  • «передать»  звонок в AGI скрипт, который проверит АОН звонящего на предмет разрешен ли этому номеру callback
  • если  callback разрешен, то сформирует call файл.
AGI — это Asterisk Gateway Interface.
Asterisk Gateway Interface это возможность расширить функционал Asterisk`а с помощью использования скриптов на многих языках программирования. Например Perl, PHP, C, Pascal, Bourne Shell.
Начнем с номера, допустим это будет гор. номер 84955556677. Добавим в extensions.conf в контекст (context) куда поступают все входящие звонки:
exten => 84955556677,1,AGI(sms-callback.php)
exten => 84955556677,n,Hangup()
Т.е. звонок на экстеншн (ext) 84955556677 будет передан AGI скрипту callback.php, а затем звонок будет сразу же завершен.
AGI скрипты для Asterisk располагаются в директории  /usr/local/share/asterisk/agi-bin/.
Почему скрипт на PHP ? PHP был выбран для AGI, т.к. callback является неотъемлимой частью проекта написанного на PHP + MySQL, потому решили и AGI пусть на PHP будет.
Сам скрипт: http://subnets.ru/files/callback.php.txt
Что происходит с скрипте ?
  • определяем необходимые переменные
  • получаем данные от Asterisk`а.
  • логи, куда же без них. В лог мы поместим все то, то получил AGI скрипт от Asterisk`а, это для дебага, потом можно выключить логирование.
  • проверяем номер (АОН) звонящего на доступ к callback
  • смотрим нет ли уже call файла для  данного номера, если нет, то создаем его
В переменной $agivar['agi_callerid'] и содержится АОН (телефонный номер) звонящего переданного нам Asterisk`ом, что в других передаваемых переменных вы сможете увидеть в log файле.
Если установить $debug в ноль, то тем самым выключите логирование.
В массиве  $permit_num содержатся номера для которых разрешен callback.
Ну и осталось дело за малым, а именно что бы был контекст (context) под звонящий номер в extensions.conf.
Допустим наш callback номер 84955556677 набрали с мобильного 89105647899 и ему разрешен callback (номер84955556677 указан в массиве  $permit_num в скрипте), то его контекст может выглядеть вот так:
[outgoing_to_89105647899]
exten => s,1,Set(CallbackNumTries=0)
exten => s,n,Set(CALLERID(num)="9998877")
exten => s,n(start),Background(vo-vvedite-ton-num-abonent)
exten => s,n,WaitExten(10)
exten => 100,1,Dial(SIP/8495${EXTEN}@sip-provider,120)
exten => _XXXXXXX,1,Dial(SIP/8495${EXTEN}@sip-provider,120)
exten => _8X.,1,Dial(SIP/${EXTEN}@sip-provider,120)
exten => i,1,Set(CallbackNumTries=$[${CallbackNumTries} + 1])
exten => i,n,Playback(vo-nomer-nabran-ne-verno)
exten => i,n,GotoIf($["${CallbackNumTries}" < "3"]?s|start)
exten => i,n,Playback(vo-dosvidaniya)
exten => t,1,Set(CallbackNumTries=$[${CallbackNumTries} + 1])
exten => t,n,GotoIf($["${CallbackNumTries}" < "3"]?s|start)
exten => t,n,Playback(vo-dosvidaniya)
exten => t,n,Hangup()
  • устанавливаем число попыток в ноль
  • выставляем АОН (под исх. звонок)
  • проигрываем записанный текст: «переведите ваш телефон в тональный набор…. бла… бла.. бла…»
  • 10 сек. ждем от человека набора номера
  • указываем возможный набор номеров: 100 (узнать время ;) ), 7-ми значный номер, 8-рка и далее сколько угодно цифр
  • даем человеку три попытки на набор номера, если истек таймаут набора номера или номер был набран не верно, то человеку это озвучивается и кол-во попыток увеличивается на 1-цу и наше мини голосовое меню начинается сначала (возврат к метке start)
  • если кол-во попыток исчерпано, то Asterisk вешает трубку
Источник http://subnets.ru/blog/?p=1444

Комментарии

Популярные сообщения из этого блога

Управление исходящей маршрутизацией вызовов Asterisk FreePBX

В данной статье рассматривается один из способов настройки исходящей маршрутизации средствами FreePBX с использованием шаблонов.  Управление исходящей маршрутизацией осуществляется на вкладке Connectivity > Outbound Routes Основными полями здесь являются: Route Name — имя маршрута, Dial patterns that will use this Route — здесь перечисляются шаблоны, используемые в данном маршруте, Trunk Sequence for Matched Routes — указываются маршруты используемые на этом направлении. В принципе этих полей достаточно для нормального функционирования исходящей маршрутизации. Шаблон — номер, некоторые цифры которого заменяются подстановочными символами и группами символов. Можно использовать следующие символы:  X — любая цифра 0 — 9; Z — любая цифра 1 — 9; N — любая цифра 2 — 9; [123] — любая из цифр, перечисленных в квадратных скобках. . — одна или неск...

Автообзвон на Asterisk (FreePBX)

Syntax of call files Specify where and how to call Channel : <channel>: Channel to use for the call. CallerID : «name» <number> Caller ID, Please note: It may not work if you do not respect the format: CallerID: «Some Name» <1234> MaxRetries : <number> Number of retries before failing (not including the initial attempt, e.g. 0 = total of 1 attempt to make the call). Default is 0. RetryTime : <number> Seconds between retries, Don't hammer an unavailable phone. Default is 300 (5 min). WaitTime : <number> Seconds to wait for an answer. Default is 45. Account : Set the account code to use. If the call answers, connect it here: Context : <context-name> Context in extensions.conf Extension : <ext> Extension definition in extensions.conf Priority : <priority> Priority of extension to start with Set : Set a variable for use in the extension logic (example: file1=/tmp/to ); in Asterisk 1.0.x use...

.call files Asterisk-Настройка call back для ТАКСИ

.call файлы — Текстовый файл соответствующий определенному синтаксису Синтаксис .call файлов Channel :  : Канал, который будет использоваться для исходящего вызова. CallerID: Name  CallerID, MaxRetries:  Количество попыток для неудачного вызова (не включая первую попытку, т.е. 0 = означает совершить 1 попытку вызова). Значение по умолчанию: 0. RetryTime:  Количество секунд между попытками вызова, не стоит очень часто ломиться на недоступный телефон. Значение по умолчанию: 300 (5 минут) WaitTime:  Количество секунд для ожидания ответа на вызов. Значение по умолчанию: 45. Account: Установка поля “account code”для записи в CDR. Если первый участник вызова ответил, то далее описываем с кем и как его соединить Context:  Контекст в файле extensions.conf. Extension:  Название екстеншена в extensions.conf.. Priority:  Номер приоритета для екстеншена, с которого нужно начать выполнение. Set: Установка переменных канала для использования их в логике обраб...