Asterisk: определить оператора по набираемому номеру для выбора линии

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

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

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

Возьмем для примера сотового оператора Билайн (Вымпел-Коммуникации). Предположим, что GSM шлюз у нас уже подключен и в астериске поднят транк до него (в sip.conf добавлен соответствующий раздел).

В моем случае, на сервере все уже настроено, и мне осталось лишь добавить нужный экстеншн. Для этого создаем файл:
touch /etc/asterisk/extension_beeline.conf
добавляем ссылку на него в /etc/asterisk/extensions.conf
#include "extensions_beeline.conf"
добавляем экстеншен из него в нужное место в диалаплане:
include => [beeline]
В файле extension_beeline.conf будет описан экстеншн beeline.
Теперь создаем файл:
mkdir /etc/asterisk/scripts
touch /etc/asterisk/scripts/beeline.sh
chmod +x /etc/asterisk/scripts/beeline.sh
nano /etc/asterisk/scripts/beeline.sh
С таким содержанием:
#!/bin/bash

#расположение файла с DEF-кодами
DOWNFILE='https://www.rossvyaz.ru/data/DEF-9xx.csv';
#рабочая папка
TMPDIR="/etc/asterisk/scripts"
#файл, где сохраним csv формат кодов
FILENAME='beeline.csv';
# выбираем сотового оператора
SO='Вымпел-Коммуникации';

echo "Качаем csv"
wget -c -q -O $TMPDIR/Kody_DEF-9kh.csv $DOWNFILE
echo "Удаляем первую строку, кодируем в utf8, отбираем по сотовому оператору"
cat Kody_DEF-9kh.csv | sed -e '1d' | iconv -c -f WINDOWS-1251 -t UTF8 | grep "$SO" > $TMPDIR/$FILENAME

#проверяем не скачали ли пустышку
check=`cat $TMPDIR/$FILENAME`
if [ "$check" == "" ]; then
exit 0
fi

#скрипт на awk генерации Dial Patterns
awk_code='
#функция определения диапазона
function ret_diap(from,to)
{
     if ((to-from)==0) return from;
     else if ((to-from)==9) return "X";
     else return "["from"-"to"]";
}
# основная функция, приводим номера из списка в пригодны для астера вид
{
DEF=$1;
razm=1;
delete out_str;
for (i=1; i <= length($3);i++)
  {
    if ((substr($3,i,1)-substr($2,i,1))==0)
  {
    for (r=1; r <= razm;r++)
      {
         out_str[r]=out_str[r] substr($3,i,1);
      }
        }
          else
            {
               if ((substr($3,i,1)-substr($2,i,1))==9)
                 {
                    for (r=1; r <= razm;r++)
                      {
                         out_str[r]=out_str[r]"X";
                      }

                 }
                else
                       {
                          if (substr($3,i,1)-substr($2,i,1)>=1 && substr($3,(i+1),1)-substr($2,(i+1),1)!=9)
                       {
                         count=1;
                         init_str=out_str[1];
                         for (j=substr($2,(i),1); j < substr($3,(i),1);j++)
                           {
                             if (count==1)
                              {
                                 out_str[count]=init_str j ret_diap(substr($2,(i+1),1),9);
                              }
                                else
                                  {
                                     out_str[count]=init_str ret_diap(j,(substr($3,(i),1)-1)) "X";
                                     j=(substr($3,(i),1)-1);
                                  }
                                    count++;
                                    if (razm<count) razm=count;
                           }
                          out_str[count]=init_str j ret_diap(0,substr($3,(i+1),1));
                          i++;
                        }
                   else
                     {
                        for (r=1; r <= razm;r++)
                          {
                             out_str[r]=out_str[r]"["substr($2,i,1)"-"substr($3,i,1)"]";
                          }
                     }
                     }
            }
  }
    for (r in out_str)
      {
         print 8DEF out_str[r];
      }
}'

# исполняем код awk, убираем табы/пробелы, создаем экстеншн
echo 'Создаем файл для Астериска'
echo '[beeline]' > /etc/asterisk/extensions_beeline.conf
cat $TMPDIR/$FILENAME | awk -F ';' "$awk_code" | sed 's/\t//g' | awk '{print "exten => _9"$0",1,GoSub(beeline-out,s,1(${EXTEN:1}))"}' >> /etc/asterisk/extensions_beeline.conf
chown asterisk:asterisk /etc/asterisk/extensions_beeline.conf
rm -f $TMPDIR/Kody_DEF-9kh.csv
rm -f $TMPDIR/$FILENAME
echo 'Перезапускаем астериск для принятия изменений'
asterisk -rx 'core reload'
Что делает скрипт понятно из комментариев: он берет CSV файл с сайта Россвязи, вытаскивает из него нужные номера и приводит их к понятному для астера виду - в моем случае генерирует строку вида exten => _9XXXXXXXXXXX,1,GoSub(beeline-out,s,1(${EXTEN:1})) . Далее скрипт сохраняет данные в файл с контекстом для билайна и перезапускает астериск. Часть скрипта скопипастил отсюда.

Обращаем внимание, что для дозвона у меня используется контекст [beeline-out], в котором уже совершается вызов через нужный транк. В случае, если транк билайна занят или недоступен звонок идет через задарму. В моем случае контекст выглядит так:
[beeline-out]
; ${ARG1} - это переданный в контекст ${EXTEN}
; beeline - транк через GSM шлюз
; zadarma - транк zardarma.com
exten => s,1,NoOp(Звоним через GSM шлюз Beeline)
same => n,ChanIsAvail(SIP/beeline&SIP/zadarma,sj); проверяем незанятый канал
same => n,NoOp(Доступный канал ${AVAILORIGCHAN})
same => n,Execif($["${AVAILORIGCHAN}"="SIP/zadarma"]?Set(ARG1=7${ARG1:1}));если звонок пойдет через задарму меняем 8 на 7 в начале номера
same => n,NoOp(Звоним на номер ${ARG1} через ${AVAILORIGCHAN})
same => n,Dial(${AVAILORIGCHAN}/${ARG1},40,Tg);звоним по незанятому каналу
same => n,Hangup
Подключить файл /etc/asterisk/extensions_beeline.conf в диалплан можно следующей строчкой:
#include "extensions_beeline.conf"

UPD 17 апреля 2019 г.
Немного обновил статью.
Для работы ChanIsAvail нужно разрешить загрузку модуля app_chanisavail.so в /etc/asterisk/modules.conf
Для работы Exec/ExecIf нужно разрешить загрузку модуля app_exec.so в /etc/asterisk/modules.conf
Протестировал работу на Asterisk 16, проблем нет.
Вместо Билайна вы можете сортировать по любому оператору немного изменив скрипт, и даже по региону.
С сегодняшними тарифами сотовых операторов, не думаю, что статья актуальна. Проще воспользоваться сервисом например zadarma, чем VoIP GSM шлюзами с симкартами. Но возможно кому-то будет полезно.

Комментариев нет:

Отправить комментарий