Самый верный способ сэкономить на звонках – использовать IP-телефонию. Но, к сожалению, даже если установить на мобильный телефон какой-нибудь Skype-клиент, ты сможешь использовать его только при наличии Wi-Fi или 3G. Чтобы обойти эту привязанность к интернету, можно организовать систему callback, которая будет звонить обоим абонентам по VoIP и связывать их между собой. В этом случае нет необходимости ни в интернете, ни в продвинутом телефоне, ни даже в VoIP-клиенте.

Система callback существует давно и предлагается многими VoIP-компаниями. Можно зайти на сайт, ввести два номера телефона, и специальный сервис позвонит обоим абонентам, чтобы соединить их между собой. Я даже пользовался когда-то этим с мобильного телефона, открывая страницу такого сервиса в веб-браузере. Этим можно воспользоваться разок где-нибудь за границей в роуминге, но использовать постоянно – нет. Совсем другое дело – организовать удобный сервис саму. Схема такая: ты звонишь на определенный номер, где установлен специальный сервер, тот сбрасывает звонок и сам перезванивает. Тебе остается набрать специальный PIN-код (выполнить авторизацию) и номер для звонка, после чего дожидаться соединения. Это называется обратным звонком, или callback’ом. Штука удобная и довольно простая в организации.

 

Мини-АТС

Если при слове «Мини-АТС» у тебя возникает ассоциация с жутко дорогостоящим оборудованием, которое устанавливается в офисах, оно ошибочно. Помимо аппаратных АТС’ок, огромное распространение получили программные продукты, в том числе бесплатный сервер Asterisk. Собственно, Asterisk будет сердцем нашей системы. Про базовую установку сервера и первичную настройку, чтобы все заработало, у нас были две статьи. PDF-версии ты найдешь на диске, а также можешь прочитать их на сайте (www.xakep.ru/magazine/xa/107/152/1.asp и www.xakep.ru/magazine/xa/108/154/1.asp). Я не рекомендую использовать для наших целей сборки типа TrixBox, Elastix и т.д.; проще будет установить и настроить все вручную. Но повторять то, о чем мы писали отдельные статьи, я сейчас не буду.

Итак, предположим, что Asterisk у нас есть. Первое, что нужно сделать – это купить и зарегистрировать на Asterisk местный городской номер, который отдается по SIP. На него мы будем звонить. Можно, конечно, не покупать SIP-номер, а использовать обычный аналоговый, который приходит по меди. Но тогда придется докупить VoIP-шлюз с FXO-портом, а с ним могут возникнуть проблемы: на древних и не очень древних АТС не всегда работает определение Caller ID, которое нам очень нужно. Да и вообще, дополнительное звено в цепочке только понизит надежность системы. По этой причине SIP-номер, безусловно, предпочтительнее. Следующий шаг – покупка (и настройка) аккаунта у VoIP-провайдера, через который мы будем звонить. Можно купить несколько и при звонках за рубеж использовать один, в Москву – другой, по России – третий. Выбор большой.

Еще пара замечаний. В качестве номера можно использовать свой сотовый номер, только для подключения его к Asterisk потребуется VoIP-GSM шлюз, а они стоят дорого: примерно от 5000 рублей за порт. Есть обходной путь – использовать для этого 3G-модем, стоимость которого не превышает 1000 рублей. Для сотового телефона желательно прикупить любой SIM Dialer, который позволит пользоваться такой схемой звонков максимально удобно – по сути, звонящему нужно будет только выбрать контакт в записной книге, а звонок и запрос к callback-системе будет произведен автоматически. Подробнее о VoIP-GSM шлюзе и SIM Dialer’е ты можешь прочитать во врезках.

 

Обработка звонков

Теперь, когда все приготовления выполнены, Asterisk настроен по инструкциям из статей, можно приступать к организации нашего сервиса. И начнем мы с того, что поменяем так называемый контекст. Для этого открываем на Астериске файл /etc/asterisk/extensions.conf (в этом файле описывается план набора, то есть как будут себя вести все входящие и исходящие вызовы) и находим контекст, в который приходят все входящие извне звонки: у меня он называется [fromgorod]. При входящем звонке система будет определять номер звонящего, и, если он есть в «списке», звонок будет отправляться на голосовое меню (IVR), в котором будет предложено набрать PIN-код, а далее – номер для звонка. Пусть мой городской номер 310309:

[fromgorod]
exten => 310309,1,NoOp(zvonyat s nomera ${CALLERID(all)})
exten => 310309,n,NoOp(${STRFTIME(${EPOCH},,%d.%m.%Y-%H:%M:%S)})
exten => 310309,n,GoToIf($["${CALLERID(number)}" = "8901234567"]?ivr,s,1)
exten => 310309,n,Answer() ;Отвечаем
......

Функция NoOp позволяет вывести в консоль Asterisk текст или состояние переменной. Первая строка выводит в консоль Caller ID звонящего, а вторая – дату и время звонка. Для работы системы это не нужно, но при отладке очень полезно. Строка «exten => 310309,n,GoToIf($["${CALLERID(number)}" = "8901234567"]?ivr,s,1)» – это неполное ветвление, оно проверяет, с какого номера пришел вызов. Если с номера 8901234567, то вызов уходит в контекст IVR; если же номер другой, тогда обработка вызова пройдет по обычной схеме. Обрати внимание, что номер может приходить без 8 в начале.

Если callback-системой будет пользоваться пара человек, то прописать под каждый их номер еще одну строчку в конфиге не будет большой проблемой. Но что, если их будет 50? Изящнее всего прописать всех пользователей в специальной базе данных. Вместе с Asterisk часто используют MySQL, чтобы записывать в нее логи звонков – CDR. В результате на сервере создается база Asterisk, в которой есть таблица CDR. Мы в этой базе создадим еще одну таблицу – callback. Для этого в консоли набираем «mysql -u asterisk -p asterisk», далее указываем пользователя, таблицу и запрос на ввод пароля. После ввода пароля создаем таблицу (телефон, PIN-код, переменная callback, имя) и заполняем параметрами одного из пользователей:

CREATE TABLE `callback` (
`phone` varchar(80) NOT NULL default '',
`pin` int(11) NOT NULL default '4321',
`callback` int(11) NOT NULL default '0',
`user` varchar(255) NOT NULL default ''
);
INSERT INTO callback(phone, pin, user) values('8901234567', '2602', 'Aggressor');

Итак, база с данными есть, как же Asterisk узнает об этом? Все достаточно просто, нужно дополнить наш контекст [fromgorod]:

exten => 310309,1,NoOp(zvonyat s nomera ${CALLERID(all)})
exten => 310309,n,NoOp(${STRFTIME(${EPOCH},,%d.%m.%Y-%H:%M:%S)})
exten => 310309,n,MYSQL(Connect connid localhost asterisk asterisk asterisk)
exten => 310309,n,MYSQL(Query resultid ${connid} select pin, callback from callback where phone=${CALLERID(number)})
exten => 310309,n,MYSQL(Fetch fetchid ${resultid} pin callback)
exten => 310309,n,NoOp(pin -> ${pin} callback# -> ${callback})
exten => 310309,n,MYSQL(Clear ${resultid})
exten => 310309,n,MYSQL(Disconnect ${connid})
exten => 310309,n,GoToIf($["${pin}" != ""]?ivr-pass,s,1)
exten => 310309,n,Answer() ;Отвечаем

Каждая строчка, по сути, говорит сама за себя: сначала обращаемся к базе, далее с помощью SQL-запроса получаем параметры для номера звонящего абонента и обрабатываем их. Непонятной может показаться последняя строчка «GoToIf($["${pin}" != ""]?ivr-pass,s,1)». Если в результате запроса номер найдется в базе, то переменная pin будет не пустой, и тогда дальше обработка вызова пойдет в контексте ivr-pass.

 

Настраиваем IVR

Итак, номер пользователя определяется и сверяется с базой данных. Что дальше? Необходимо проиграть ему инструкции, как ввести PIN-код, чтобы произвести авторизацию пользователя, и обработать вход. Голосовые меню, которые взаимодействуют с пользователем, называются IVR. После того, как номер абонента определился, звонок перекидывается на контекст ivr-pas:

[ivr-pass]
exten => s,1,Background(WelcomePass) ;
exten =>s,n,WaitExten(10)
exten => _XXXX,1, GoToIf($["${EXTEN}" = "${pin}"]?ivr,s,1)
exten => _XXXX,n,Hangup
exten => t,1,Hangup
exten => i,1,Hangup

Здесь мы воспроизводим ролик WelcomePass (его необходимо предварительно скопировать в /var/lib/asterisk/sounds/ru). Потом ждем выбора пользователя 10 сек. Если за 10 секунд никакой номер не введен, то кладем трубку: exten => t,1,Hangup. Если введен PIN не больше четырех символов, опять же, кладем трубку: exten => i,1,Hangup. Никто не мешает, к примеру, вместо Hangup прописать возможность еще пару раз ввести PIN, и только после третьей неудачной попытки класть трубку. Так или иначе, если были введены четыре символа, которые совпадают с PIN-кодом, то мы переходим в контекст ivr.

[ivr]
exten =>s,1,Set (inum=0)
exten =>s,n,Set (tnum=0)
exten => s,n,Background(Welcome)
exten =>s,n,WaitExten(10)
exten => 1,1,GoTo(ivr-out,s,1)
exten => 2,1,GoTo(ivr-ch-pin,s,1)
exten => i,1,Playback(pbx-invalid)
exten => i,n,Set(inum=$[${inum} + 1])
exten => i,n,GotoIf($["${inum}" < "3"]?s,1)
exten => i,n,Hangup()
exten => t,1,Set(tnum=$[${tnum} + 1])
exten => t,n,GotoIf($["${tnum}" < "3"]?s,1)
exten => t,n,Hangup()

Контекст ivr начинается с обнуления двух переменных inum и tnum – это количество неверных попыток ввода и количество прошедших таймаутов. При каждом неверном вводе воспроизводится стандартный ролик pbx-invalid, а переменная inum увеличивается на 1. После трех ошибок кладется трубка, то же самое происходит и с переменной tnum. Далее воспроизводится ролик Welcome, за ним ожидаем ввод номера для звонка. В нашем меню две опции: 1 – позвонить и 2 – сменить PIN-код:

[ivr-out]
exten => s,1,Set (inum=0)
exten => s,n,Set (tnum=0)
exten => s,n,Background(beep)
exten => s,n,WaitExten(10)
exten => 89XXXXXXXXX,1,Dial(SIP/bla1/${EXTEN}
exten => 89XXXXXXXXX ,n,Hangup
exten => 8495XXXXXXX,1,Dial(SIP/bla2/${EXTEN}
exten => 8495XXXXXXX ,n,Hangup
exten => 8[2-8]XXXXXXXXX,1,Dial(SIP/blabla3/${EXTEN}
exten => 8[2-8]XXXXXXXXX ,n,Hangup
exten => i,1,Playback(pbx-invalid)
exten => i,n,Set(inum=$[${inum} + 1])
exten => i,n,GotoIf($["${inum}" < "3"]?s,1)
exten => i,n,Hangup()
exten => t,1,Set(tnum=$[${tnum} + 1])
exten => t,n,GotoIf($["${tnum}" < "3"]?s,1)
exten => t,n,Hangup()

[ivr-ch-pin]
exten => s,1,Background(beep)
exten => s,n,WaitExten(10)
exten => _XXXX,1,MYSQL(Connect connid localhost asterisk asterisk asterisk)
exten => _XXXX,n,MYSQL(Query resultid ${connid} update callback set `pin`=${EXTEN} where phone=${CALLERID(number)})
exten => _XXXX,n,MYSQL(Disconnect ${connid})
exten => _XXXX,n,Hangup()
exten => i,1,Hangup()
exten => t,1,Hangup()

В контексте ivr-out прописаны исходящие звонки. Вначале воспроизводится стандартный «бииип», после которого можно набирать номер для звонка. В конфиге у нас прописаны три направления: сотовые, Москва и межгород; каждое направление соединяется через определенный транк (аккаунт VoIP-провайдера): blabla1, blabla2 или blabla3. Можно обойтись одним, но для каждого направления можно выбрать наиболее выгодного VoIP-оператора, этим мы и воспользовались.

В контексте ivr-ch-pin, который отвечает за смену PIN’а: сначала воспроизводится «бииип», после чего дается 10 сек на ввод нового PIN’а. Когда новый PIN введен, происходит подключение к базе и обновление PIN-кода в таблице.

 

Call-файлы в Asterisk’е

Собственно, с этого момента система уже работает. Мы звоним на наш номер, авторизуемся с помощью PIN-кода, далее вводим номер телефона, на который Asterisk и перенаправляет наш звонок. Тестовый звонок… да, все работает! Однако в самом начале статьи мы говорили о том, что наша callback-система должна сама перезванивать, чтобы мы тратились на исходящие звонки с сотового. Как же это сделать?

В Астериске есть так называемые call-файлы, которые позволяют инициировать вызов и соединять два номера. Создаем конфиг и заполняем следующим:

Channel: SIP/blabla1/8901234567
MaxRetries: 2
RetryTime: 3
WaitTime: 20
Context: ivr-pass
Extension: s
Priority: 2
Archive: Yes

Что за… и за что отвечает:

  • Channel – указывает тип, название транка и номер телефона;
  • MaxRetries – параметр определяет количество попыток дозвона. Как только они будут исчерпаны, файл удалится;
  • RetryTime – время между повторениями;
  • WaitTime – этот параметр указывает, сколько времени необходимо ждать поднятия трубки до того, как прекратить попытку дозвониться;
  • Context – это контекст, выполнение которого начнется после поднятия трубки;
  • Extension – это номер в контексте ivr-pass, который будет набран, когда возьмут трубку (пишем тут s);
  • Priority – это приоритет экстеншина s, с которого начнется обработка (укажем 2)
  • Archive – если поставить Yes, тогда после выполнения call-файла в /var/spool/asterisk/outgoing_done можно будет посмотреть историю обработки вызова.

Если созданный файл переместить в /var/spool/asterisk/outgoing/, то Астериск сразу начнет звонить на номер 8901234567 (причем рекомендуется call-файл именно перемещать, а не копировать). Время каждой попытки дозвона – 20 секунд, после чего номер набирается заново, и так два раза. Если во время одной из попыток абонент возьмет трубку, то система попытается набрать экстеншен s в контексте callback.

 

Настраиваем callback

Добавить гибкости, подставляя нужный номер, можно при помощи AGI (AsteriskGatewayInterface), интерфейса взаимодействия с внешними скриптами. Внешний скрипт можно написать на Perl, PHP, C, Bash. Предлагаю написать нужный нам скрипт на Bash – это проще и быстрее всего, выглядеть он будет так:

#!/bin/bash
echo Channel: SIP/blabla1/$1 > /tmp/$2
echoMaxRetries: 2 >> /tmp/$2
echoRetryTime: 3 >> /tmp/$2
echoWaitTime: 20 >> /tmp/$2
echo Context: ivr-pass >> /tmp/$2
echo Extension: s >> /tmp/$2
echo Priority: 2 >> /tmp/$2
echo Archive: Yes >> /tmp/$2
mv /tmp/$2 /var/spool/asterisk/outgoing

Готовый файл называем callback.agi и перемещаем в /var/lib/asterisk/agi-bin. При вызове скрипта из контекста в Астериске ему будут переданы две переменных: номер телефона ($1 в скрипте), на который будем перезванивать, и имя call-файла ($2 в скрипте).

Когда мы создавали таблицу callback,то сделали в ней поле callback, которое по умолчанию равно 0. При входящих звонках мы получаем значение этого поля вмести с PIN-кодом. Если состояние этого поля не равно 0, то будем перезванивать. Отредактируем контекст ivr-pass и создадим новый callback:

[ivr-pass]
exten => s,1, GoToIf($["${callback}"! = "0"]?callback,s,1)
exten => s,n,Background(WelcomePass) ;
exten =>s,n,WaitExten(10)
exten => _XXXX,1, GoToIf($["${EXTEN}" = "${pin}"]?ivr,s,1)
exten => _XXXX,n,Hangup
exten => t,1,Hangup
exten => i,1,Hangup

[callback]
exten => s,1,AGI(callback.agi,${callback},${UNIQUEID})
exten =>s,n,hangup

Первая строка в [callback] запускает скрипт под названием callback.agi и передает две переменных: номер и UNIQUEID в качестве названия для call-файла. Таким образом и происходит обратный вызов.

 

Плюсы и минусы

Результат всех этих действий – полноценная callback-система. Ее плюсы очевидны: можно реально экономить при звонках, особенно за пределы своего города или в роуминге. Из минусов – при звонке будет теряться твой CallerID, у абонента вместо этого будет высвечиваться номер VoIP-оператора. Как вариант, можно найти такого VoIP-прова, который позволяет подставить свой (или даже произвольный) CallerID. Еще один минус системы – увеличенное время соединения. У меня при звонке через sim-dialer от момента вызова номера в контакт листе до «гудков» на набранный номер уходит примерно 20-25 секунд. Но я готов ждать :). В ближайших планах – прикрутить к системе биллинг и реализовать заказ звонка через сайт, продавая callback как услугу. 🙂

 

SimDialer

Использовать callback-систему в «голом» виде не всегда удобно. Ведь приходится вручную звонить на номер доступа, отвечать на звонок и вручную набирать PIN-код и номер для соединения. Я бы, возможно, и не стал браться за всю эту затею, если бы мой знакомый не заказал себе набор SimDialer. Он представляет собой набор из маленького чипа и контактов, которые накладываются поверх SIM-карты. После его установки в телефоне появляется новое SIM-меню. В меню указывается номер доступа к нашей системе и сам PIN-код. Важный пункт – bypassprefixes, в котором перечисляются номера телефонов, набор на которые должен осуществляться напрямую. В результате использовать callback-систему становится проще простого. Ты открываешь телефонную книгу, находишь нужный номер и просто звонишь. Если номер есть в bypassprefixes, то звонок пойдет через GSM, а если нет, то через номер доступа, причем SimDialer сам пошлет DTMF-сигналами в Астериск нужный номер и PIN-код. Увы, насколько бы тонким ни был чип, но все же не во все телефоны удастся его запихнуть. К тому же, на iPhone такая штука почему-то не заработала. В качестве альтернативы я нашел приложение CardCaller, которое предназначено для звонков по карточкам IP-телефонии. Оно умеет само вводить номер телефона и PIN-код, но, к сожалению, не поддерживает callback.

 

Настраиваем GSM-шлюз

В качестве номера доступа удобно использовать не только SIP-номер, но и обычный федеральный сотовый. Чтобы подключить сотовый номер к Астериску, нужен VoIP-GSM шлюз, для этого отлично подойдет 3G USB-модем HUAWEI Е1550, который активно продают операторы сотовой связи. С его использованием можно не только сделать традиционный callback, но и реализовать обратный вызов через SMS. Прежде чем подключить модем, нужно убедиться, что он поддерживает голос, это может определить прога MICRO-BOX HUAWEI MODEM UNLOCKER. Она же снимет привязку к определенному оператору.

Для нормальной работы нам потребуется ядро 2.6.32 и выше. Скачиваем и устанавливаем модуль для Asterisk (www.makhutov.org/svn/chan_datacard), который реализует работу с 3G-модемом. Далее проверяем, появился ли chan_datacard.so в /usr/lib/asterisk/modules. Появился? Хорошо. Руками копируем ./trunk/etc/datacard.conf в /etc/asterisk. В этом конфиге по умолчанию прописаны два устройства [datacard0] и [datacard1] – одно удаляем, оно нам не нужно. Меняем разъем, куда подключен шлюз, и контекст для него:

[datacard0]
audio=/dev/ttyUSB1
data=/dev/ttyUSB2
context=datacard-incoming
group=1
rxgain=3
txgain=3

Теперь сохраняет изменения, перезапускаем Астериск – он готов к работе. Можно прописать контекст и принимать/совершать звонки, а можно проверить баланс или отправить SMS:

CLI>datacardsms datacard0 89000000000 Hello!
CLI>datacardussd datacard0 *102#
[datacard0] Got USSD response: 'Баланс 155.49 р. Аня+Саша=любовь. Аутебя? Шли ИИмя+Имя на 5050 3р'

 

DVD

Полные версии конфигов из статьи ты найдешь на нашем диске.

Оставить мнение

Check Also

Карманные трояны. Как работают мобильные банкеры

Одним солнечным апрельским утром мой завтрак был прерван телефонным звонком приятеля — пре…