Как стать автором
Обновить

Как легко и просто научить ваш Asterisk звонить через нужного оператора

Время на прочтение7 мин
Количество просмотров33K
Приветствую тебя, %username%!

Сегодня мне хотелось бы поделиться решением, позволяющим научить ваш Asterisk автоматически маршрутизировать звонки по соответствующим направлениям, не прибегая к громоздким регулярным выражениям.

image

Практика показывает, что все больше и больше компаний начинают задумываться о своих расходах на связь. Львиную часть расходов при этом составляют вызовы на мобильные номера. Отсюда и родилась задача обрабатывать исходящие вызовы и направлять через ту линию, где звонок будет совершен бесплатно или за наименьшую стоимость.

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

В качестве шлюза выступал OpenVox VoxStack VS-GW1202-4G, в который предварительно были вставлены сим-карты известных вам операторов и настроены 3 транка до Asterisk.

Безусловно, путей решения проблемы великое множество, но мне хотелось бы остановиться на решении, которое я счел оптимальным: автоматическая сверка номеров из базы MySQL и дальнейшее перенаправление на нужную линию. Реализация довольно проста, хотя местами может показаться неказистой.

Разбить все работы можно на несколько этапов:
  1. Создать и заполнить базу данных
  2. Подготовить Asterisk к работе с БД
  3. Написать диалплан для обработки исходящих вызовов


Подготовка базы



Для начала необходимо создать отдельную базу (опционально):

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="\r\n"

Выкачиваем список кодов мобильных операторов с сайта Россвязи, преобразуем кодировку и заливаем в нашу базу. Путь до файла опционален.

База заполнена, но чтобы избежать проблем с кодировками внесем небольшие изменения: т.к. мне необходимо отбирать номера большой тройки для звонков в домашнем регионе, то я меняю только их, регион помечаю как 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 install


Смотрим где лежат драйвера для 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

Скрытый текст
aster*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

Создаем контекст для исходящих звонков.
Скрытый текст
[from-users]

exten => _8XXXXX.,1,NoOp(Call from ${CALLERID(num)} to ${EXTEN})
same => n,Set(TARGETNO=${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



Создаем контексты для звонков на провайдеров мобильной связи, а так же для провайдера по-умолчанию. В настройке каждого транка я ограничил количество одновременных сессий до одной и добавил переадресацию вызова на транк по-умолчанию в случае занятости или недоступности линии.

Скрытый текст
[to-prov-trunk]
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,${TARGETNO},1)
exten => s-BUSY,1,Goto(to-prov-trunk,${TARGETNO},1)
exten => s-.,1,Hangup()



Ну и теперь непосредственно сам макрос:
Скрытый текст
[macro-seeknumber-odbc]
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 для ежемесячного апгрейда, но это будем считать домашним заданием ;)
Теги:
Хабы:
Всего голосов 16: ↑16 и ↓0+16
Комментарии9

Публикации

Истории

Ближайшие события

7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань