вторник, 1 сентября 2009 г.

Биллинг для asterisk 1.6

Сразу оговорюсь, что данная статья предополагает что опыт внедрения asterisk у вас уже был. В нашей компании необходимо было внедрить небольшой call-центр, который бы включал в себя следующие функции:
1. Голосовое меню[Приветствие, выбор отдела или конкретного сотрудника, авторизация по pin-коду].После авторизации необходимо проверить наличие средств на лицевом счете и перевести звонок на ответственного сотрудника. В случае если средств не достаточно перевести на секретаря, который поможет пополнить[выписать] счет.
2. После разговора провести списание средств.

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

Итак, в этой статье я не буду рассказывать как заставить писать логи asterisk в mysql базу. В интернете достаточно информации на эту тему [http://www.voip-info.org/wiki/view/Asterisk+cdr+mysql].

После успешного подключения связки Asterisk+cdr+mysql нам необходимо в этой же базе, куда пишутся логи, создать 2 новые таблицы[назовем ее "organizations" и "tariff"] со следующими полями:
organizations{
id int(11) auto_increment // ID организации
name text // Наименование организации
pin varchar(20) // Пин-код[необходмо обеспечить уникальность]
time int(11) // Оставшееся время
id_tarif int(11) // ID тарифа
}

tariff{
id int(11) auto_increment // ID тарифа
name varchar(50) // Название тарифа
price int(11) // Цена за минуту
descr varchar(300) // Описание
}


Конечно, вы можете добавить свои поля, если видите в этом необходимость. Далее качаете phpagi c официального сайта[http://phpagi.sourceforge.net/]. Распаковывайте файлы в папку /var/lib/asterisk/agi-bin. И в этом же каталоге создаете файл-скрипт с именем auth.php со следующим содержанием[сразу оговорюсь, что скрипт далек от совершенства :)]:
#!/usr/bin/php -q
require('phpagi.php');
function auth($agii)
{
// Определяем номера телефонов. Если абонент недоступен - идет переадресация на сотовый телефон.
$phones = array(
"79110071250" => "000", // Менеждер 1
"79217519838" => "001", // Менеждер 2
...................................................
"79216497787" => "505" // Менежер 505
);
$buffer='';
$dtmfwait=3000;
$result=$agii->fastpass_get_data($buffer,"hello",$dtmfwait,3);
$digits=$result['result'];
//Набран внутренний номер
if(strlen($digits)==3)
{
$agii->exec('Dial',"SIP/$digits");
foreach ($phones as $phone_1 => $phone_2) {
if($phone_2==$digits)
{
$agii->stream_file("net_na_meste");
$agii->exec('Set',"_NUMBER=$phone_1");
$agii->exec('Dial','H323/9999999@prov,,M(bubble),m');
}
}
}
if(strlen($digits)==1)
{
//Соединение с горячей линией 1С [тут авторизация по пин коду]
if($digits=='1')
{
for($i=1;$i<=3;$i++)
{
$agii->stream_file('pin_code');
$result = $agii->get_data('beep', 8000,6);
$code=$result['result'];
if($code=='0')
{
$agii->exec('Dial','SIP/000');
}
else {
$result = mysql_query("select time,id from organizations where pin='$code';");

if (mysql_num_rows($result)==1)
{
while ($row = mysql_fetch_array($result)) {
if($row[0]<=0) //если баланc <0
{
$agii->stream_file("balans_minus");
$agii->exec('Dial','SIP/000');
}
else
{
$agii->exec("Set CDR(userfield)=$row[1]");
mkdir('/home/shares/asterisk/'.$row[1]);
$path='/home/shares/asterisk/'.$row[1].'/'.date("d.m.y.H.i").'.wav';
$agii->exec('MixMonitor',$path.',b');
$agii->exec('Dial','SIP/302,20,tm');
$agii->exec('Dial','SIP/401&SIP/405&SIP/000,,tm');
}
}
}
//даем на ввод пин кода 3 попытки
else
{
$tries=3-$i;
$agii->stream_file('error_pin_code');
if($tries==0)
{
$agii->stream_file('3_error_pin_code');
$agii->exec('Dial','SIP/000');
}
}
}
}
}
//Техподдержка
if($digits=='2')
$agii->exec('Dial','SIP/405');
//Контроль качества
if($digits=='3')
$agii->exec('Dial','SIP/007');
//Секретарь
if($digits=='0')
$agii->exec('Dial','SIP/000');
}
if(strlen($digits)==0) $agii->exec('Dial','SIP/000');
$agii->stream_file("bye");
$agii->exec('Hangup');
return 0;
}

$link = mysql_connect("localhost", "asterisk", "password") or die("Could not connect: " . mysql_error());
mysql_select_db('asterisk', $link) or die ('Can\'t use database : ' . mysql_error());
$agi = new AGI();
$agi->answer();
$agi->exec('NoOp','Starting main module AGI script');
//Запуск основного модуля
auth($agi);
mysql_close($link);
?>

Далее в extensions.conf прописываем что при звонке с внешней линии - идет выполнение скрипта auth.php. [Если будут вопросы как это сделать - спрашивайте в комментах].

Теперь самый интересный вопрос: каким образом произойдет списание средств после разговора? Я очень долго искал информацию как можно выполнить свой скрипт после того как на одной из сторон кладется тубка, но ничего не нашел. А решение оказалось более чем простым. Для реализации списания средств со счета абонента, я использовал crontab. В /etc/crontab добавляется строка:
*/5 * * * * root /var/lib/asterisk/agi-bin/auth-pay.agi

Данной строкой мы каждые 5 минут запускам скрипт auth-pay.agi:
#!/usr/bin/php -q
$link = mysql_connect("localhost", "asterisk", "password") or die("Could not connect: " . mysql_error());
mysql_select_db('asterisk', $link) or die ('Can\'t use database : ' . mysql_error());
$result = mysql_query("SELECT userfield,billsec FROM cdr WHERE UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(calldate) <=300 AND userfield!=''") or die(mysql_error());

if (mysql_num_rows($result)>0)
{
while ($row = mysql_fetch_array($result)) {
mysql_query("UPDATE organizations SET time=time-$row[1] WHERE id=$row[0]");
}
}
$user =mysql_num_rows($result);
if ($user==1) {echo "$user User online";} else {echo "$user Users online";}
mysql_close($link);
?>


Данным скриптом мы выбераем все id организации, разговаривавших за последние 5 минут. Берем время разговора и вычитаем из поля time таблицы organizations.

Вот и все! Мини-биллинг готов. Такую схему можно перенести и на другие возможности астериска: например для ограничения звонков не с внешней линией, а с ваших абонентов.

1 комментарий:

Slezhuk комментирует...

Для того что бы выполнить что то после Hangup(трубку положили) нужно в контекст добавить экстеншн "h".