В нашей организации используется Астериск, а для исходящих вызовов к нему подключен GSM-шлюз Yeastar TG800 на 8 симок.
Каждый месяц мы покупаем пакет минут на каждую симку.
Для совершения исходящего вызова обычно используется перебор симок по порядку, пока не найдется свободная:
exten => _X.,n,Dial(SIP/gsm1/${EXTEN},,tT)
exten => _X.,n,Dial(SIP/gsm2/${EXTEN},,tT)
exten => _X.,n,Dial(SIP/gsm3/${EXTEN},,tT)
и т. д.
В этом случае больше всего вызовов проходит через первую симку, а меньше всего через последнюю. Поэтому к середине месяца минуты на первых симках заканчиваются раньше, чем на последних, и возникает ситуация, когда позвонить можно допустим через 4 симки из 8, так как на первых четырех минуты закончились. Это плохо, так как остаются 4 исходящие линии вместо 8.
Чтобы этого избежать, нужно делать вызовы не в одном и том же порядке, а в соответствии с остатком минут на симках.
Попутно я записываю остатки минут, актуальность (во сколько были проверены остатки) и когда пакет был активирован в БД MySQL.
Можно зайти на страничку и быстро узнать остатки:
Чтобы проверять остатки, нужно на шлюзе включить API, для этого заходим в раздел SMS, далее в API-настройки. Там все понятно, нужно задать логин с паролем и разрешенную подсеть для подключения.
Будем проверять остатки каждый час, для этого в крон добавим вызов скрипта (у меня каждую 53-ю минуту часа):
53 */1 * * * root php %path-to-script%/getbalance.php
Далее нам нужно создать 8 текстовых файлов (или сколько у вас используется симок) с именами от nc2.txt до nc9.txt. Номера такие, потому что шлюз нумерует свои порты именно так, от 2 до 9.
В каждом файле команды шлюзу. Пример для nc2.txt:
action: login
username: ******
secret: *******
action: smscommand
command: gsm send ussd 2 *255*0#
action: logoff
Пример приведен для Теле2.
Соответственно в файле nc3.txt вместо «ussd 2» пишем «ussd 3».
Скрипт передает эти команды шлюзу, в ответ мы получаем файлы с именами от nc2out.txt до nc9out.txt.
Ответ будет такой (для Теле2):
Asterisk Call Manager/1.1
Response: Success
Message: Authentication accepted
Response: Follows
Privilege: SMSCommand
1:Received USSD success on span: 2
USSD Responses: 2
USSD Code: 64
USSD Len: 85
USSD Message: Ostatok v pakete: 968 min./mes., 5000 SMS/mes., 20480 MB/mes. Parametry’ tarifa *107#
—END COMMAND—
Response: Goodbye
Message: Thanks for all the fish.
Уточнение: нужно переключить ответы оператора на транслит, иначе получаем текст в неудобной кодировке.
Дальше мы выдергиваем количество оставшихся минут, сортируем и записываем порядок вызова в файлы от out1.txt до out8.txt (это уже для астериска, поэтому нумерация нормальная). То есть если порядок вызова оказался 5, 4, 3, 2, 1, 7, 8, 6, то в файл out1.txt мы пишем цифру 5, в файл out2.txt пишем 4 и т. д.
Осталось в диалплане прочитать эти файлы и делать вызовы в правильном порядке:
exten => _X.,n,set(SIMNUM1=${SHELL(/usr/bin/php -f %pathtoscript%/getsim1.php)})
exten => _X.,n,set(SIMNUM2=${SHELL(/usr/bin/php -f %pathtoscript%/getsim2.php)})
exten => _X.,n,set(SIMNUM3=${SHELL(/usr/bin/php -f %pathtoscript%/getsim3.php)})
exten => _X.,n,set(SIMNUM4=${SHELL(/usr/bin/php -f %pathtoscript%/getsim4.php)})
exten => _X.,n,set(SIMNUM5=${SHELL(/usr/bin/php -f %pathtoscript%/getsim5.php)})
exten => _X.,n,set(SIMNUM6=${SHELL(/usr/bin/php -f %pathtoscript%/getsim6.php)})
exten => _X.,n,set(SIMNUM7=${SHELL(/usr/bin/php -f %pathtoscript%/getsim7.php)})
exten => _X.,n,set(SIMNUM8=${SHELL(/usr/bin/php -f %pathtoscript%/getsim8.php)})
exten => _X.,n,Dial(SIP/gsm${SIMNUM1}/${EXTEN},,tT)
exten => _X.,n,Dial(SIP/gsm${SIMNUM2}/${EXTEN},,tT)
exten => _X.,n,Dial(SIP/gsm${SIMNUM3}/${EXTEN},,tT)
exten => _X.,n,Dial(SIP/gsm${SIMNUM4}/${EXTEN},,tT)
exten => _X.,n,Dial(SIP/gsm${SIMNUM5}/${EXTEN},,tT)
exten => _X.,n,Dial(SIP/gsm${SIMNUM6}/${EXTEN},,tT)
exten => _X.,n,Dial(SIP/gsm${SIMNUM7}/${EXTEN},,tT)
exten => _X.,n,Dial(SIP/gsm${SIMNUM8}/${EXTEN},,tT)
Да, чуть не забыл, файл getsim1.php:
<?php
$t = file_get_contents(‘%pathtoscript%/out1.txt’);
echo $t[0];
return $t[0];
?>
Теперь собственно сам главный скрипт getbalance.php.
IP шлюза возьмем 10.10.1.1.
Знаю, что нужно сделать цикл вместо копипасты, но руководствуюсь золотым правилом админа: работает — не трогай 🙂
<?php include '/var/lib/asterisk/agi-bin/lib.php'; $s=0; a2: $s++; if ($s>10) exit; system('nc 10.10.1.1 5038 < %pathtoscript%/nc2.txt > %pathtoscript%/nc2out.txt'); $text = file_get_contents('%pathtoscript%/nc2out.txt'); if(preg_match('/te:\ (.*)\ min/',$text,$matches)) $text2 = $matches[1]; else goto a2; $s=0; a3: $s++; if ($s>10) exit; system('nc 10.10.1.1 5038 < %pathtoscript%/nc3.txt > %pathtoscript%/nc3out.txt'); $text = file_get_contents('%pathtoscript%/nc3out.txt'); if(preg_match('/te:\ (.*)\ min/',$text,$matches)) $text3 = $matches[1]; else goto a3; $s=0; a4: $s++; if ($s>10) exit; system('nc 10.10.1.1 5038 < %pathtoscript%/nc4.txt > %pathtoscript%/nc4out.txt'); $text = file_get_contents('%pathtoscript%/nc4out.txt'); if(preg_match('/te:\ (.*)\ min/',$text,$matches)) $text4 = $matches[1]; else goto a4; $s=0; a5: $s++; if ($s>10) exit; system('nc 10.10.1.1 5038 < %pathtoscript%/nc5.txt > %pathtoscript%/nc5out.txt'); $text = file_get_contents('%pathtoscript%/nc5out.txt'); if(preg_match('/te:\ (.*)\ min/',$text,$matches)) $text5 = $matches[1]; else goto a5; $s=0; a6: $s++; if ($s>10) exit; system('nc 10.10.1.1 5038 < %pathtoscript%/nc6.txt > %pathtoscript%/nc6out.txt'); $text = file_get_contents('%pathtoscript%/nc6out.txt'); if(preg_match('/te:\ (.*)\ min/',$text,$matches)) $text6 = $matches[1]; else goto a6; $s=0; a7: $s++; if ($s>10) exit; system('nc 10.10.1.1 5038 < %pathtoscript%/nc7.txt > %pathtoscript%/nc7out.txt'); $text = file_get_contents('%pathtoscript%/nc7out.txt'); if(preg_match('/te:\ (.*)\ min/',$text,$matches)) $text7 = $matches[1]; else goto a7; $s=0; a8: $s++; if ($s>10) exit; system('nc 10.10.1.1 5038 < %pathtoscript%/nc8.txt > %pathtoscript%/nc8out.txt'); $text = file_get_contents('%pathtoscript%/nc8out.txt'); if(preg_match('/te:\ (.*)\ min/',$text,$matches)) $text8 = $matches[1]; else goto a8; $s=0; a9: $s++; if ($s>10) exit; system('nc 10.10.1.1 5038 < %pathtoscript%/nc9.txt > %pathtoscript%/nc9out.txt'); $text = file_get_contents('%pathtoscript%/nc9out.txt'); if(preg_match('/te:\ (.*)\ min/',$text,$matches)) $text9 = $matches[1]; else goto a9; $text10 = "0"; system('echo "'.date('l dS F Y h:i:s A').' '.$text2.' '.$text3.' '.$text4.' '.$text5.' '.$text6.' '.$text7.' '.$text8.' '.$text9.'" >>%pathtoscript%/balance.txt'); $row = mysql_query("select minutes from `lines` where clid like '%2636'",$link); $row1 = mysql_fetch_assoc($row); $old1 = $row1['minutes']+0; $new1 = $text2+0; if ($old1 < $new1) $row = mysql_query("update `lines` set balancedate=NOW()",$link); $row = mysql_query("update `lines` set balance=".$text10.", minutesdate=NOW(), minutes=".$text2." where clid like '%2636'",$link); $row = mysql_query("update `lines` set balance=".$text10.", minutesdate=NOW(), minutes=".$text3." where clid like '%4036'",$link); $row = mysql_query("update `lines` set balance=".$text10.", minutesdate=NOW(), minutes=".$text4." where clid like '%4977'",$link); $row = mysql_query("update `lines` set balance=".$text10.", minutesdate=NOW(), minutes=".$text5." where clid like '%2414'",$link); $row = mysql_query("update `lines` set balance=".$text10.", minutesdate=NOW(), minutes=".$text6." where clid like '%2451'",$link); $row = mysql_query("update `lines` set balance=".$text10.", minutesdate=NOW(), minutes=".$text7." where clid like '%2536'",$link); $row = mysql_query("update `lines` set balance=".$text10.", minutesdate=NOW(), minutes=".$text8." where clid like '%2581'",$link); $row = mysql_query("update `lines` set balance=".$text10.", minutesdate=NOW(), minutes=".$text9." where clid like '%4056'",$link); $n[1] = $text2+0; $n[2] = $text3+0; $n[3] = $text4+0; $n[4] = $text5+0; $n[5] = $text6+0; $n[6] = $text7+0; $n[7] = $text8+0; $n[8] = $text9+0; $p[1] = 1; $p[2] = 2; $p[3] = 3; $p[4] = 4; $p[5] = 5; $p[6] = 6; $p[7] = 7; $p[8] = 8; for($i=1;$i<=7;$i++) for($j=$i+1;$j<=8;$j++) if ($n[$i]<$n[$j]) { $t = $n[$i]; $n[$i] = $n[$j]; $n[$j] = $t; $t = $p[$i]; $p[$i] = $p[$j]; $p[$j] = $t; } system('echo "'.$p[1].'" >%pathtoscript%/out1.txt'); system('echo "'.$p[2].'" >%pathtoscript%/out2.txt'); system('echo "'.$p[3].'" >%pathtoscript%/out3.txt'); system('echo "'.$p[4].'" >%pathtoscript%/out4.txt'); system('echo "'.$p[5].'" >%pathtoscript%/out5.txt'); system('echo "'.$p[6].'" >%pathtoscript%/out6.txt'); system('echo "'.$p[7].'" >%pathtoscript%/out7.txt'); system('echo "'.$p[8].'" >%pathtoscript%/out8.txt'); system('asterisk -rx "dialplan reload"'); ?>
Содержимое lib.php (там подключение к MySQL):
<?php $dbuser = "*******"; $dbpass = "*******"; $dbserv = "localhost"; $dbname = "*******"; $dbengn = "mysql"; function sql_connect($host, $login, $password, $dbname) { $link = mysql_connect($host, $login, $password, true); mysql_select_db($dbname, $link); mysql_query("SET NAMES cp1251"); return $link; } function sql_query($query, $link) { $res = mysql_query($query, $link); return $res; } function sql_fetch_assoc($result) { return mysql_fetch_assoc($result); } $link = sql_connect($dbserv, $dbuser, $dbpass, $dbname); ?>
Еще небольшие пояснения.
На каждой симке делается 10 попыток проверить баланс (это не всегда удается), если хотя бы на одной не получилось, выходим и пробуем через час.
Почему text10=«0»? Раньше использовались симки МТС, которые возвращали баланс (в рублях). В Теле2 используется общий счет, на конкретные симки мы денег не кладем, поэтому там всегда 0.
Балансы также дописываются в файл balance.txt, чтобы можно было посмотреть историю и посчитать статистику трат.
Спасибо за внимание!
ссылка на оригинал статьи https://habrahabr.ru/post/318004/
Добавить комментарий