Определение региона по номеру телефона в Asterisk без использования БД

842d6ffc5706476c85ab66f6d036ae22.jpg

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

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

По адресу www.rossvyaz.ru/activity/num_resurs/registerNum находится официальная, обновляемая «Выписка из реестра Российской системы и плана нумерации» в четырех томах, по первым цифрам кода: 3, 4, 8 и 9. Нам нужно скачать все файлы в формате csv, объединить их, удалить ненужные и пустые поля, поменять кодировку на UTF-8, и все пробелы заменить на »_», чтобы потом было проще работать в bash-скрипте.

Файл получается следующего вида:

префикс! начальный номер! конечный номер! кому принадлежит! где зарегистрирован

999!9440000!9449999!"ОАО_""Основа_Телеком"""!Ставропольский_край
999!9450000!9459999!"ОАО_""Основа_Телеком"""!Тверская_область
999!9460000!9469999!"ОАО_""Основа_Телеком"""!Тульская_область
999!9470000!9479999!"ОАО_""Основа_Телеком"""!Ульяновская_область
999!9480000!9489999!"ОАО_""Основа_Телеком"""!Ярославская_область
999!9490000!9499999!"ПАО_""МегаФон"""!Республика_Саха_/Якутия/

Теперь напишем bash скрипт. Сначала отберем строки по трехзначному коду, потому что прогонять каждый раз весь файл через цикл очень долго. У меня на тесте один прогон длился 4 секунды, а текущий вариант выполняется гораздо быстрее, хотя тоже не моментально

#!/bin/bash

#Определим переменные
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"  #определим директорию со скриптом
FILE=9.csv   #имя файла
NUM=$1       # номер, переданный скрипту в 10-значном формате
PREF=${NUM:0:3} #код
MID=${NUM:3:11} # сам номер
lines="$( cat $DIR/$FILE | grep ^$PREF )"   # получаем из файла все вхождения по коду города. Все строки 
#слепляются тут в одну очень длинную строку, именно поэтому и нужно было избавиться от пробелов, которые теперь будут отделять записи друг от друга

for str in $lines; #цикл по всем вхождениям
do 

start=$( echo $str | cut -d '!' -f2 )   #получаем начальный и конечный номера диапазона
stop=$( echo $str | cut -d '!' -f3 )

if [ "$MID" -le "$stop" ]&&[ "$MID" -ge  "$start" ] #если наш номер входит в диапазон
then
reg=$( echo $str | cut -d '!' -f5 )    #получаем название региона
break  #выходим из цикла
fi

done
echo -n $reg # и выводим результат
exit 0

Обратите внимание на параметр -n в вызове echo. Он необходим, так как без него результат выполнения скрипта помимо названия региона будет содержать перевод строки, что нам совсем ни к чему.

Теперь нужно дать права скрипту на выполнение

chmod +x region.sh

и не забыть дать доступ тому пользователю, от которого работает Asterisk к скрипту и файлу с данными.

chown asterisk:asterisk -R /path-to-script

Для проверки запустим свой скрипт с каким-нибудь номером из консоли и убедимся в его работоспособности.

Остается работа с Asterisk. Приложение System, которое обычно используют для исполнения скриптов, нам не подходит — нам нужен результат работы скрипта, а не статус выполнения!

Поэтому воспользуемся функцией SHELL:

exten => 9999,n,Noop(${CALLERID(num)})
exten => 9999,n,Set(num=${CALLERID(num):1})  ; номера у нас 11-значные, так что первую цифру отрежем

exten => 9999,n,Set(__reg=${SHELL(/home/asterisk/region.sh ${num}  )})
exten => 9999,n,Noop(${reg})

Обратите внимание, переменную мы присваиваем с двумя символами »__» в начале, чтобы она наследовалась при переводе звонка по Goto в другие контексты и макросы.

В консоли в момент звонка мы соответственно увидим

Executing [9999@region] NoOp("SIP/123-00000026", "79063454647") in new stack
    -- Executing [9999@region] Set("SIP/123-00000026", "num=9063454647") in new stack
    -- Executing [9999@region] Set("SIP/123-00000026", "reg=Самарская_обл.") in new stack
    -- Executing [9999@region] NoOp("SIP/123-00000026", "Самарская_обл.") in new stack

Аналогичным образом можно получить название оператора связи по каждому номеру, только поле в скрипте нужно будет получить не под номером 5, а под номером 4

	reg=$( echo $str | cut -d '!' -f4 )

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

Автор статьи: системный администратор Centos-admin.ru — Алексей Дмитриев.

Комментарии (5)

  • 10 августа 2016 в 08:57

    0

    Извините, если я чего не понял, но, если абонент перешёл к другому оператору со своим номером, то этот скрипт будет выдавать неверные результаты?
    • 10 августа 2016 в 09:48

      0

      Насколько мне известно перенос номера по MNP возможен только внутри одного региона, соответственно область всё равно будет правильно определяться.
  • 10 августа 2016 в 09:46 (комментарий был изменён)

    0

    Почему решили отказаться от хранения в бд? У Вас при каждом звонке читаются все данные. В бд был бы поиск по дереву, индекс, кеширование…
    • 10 августа 2016 в 10:56

      0

      Тоже не понял почему столь странное решение. Даже если не использовать большие продукты типа Postgresql или Mysql всегда можно остановиться на аналогах sqlite. Да и запуск shell скрипта из астера при каждом звонке немного неоптимально. Это только мое мнение. Может и не прав
  • 10 августа 2016 в 12:27

    0

    Как минимум, можно разделить один файл, на кучку по префиксам. Тогда просматривать будем файл меньше → быстрее. sqlite безусловно будет быстрее, там ведь есть индексация.

© Habrahabr.ru