artem
18.11.2016
7509

Определение страны по номеру телефона в Asterisk

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

Определение страны по номеру телефона — важная задача телефонии, решение которой необходимо для реализации таких механизмов, как
— запрет/разрешение вызовов в определённые страны
— различная маршрутизация входящих вызовов, в зависимости от страны звонящего (проигрывание сообщений на различных языках)
— добавление названия страны звонящего к Caller ID

Формирование базы кодов стран

Для хранения кодов стран будет использоваться следующая таблица MySQL:

CREATE TABLE ‘country_tel_codes’ (
‘country_code’ smallint(6) NOT NULL,
‘region_code’ smallint(6) DEFAULT NULL,
‘country_name’ varchar(255) NOT NULL DEFAULT »,
‘region_name’ varchar(255) NOT NULL DEFAULT »,
KEY ‘country_code’ (‘country_code’),
KEY ‘region_code’ (‘region_code’)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

country_code — телефонный код страны
region_code — телефонный код региона страны
country_name — название страны
region_name — название региона страны
Указанных полей вполне достаточно для решения обозначенных задач.

Телефонный код региона и название региона будут использоваться только для различия телефонных номеров США и Канады (из-за особенностей плана нумерации стран Северной Америки).

Информацию для наполнения таблицы можно взять, например, здесь http://www.allareacodes.com/international_dialing_codes.htm, выделите на странице всю таблицу с кодами стран и вставьте её в какой-либо файл. Для внесения информации в таблицу, нужно преобразовать данные в формат CSV, можно сделать это с помощью следующего скрипта:

#!/bin/bash
export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
if [ «$1″ == » ]; then
echo ‘Please specify the codes file’
exit 1
fi
CODES_FILE=»$1″
if [ ! -r «$CODES_FILE» ]; then
echo «Couldn’t read ‘$CODES_FILE'»
exit 1
fi
while read -r line; do
COUNTRY_NAME=»${line%% *}»
[ «$COUNTRY_NAME» == ‘Country’ ] && continue
line=»${line#* }»
COUNTRY_CODE=»${line%% *}»
line=»${line#* }»
line=»${line#* }»
line=»${line#* }»
PHONE_FORMAT=»${line%% *}»
REGION_NAME=
REGION_CODE=
if ! [[ $PHONE_FORMAT =~ digit ]]; then
continue
fi
if [ «$COUNTRY_NAME» == ‘Canada’ ] || [ «$COUNTRY_NAME» == ‘United States’ ]; then
continue
fi
if [[ $PHONE_FORMAT == *(* ]]; then
REGION_NAME=»$COUNTRY_NAME»
[[ $COUNTRY_NAME == *,* ]] && COUNTRY_NAME=»»${COUNTRY_NAME}»»
[[ $REGION_NAME == *,* ]] && REGION_NAME=»»${REGION_NAME}»»
REGION_CODES=»${PHONE_FORMAT##*(}»
REGION_CODES=»${REGION_CODES%%)*}»
REGION_CODE=»${REGION_CODES%%/*}»
REGION_CODES=»${REGION_CODES#${REGION_CODE}}»
REGION_CODES=»${REGION_CODES#/}»
while [ «$REGION_CODE» != » ]; do
echo «${COUNTRY_CODE},${REGION_CODE},${COUNTRY_NAME},${REGION_NAME}»
REGION_CODE=»${REGION_CODES%%/*}»
REGION_CODES=»${REGION_CODES#${REGION_CODE}}»
REGION_CODES=»${REGION_CODES#/}»
done
else
REGION_NAME=»$COUNTRY_NAME»
[[ $COUNTRY_NAME == *,* ]] && COUNTRY_NAME=»»${COUNTRY_NAME}»»
[[ $REGION_NAME == *,* ]] && REGION_NAME=»»${REGION_NAME}»»
echo «${COUNTRY_CODE},${REGION_CODE},${COUNTRY_NAME},${REGION_NAME}»
fi
done <«$CODES_FILE»

Использовать скрипт можно так:

./process_codes.sh codes_raw >/tmp/countries.csv

Где «process_codes.sh» — имя скрипта, «codes_raw» — имя файла с исходной таблицей кодов, «countries.csv» — имя файла, в который будут записаны готовые данные для загрузки в таблицу MySQL.
У всех стран Северной Америки общий номерной план — 11 цифр, первая цифра — 1, по следующим трём цифрам можно определить страну принадлежности номера. Канаде и США соответствует достаточно большое количество трёхзначных кодов, идущих вперемешку друг с другом и с кодами других стран, поэтому коды регионов Канады и США будут обработаны отдельно.
Ниже приведено содержимое CSV-файла для загрузки телефонных кодов Канады в таблицу кодов стран (телефонные коды Канады взяты из Википедии https://en.wikipedia.org/wiki/Telephone_numbers_in_Canada)

cat <<«EOF123″ >/tmp/canada.csv
1,204,Canada,Manitoba
1,226,Canada,Ontario
1,236,Canada,British Columbia
1,249,Canada,Ontario
1,250,Canada,British Columbia
1,289,Canada,Ontario
1,306,Canada,Saskatchewan
1,343,Canada,Ontario
1,365,Canada,Ontario
1,403,Canada,Alberta
1,416,Canada,Ontario
1,418,Canada,Quebec
1,431,Canada,Manitoba
1,437,Canada,Ontario
1,438,Canada,Quebec
1,450,Canada,Quebec
1,506,Canada,New Brunswick
1,514,Canada,Quebec
1,519,Canada,Ontario
1,579,Canada,Quebec
1,581,Canada,Quebec
1,587,Canada,Alberta
1,600,Canada,Specialized Telecom Services
1,604,Canada,British Columbia
1,613,Canada,Ontario
1,639,Canada,Saskatchewan
1,647,Canada,Ontario
1,705,Canada,Ontario
1,709,Canada,Newfoundland and Labrador
1,778,Canada,British Columbia
1,780,Canada,Alberta
1,782,Canada,Nova Scotia and Prince Edward Island
1,807,Canada,Ontario
1,819,Canada,Quebec
1,867,Canada,»Yukon, Northwest Territories and Nunavut»
1,873,Canada,Quebec
1,902,Canada,Nova Scotia and Prince Edward Island
1,905,Canada,Ontario
EOF123

Список телефонных кодов США можно взять здесь http://www.allareacodes.com/area_code_listings_by_state.htm. Как и в случае с кодами стран, скопируйте таблицу со страницы и вставьте её в какой-либо файл, ниже приведён скрипт для преобразования этой таблицы в формат CSV:

#!/bin/bash
export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
if [ «$1″ == » ]; then
echo ‘Please specify the codes file’
exit 1
fi
CODES_FILE=»$1″
if [ ! -r «$CODES_FILE» ]; then
echo «Couldn’t read ‘$CODES_FILE'»
exit 1
fi
COUNTRY_NAME=’United States’
COUNTRY_CODE=1
while read -r line; do
REGION_NAME=»${line%% *}»
[[ $REGION_NAME == *,* ]] && REGION_NAME=»»${REGION_NAME}»»
REGION_CODES=»${line#* }»
while [[ $REGION_CODES == *, * ]]; do
CUR_CODE=»${REGION_CODES%%, *}»
REGION_CODES=»${REGION_CODES#*, }»
echo «${COUNTRY_CODE},${CUR_CODE},${COUNTRY_NAME},${REGION_NAME}»
done
echo «${COUNTRY_CODE},${REGION_CODES},${COUNTRY_NAME},${REGION_NAME}»
done <«$CODES_FILE»

Использовать скрипт нужно следующим образом:

./process_usa_codes.sh usa_raw >/tmp/usa.csv

Ещё одна проблемная страна — Казахстан, его код страны — 7, такой же, как и у России. Его внутренние телефонные номера начинаются с цифр 6 и 7, это будет проверено отдельным условием в диалплане.
Загружаем полученные CSV-фалы в таблицу и наша база кодов стран готова:

LOAD DATA INFILE ‘/tmp/countries.csv’ INTO TABLE ‘country_tel_codes’ FIELDS TERMINATED BY ‘,’ ENCLOSED BY ‘»‘ LINES TERMINATED BY ‘n’;
LOAD DATA INFILE ‘/tmp/canada.csv’ INTO TABLE ‘country_tel_codes’ FIELDS TERMINATED BY ‘,’ ENCLOSED BY ‘»‘ LINES TERMINATED BY ‘n’;
LOAD DATA INFILE ‘/tmp/usa.csv’ INTO TABLE ‘country_tel_codes’ FIELDS TERMINATED BY ‘,’ ENCLOSED BY ‘»‘ LINES TERMINATED BY ‘n’;

Диалплан для определения страны по номеру телефона

Для запросов в БД нужны две вспомогательные ODBC-функции, внесите их в файл /etc/asterisk/func_odbc.conf

[TEL_COUNTRY]
dsn=asteriskdb
readsql=SELECT ‘country_name’ FROM ‘country_tel_codes’ WHERE ‘country_code’=’${SQL_ESC(${ARG1})}’ LIMIT 1;
[TEL_COUNTRY_REGION]
dsn=asteriskdb
readsql=SELECT ‘country_name’ FROM ‘country_tel_codes’ WHERE ‘country_code’=’${SQL_ESC(${ARG1})}’ AND ‘region_code’=’${SQL_ESC(${ARG2})}’ LIMIT 1;

asteriskdb — имя вашего ODBC-подключения (настройка ODBC-подключения не будет описана в рамках этой статьи).
Функция ODBC_TEL_COUNTRY нужна для определения названия страны по коду страны, функция ODBC_TEL_COUNTRY_REGION нужна для определения названия страны по коду страны и коду региона.
После внесения изменений, выполните команду

asterisk -rx «module reload func_odbc.so»

Ниже приведён контекст для определения названия страны:

[sub-country-by-number]
exten => s,1,Set(CHECK_NUMBER=${ARG1})
same => n,Set(INTERNATIONAL_NUMBER=)
same => n,Set(COUNTRY_NAME=)
same => n,Set(REGION_CODE=)
same => n,GotoIf($[«${CHECK_NUMBER:0:3}»!=»810″]?cont10)
same => n,Set(INTERNATIONAL_NUMBER=${CHECK_NUMBER:3})
same => n,Goto(cont30)
same => n(cont10),GotoIf($[«${CHECK_NUMBER:0:4}»!=»9810″]?cont15)
same => n,Set(INTERNATIONAL_NUMBER=${CHECK_NUMBER:4})
same => n,Goto(cont30)
same => n(cont15),GotoIf($[«${CHECK_NUMBER:0:1}»!=»+»]?cont20)
same => n,Set(INTERNATIONAL_NUMBER=${CHECK_NUMBER:1})
same => n,Goto(cont30)
same => n(cont20),Set(INTERNATIONAL_NUMBER=${CHECK_NUMBER})
same => n(cont30),GotoIf($[«${INTERNATIONAL_NUMBER:0:1}»=»7» & «${INTERNATIONAL_NUMBER:1:1}»!=»7″ & «${INTERNATIONAL_NUMBER:1:1}»!=»6″]?russia)
same => n,GotoIf($[«${INTERNATIONAL_NUMBER:0:1}»=»7»]?kazakhstan)
same => n,GotoIf($[«${INTERNATIONAL_NUMBER}»=»»]?error)
same => n,GotoIf($[${LEN(${INTERNATIONAL_NUMBER})} < 5]?error)
same => n,Set(FIRST_DIGIT=${INTERNATIONAL_NUMBER:0:1})
same => n,GotoIf($[«${FIRST_DIGIT}» = «0»]?error)
same => n,Set(COUNTRY_CODE_LEN=1)
same => n,Set(COUNTRY_CODE_LIMIT=3)
same => n,ExecIf($[«${FIRST_DIGIT}» = «1» | «${FIRST_DIGIT}» = «7»]?Set(COUNTRY_CODE_LEN=0))
same => n,ExecIf($[«${FIRST_DIGIT}» = «1» | «${FIRST_DIGIT}» = «7»]?Set(COUNTRY_CODE_LIMIT=1))
same => n(loop10_start),Set(COUNTRY_CODE_LEN=$[${COUNTRY_CODE_LEN} + 1])
same => n,GotoIf($[${COUNTRY_CODE_LEN} > ${COUNTRY_CODE_LIMIT}]?loop10_end)
same => n,Set(COUNTRY_CODE=${INTERNATIONAL_NUMBER:0:${COUNTRY_CODE_LEN}})
same => n,GotoIf($[«${FIRST_DIGIT}» != «1»]?dont_check_region)
same => n,Set(REGION_CODE=${INTERNATIONAL_NUMBER:${COUNTRY_CODE_LEN}:${REGION_CODE_LEN}})
same => n,Set(COUNTRY_NAME=${ODBC_TEL_COUNTRY_REGION(${COUNTRY_CODE},${REGION_CODE})})
same => n,Goto(after_country_check)
same => n(dont_check_region),Set(REGION_CODE=)
same => n,Set(COUNTRY_NAME=${ODBC_TEL_COUNTRY(${COUNTRY_CODE})})
same => n(after_country_check),GotoIf($[«${COUNTRY_NAME}» = «» | «${COUNTRY_NAME}» = «NULL»]?loop10_start)
same => n(loop10_end),GotoIf($[«${COUNTRY_NAME}» = «» | «${COUNTRY_NAME}» = «NULL»]?error)
same => n,Goto(end)
same => n(russia),Set(COUNTRY_CODE=${INTERNATIONAL_NUMBER:0:1})
same => n,Set(REGION_CODE=${INTERNATIONAL_NUMBER:1:3})
same => n,Set(COUNTRY_NAME=Russian Federation)
same => n,Goto(end)
same => n(kazakhstan),Set(COUNTRY_CODE=${INTERNATIONAL_NUMBER:0:1})
same => n,Set(COUNTRY_NAME=Kazakhstan)
same => n,Goto(end)
same => n(error),Set(COUNTRY_NAME=)
same => n,Set(COUNTRY_CODE=)
same => n,Set(REGION_CODE=)
same => n(end),Return

В контексте сперва проверяется первая цифра международного номера телефона, затем:
— Если первая цифра 7, а вторая цифра — не 6 и не 7, значит страна — Россия.
— Если первая цифра 7, а вторая цифра — 6 или 7, значит страна — Казахстан.
— Если первая цифра 1, производится поиск названия страны по таблице кодов стран, по коду страны и коду региона (код региона — 2, 3, 4-я цифры).
— Во всех остальных случаях производится поиск названия страны по таблице кодов стран, по коду страны. Кодом страны считаются первые две цифры международного номера телефона, если по первым двум цифрам не удалось определить название страны, производится поиск по первым трём цифрам.
Пример вызова контекста:


same => Gosub(sub-country-by-number,s,1(${CALLERID(num)}))

CALLERID(num) — имя переменной, содержащей проверяемый номер телефона
В случае успешного нахождения названия страны на канале будут установлены следующие переменные:
INTERNATIONAL_NUMBER — проверяемый международный номер без префикса (начинается с кода страны)
COUNTRY_NAME — название страны
COUNTRY_CODE — телефонный код страны
Если название страны не удалось определить, переменные COUNTRY_NAME и COUNTRY_CODE будут иметь пустое значение.

Подписаться
Уведомить о
guest
1 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
Валерий
Валерий
13.10.2020 14:59

Спасибо огромное за развернутый пример!
Автору респект за проделанную работу!

Остались вопросы?

Я - Виталий Шелест, менеджер компании Voxlink. Хотите уточнить детали или готовы оставить заявку? Укажите номер телефона, я перезвоню в течение 3-х секунд.

VoIP оборудование


ближайшие курсы

10 доводов в пользу Asterisk

Распространяется бесплатно.

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

Безопасен в использовании.

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

Надежен в эксплуатации.

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

Гибкий в настройке.

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

Имеет огромный функционал.

Во многом именно Asterisk показал какой должна быть современная телефонная станция. За многие годы развития функциональность Asterisk расширилась, а все основные возможности по-прежнему доступны бесплатно сразу после установки.

Интегрируется с любыми системами.

То, что Asterisk не умеет сам, он позволяет реализовать за счет интеграции. Это могут быть интеграции с коммерческими телефонными станциями, CRM, ERP системами, биллингом, сервисами колл-трекинга, колл-бэка и модулями статистики и аналитики.

Позволяет телефонизировать офис за считанные часы.

В нашей практике были проекты, реализованные за один рабочий день. Это значит, что утром к нам обращался клиент, а уже через несколько часов он пользовался новой IP-АТС. Безусловно, такая скорость редкость, ведь АТС – инструмент зарабатывания денег для многих компаний и спешка во внедрении не уместна. Но в случае острой необходимости Asterisk готов к быстрому старту.

Отличная масштабируемость.

Очень утомительно постоянно возвращаться к одному и тому же вопросу. Такое часто бывает в случае некачественного исполнения работ или выбора заведомо неподходящего бизнес-решения. С Asterisk точно не будет такой проблемы! Телефонная станция, построенная на Asterisk может быть масштабируема до немыслимых размеров. Главное – правильно подобрать оборудование.

Повышает управляемость бизнеса.

Asterisk дает не просто набор полезных функций, он повышает управляемость организации, качества и комфортности управления, а также увеличивает прозрачность бизнеса для руководства. Достичь этого можно, например, за счет автоматизации отчетов, подключения бота в Telegram, санкционированного доступа к станции из любой точки мира.

Снижает расходы на связь.

Связь между внутренними абонентами IP-АТС бесплатна всегда, независимо от их географического расположения. Также к Asterisk можно подключить любых операторов телефонии, в том числе GSM сим-карты и настроить маршрутизацию вызовов по наиболее выгодному тарифу. Всё это позволяет экономить с первых минут пользования станцией.