Автообзвон
Есть задачи реализация которых возможна через автоматический обзвон. Например ?
Например вы сис.админ, у вас есть локальная сеть и куча оборудования в ней, но вы же не 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 вешает трубку
З.Ы. При копировании статьи ссылка на источник ОБЯЗАТЕЛЬНА !
Автор: Николаев Дмитрий (virus (at) subnets.ru)