Приветствую тебя, %username%!
Сегодня мне хотелось бы поделиться решением, позволяющим научить ваш Asterisk автоматически маршрутизировать звонки по соответствующим направлениям, не прибегая к громоздким регулярным выражениям.
Практика показывает, что все больше и больше компаний начинают задумываться о своих расходах на связь. Львиную часть расходов при этом составляют вызовы на мобильные номера. Отсюда и родилась задача обрабатывать исходящие вызовы и направлять через ту линию, где звонок будет совершен бесплатно или за наименьшую стоимость.
В моем случае, необходимо было маршутизировать звонки на домашний регион операторов большой тройки через соответствующие сим-карты, вставленные в GSM-шлюз. Вместо выбранных операторов можно использовать любые другие направления по аналогии с сабжем.
В качестве шлюза выступал OpenVox VoxStack VS-GW1202-4G, в который предварительно были вставлены сим-карты известных вам операторов и настроены 3 транка до Asterisk.
Безусловно, путей решения проблемы великое множество, но мне хотелось бы остановиться на решении, которое я счел оптимальным: автоматическая сверка номеров из базы MySQL и дальнейшее перенаправление на нужную линию. Реализация довольно проста, хотя местами может показаться неказистой.
Разбить все работы можно на несколько этапов:
- Создать и заполнить базу данных
- Подготовить Asterisk к работе с БД
- Написать диалплан для обработки исходящих вызовов
Подготовка базы
Для начала необходимо создать отдельную базу (опционально):
CREATE DATABASE telcodes;
Рутовый доступ в нашей ситуации лучше не давать, т.к. в этом нет необходимости, да и дырка в безопасности в виде рутового логина и пароля к MySQL в открытом виде в конфигурационном файле никому не нужна. Поэтому даем доступ произвольному пользователю только к нашей БД (в моем случае пользователь: «mobile», пароль: «heavypass»)
CREATE DATABASE telcodes;
GRANT ALL ON telcodes.* TO mobile@localhost IDENTIFIED BY 'mobile';
FLUSH PRIVILEGES;
SET PASSWORD FOR 'mobile'@'localhost'=PASSWORD('heavypass');
Создаем таблицу:
CREATE TABLE telcodes (
code smallint(3) NOT NULL,
begin int(11) NOT NULL,
end int(11) NOT NULL,
ammount int (11) NOT NULL,
operator varchar(100) NOT NULL,
region varchar(100) NOT NULL)
DEFAULT CHARSET=utf8;
А теперь можно перейти непосредственно к заполнению. Каждый волен сам выбирать для себя способ, я остановлюсь на следующем:
wget http://www.rossvyaz.ru/docs/articles/Kody_DEF-9kh.csv
cat Kody_DEF-9kh.csv | iconv -f pt154 -t utf8 >> telcodes.csv
mysqlimport -umobile -p telcodes /usr/src/telcodes.csv --fields-terminated-by=';' --lines-terminated-by="rn"
Выкачиваем список кодов мобильных операторов с сайта Россвязи, преобразуем кодировку и заливаем в нашу базу. Путь до файла опционален.
База заполнена, но чтобы избежать проблем с кодировками внесем небольшие изменения: т.к. мне необходимо отбирать номера большой тройки для звонков в домашнем регионе, то я меняю только их, регион помечаю как home, а названия меняю на beeline, mts и megafon соответственно:
UPDATE telcodes set region = 'home',operator = 'beeline' where operator = 'Вымпел-Коммуникации' and region = 'Москва и Московская область';
UPDATE telcodes set region = 'home',operator = 'mts' where operator = 'Мобильные ТелеСистемы' and region = 'Москва и Московская область';
UPDATE telcodes set region = 'home',operator = 'megafon' where operator = 'МегаФон' and region = 'Москва и Московская область';
База данных готова к использованию.
Подготовка asterisk
Соединять Asterisk с БД будем при помощи ODBC, т.к. считаю это унифицированным решением. Для начала необходимо пересобрать Asterisk с поддержкой ODBC. В качесте ОС в моем случае выступает Ubuntu Server 12.04.
Ставим пакеты unixodbc, unixodbc-dev и libmyodbc
apt-get install unixodbc unixodbc-dev libmyodbc
В каталоге с исходниками Asterisk выполняем:
./configure && make menuselect
Проверяем что модуль res_odbc доступен и включен, в функциях надо проверить доступность func_odbc. Затем сохраняем и выполняем:
make && make installl
Смотрим где лежат драйвера для ODBC
# dpkg -L libmyodbc
/.
/usr
/usr/lib
/usr/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu/odbc
/usr/lib/x86_64-linux-gnu/odbc/libmyodbc.so #Запоминаем месторасположение
/usr/share
/usr/share/libmyodbc
/usr/share/libmyodbc/odbcinst.ini
/usr/share/doc
/usr/share/doc/libmyodbc
/usr/share/doc/libmyodbc/README.Debian
/usr/share/doc/libmyodbc/changelog.Debian.gz
/usr/share/doc/libmyodbc/examples
/usr/share/doc/libmyodbc/examples/odbc.ini
/usr/share/doc/libmyodbc/copyright
Смотрим где лежат конфигурационные файлики для ODBC
#odbcinst -j
unixODBC 2.2.14
DRIVERS............: /etc/odbcinst.ini
SYSTEM DATA SOURCES: /etc/odbc.ini
FILE DATA SOURCES..: /etc/ODBCDataSources
USER DATA SOURCES..: /root/.odbc.ini
SQLULEN Size.......: 8
SQLLEN Size........: 8
SQLSETPOSIROW Size.: 8
Правим /etc/odbcinst.ini, добавляем в файл запись:
[MySQL]
Description = MySQL driver
Driver = /usr/lib/x86_64-linux-gnu/odbc/libmyodbc.so #указываем запомненное местоположение
Setup = /usr/lib/x86_64-linux-gnu/odbc/libodbcmyS.so #там же лежит файл libodbcmyS.so
CPTimeout =
CPReuse =
Описываем подключение к БД в файлe /etc/odbc.ini
[telcodes-mysql]
Driver = MySQL
Description = MySQL telcodes via ODBC
Server = localhost #адрес сервера, где стоит база
Port = 3306
User = mobile #Созданный ранее пользователь
Password = heavypass #Пароль пользователя
Database = telcodes #База данных, где будут храниться
Socket = /var/run/mysqld/mysqld.sock
А теперь непосредственно знакомим Asterisk с MySQL при помощи ODBC. Вносим изменения в /etc/asterisk/res_odbc.ini
[telcodes]
enabled => yes
dsn => telcodes-mysql
username => mobile
password => heavypass
pooling => no
limit => 1
pre-connect => yes
и прописываем загрузку модуля в /etc/asterisk/modules.conf
preload => res_odbc.so
Теперь можно перезапустить сам Asterisk, ну или только загрузить модуль res_odbc.so. После чего в CLI выполняем команду:
odbc show all
ODBC DSN Settings
— Name: telcodes
DSN: telcodes-mysql
Last connection attempt: 1970-01-01 03:00:00
Pooled: No
Connected: Yes
На этом второй этап можно считать завершенным, мы создали и заполнили БД и подружили её с Asterisk.
Написание диалплана
Ну а теперь можно приступить непосредственно к написанию диалплана для обработки исходящих вызовов.
Каждый набранный номер обрабатывается макросом, в результе работы которого будет определена линия, через которую будет совершен вызов. В качестве аргумента передаваемого макросу будет выступать набираемый номер. Первый символ (8) будет отрезаться еще до попадания в макрос, после чего в SQL запросе мы будем отбирать дальнейшие первые 3 символа (123) для сравнения с кодом оператора и оставшиеся 7 символов (4567890) для определения принадлежности к той или иной номерной емкости.
Все работы с БД прописываются в конфигурационном файле func_odbc.conf. Здесь мы придумываем и составляем функции для взаимодействия с БД, а затем используем их результат в диалплане. Каждый раздел описывает свою функцию, название каждого раздела принято заполнять в верхнем регистре.
В моем случае необходимо определить принадлежность номера к региону и оператору и передать результат для дальнейшей обработки макросом.
Добавляем в /etc/asterisk/func_odbc.conf запись
[MOBILE]
dsn=telcodes-mysql
read=SELECT operator,region FROM `telcodes` WHERE ${ARG1:3:9} BETWEEN `begin` AND `end` AND `code`=${ARG1:0:3}
Стоит запомнить, что результатом запроса будут служить 2 значения, разделенных запятой: оператор, обслуживающий данный номер, а так же регион обслуживания.
Ну а теперь можно непосредственно написать сами правила обработки номера. В примере буду опускать лишние правила, оставлю только то, что относится к делу. Все изменения производим в файле /etc/asterisk/extensions.conf
Создаем контекст для исходящих звонков.
exten => _8XXXXX.,1,NoOp(Call from ${CALLERID(num)} to ${EXTEN})
same => n,Macro(seeknumber-odbc,${EXTEN:1:10})
same => n,Noop(${TRUNK_MOB})
same => n,Goto(to-${TRUNK_MOB},${EXTEN},1)
same => n,Hangup
Создаем контексты для звонков на провайдеров мобильной связи, а так же для провайдера по-умолчанию. В настройке каждого транка я ограничил количество одновременных сессий до одной и добавил переадресацию вызова на транк по-умолчанию в случае занятости или недоступности линии.
exten => _8XXXXX.,1,NoOp(Call from ${CALLERID(num)} to ${EXTEN} other)
same => n,Dial(SIP/prov-trunk/${EXTEN},140,Tt)
same => n,Hangup()
[to-beeline]
exten => _8XXXXX.,1,NoOp(Call from ${CALLERID(num)} to ${EXTEN} beeline)
same => n,Dial(SIP/beeline/${EXTEN},140,Tt)
same => n,Goto(status,s-${DIALSTATUS},1)
[to-megafon]
exten => _8XXXXX.,1,NoOp(Call from ${CALLERID(num)} to ${EXTEN} megafon)
same => n,Dial(SIP/megafon/${EXTEN},140,Tt)
same => n,Goto(status,s-${DIALSTATUS},1)
[to-mts]
exten => _8XXXXX.,1,NoOp(Call from ${CALLERID(num)} to ${EXTEN} mts)
same => n,Dial(SIP/mts/${EXTEN},140,Tt)
same => n,Goto(status,s-${DIALSTATUS},1)
[status]
exten => s-ANSWER,1,Hangup()
exten => s-CHANUNAVAIL,1,Goto(to-prov-trunk,1)
exten => s-BUSY,1,Goto(to-prov-trunk,1)
exten => s-.,1,Hangup()
Ну и теперь непосредственно сам макрос:
exten => s,1,NoOp( == tel nomber == ${ARG1:0:3} == ${ARG1:3:9} == )
same => n,Set(result=${ODBC_MOBILE()})
same => n,Set(operator=${CUT(result,,,1)})
same => n,Set(region=${CUT(result,,,2)})
same => n,NoOp(name of region = ${region})
same => n,NoOp(name of operator = ${operator})
same => n,Set(TRUNK_MOB=prov-trunk)
same => n,ExecIf($["${operator}"=«beeline»&"${region}"=«home»]?Set(TRUNK_MOB=beeline))
same => n,ExecIf($["${operator}"=«mts»&"${region}"=«home»]?Set(TRUNK_MOB=mts))
same => n,ExecIf($["${operator}"=«megafon»&"${region}"=«home»]?Set(TRUNK_MOB=megafon))
В итоге получилось, на мой взгляд, неплохое унифицированное решение для распределения вызовов на нужные направления по номеру телефона. В дополнение скажу, что его можно автоматизировать еще больше, написав bash-скрипт на загрузку и обработку свежих баз с сайта РосСвязи и добавив его в crontab для ежемесячного апгрейда, но это будем считать домашним заданием ;)
Автор: Amalgamm