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

Материал из support.qbpro.ru
(разн.) ← Предыдущая версия | Текущая версия (разн.) | Следующая версия → (разн.)

В этой статье будет описано, как решить задачу определения страны по номеру телефона средствами диалплана 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-файла для загрузки телефонных кодов Канады в таблицу кодов стран (телефонные коды Канады взяты из Википедии )

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

Список телефонных кодов США можно взять здесь . Как и в случае с кодами стран, скопируйте таблицу со страницы и вставьте её в какой-либо файл, ниже приведён скрипт для преобразования этой таблицы в формат 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 будут иметь пустое значение.

ИСТОЧНИК: