Давно хотел, чтобы мои операторы выдели информацию о клиенте и знали уже перед самим входящим звонком — кто звонит. Но покупать готовые модуля за басновловные деньги не хотелось.
Вариант нашелся сам…
Так как у нас звёздочка в мир не смотрит, то очевидно что все выполняется на сервере с астериском.
Итак, нашел библиотеку PHP Tail, спасибо автору Тошио Такигучи.
Задумка такова, php грепать логи звездочки на предмет входящего звонка, смотреть номер телефона входящего звонка, далее скармливать номер, в нашем случае самописному биллингу.
Полученный ответ, то ли это инфа о клиенте, то ли что номер не найден ни в одной карточке клиента выводить на экран, приостановить выполнение до нажатия оператором кнопки Далее (для того чтобы он успел поработать с информацией).
Итак выполнение:
1. В библиотеке от Тошио Такигучи в файле Log.php необходимо заменить строку
на
ну или другой адрес если у Вас лог пишется в другой файл.
2. Основные телодвижения будут происходить с файлом PHPTail.php.
В function updateLog() необходимо задать четкое понимание входящего звонка и номера телефона этого звонка.
Реализовано так:
Далее ajax-ом передаем полученный номер телефона на другой php-файл где и происходит обращение к api биллинга.
Полный код PHPTail.php
3. Создаем файлы которые и будут отправлять запросы api
curl.php — получает инфу о клиенте
curl_send_phone.php — записывает номер телефона в карточку клиента
4. Кладем эти 4-ре файла в /var/www/html/ (ну или в другое место, в зависимости от Вашей конфигурации Asterisk-а)
P.S. Не пишите, что мол есть AMI, оно не столь гибкое.
Вариант нашелся сам…
Так как у нас звёздочка в мир не смотрит, то очевидно что все выполняется на сервере с астериском.
Итак, нашел библиотеку PHP Tail, спасибо автору Тошио Такигучи.
Задумка такова, php грепать логи звездочки на предмет входящего звонка, смотреть номер телефона входящего звонка, далее скармливать номер, в нашем случае самописному биллингу.
Полученный ответ, то ли это инфа о клиенте, то ли что номер не найден ни в одной карточке клиента выводить на экран, приостановить выполнение до нажатия оператором кнопки Далее (для того чтобы он успел поработать с информацией).
Итак выполнение:
1. В библиотеке от Тошио Такигучи в файле Log.php необходимо заменить строку
$tail = new PHPTail("/path/to/log");
на
$tail = new PHPTail("/var/log/asterisk/full");
ну или другой адрес если у Вас лог пишется в другой файл.
2. Основные телодвижения будут происходить с файлом PHPTail.php.
В function updateLog() необходимо задать четкое понимание входящего звонка и номера телефона этого звонка.
Реализовано так:
if (value.includes('macro-user-callerid') && value.includes('Local/'+grep) && value.includes('CALLERID(name)=')) {
var str = value.split('CALLERID(name)=');
var num = str[1].replace(/\D+/g, '')
......
}
Далее ajax-ом передаем полученный номер телефона на другой php-файл где и происходит обращение к api биллинга.
$.ajax({
type: "POST",
url: "curl.php?num="+num,
success: function(data) {
if (data!=''){
var res = JSON.parse(data);
// далее работаем с полученными данными
...
clearInterval(intervalID); //останавливаем и ждем действия от оператора
}
else {
clearInterval(intervalID); //останавливаем и ждем действия от оператора
// Для того чтобы оператор мог подвязать этот номер телефона к карточке клиента
}
Полный код PHPTail.php
<?php
class PHPTail {
private $log = "";
private $updateTime;
private $maxSizeToLoad;
public function __construct($log, $defaultUpdateTime = 2000, $maxSizeToLoad = 2097152) {
$this->log = $log;
$this->updateTime = $defaultUpdateTime;
$this->maxSizeToLoad = $maxSizeToLoad;
}
public function getNewLines($lastFetchedSize, $grepKeyword, $invert) {
clearstatcache();
$fsize = filesize($this->log);
$maxLength = ($fsize - $lastFetchedSize);
if($maxLength > $this->maxSizeToLoad) {
return json_encode(array("size" => $fsize, "data" => array("ERROR: PHPTail attempted to load more (".round(($maxLength / 1048576), 2)."MB) then the maximum size (".round(($this->maxSizeToLoad / 1048576), 2)."MB) of bytes into memory. You should lower the defaultUpdateTime to prevent this from happening. ")));
}
$data = array();
if($maxLength > 0) {
$fp = fopen($this->log, 'r');
fseek($fp, -$maxLength , SEEK_END);
$data = explode("\n", fread($fp, $maxLength));
}
if($invert == 0) {
$data = preg_grep("/$grepKeyword/",$data);
}
else {
$data = preg_grep("/$grepKeyword/",$data, PREG_GREP_INVERT);
}
if(end($data) == "") {
array_pop($data);
}
return json_encode(array("size" => $fsize, "data" => $data));
}
public function generateGUI() {
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Info about called</title>
<link rel="shortcut icon" href="/admin/images/favicon.ico">
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<link type="text/css" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/themes/flick/jquery-ui.css" rel="stylesheet"></link>
<style type="text/css">
#grepKeyword, #settings {font-size: 80%;}
.float {background: #f5f5f5;z-index: 9999;border-bottom: 1px solid black;padding: 10px 0 10px 0;margin: 0px;height: 30px;width: 100%;text-align: left;}
</style>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.min.js"></script>
<script type="text/javascript">
/* <![CDATA[ */
lastSize = <?php echo filesize($this->log); ?>;
grep = "";
invert = 0;
documentHeight = 0;
scrollPosition = 0;
scroll = true;
$(document).ready(function(){
$( "#settings" ).dialog({
modal: true,
resizable: false,
draggable: false,
autoOpen: false,
width: 590,
height: 270,
buttons: {
Close: function() {
$( this ).dialog( "close" );
}
},
close: function(event, ui) {
grep = $("#grep").val();
invert = $('#invert input:radio:checked').val();
$("#grepspan").html("Ваш номер телефона: \"" + grep + "\"");
$("#invertspan").html("Inverted: " + (invert == 1 ? 'true' : 'false'));
}
});
$('#grep').keyup(function(e) {
if(e.keyCode == 13) {
$( "#settings" ).dialog('close');
}
});
$("#grep").focus();
$("#grepKeyword").button();
$("#grepKeyword").click(function(){
$( "#settings" ).dialog('open');
$("#grepKeyword").removeClass('ui-state-focus');
});
$(window).scroll(function(e) {
if ($(window).scrollTop() > 0) {
$('.float').css({
position: 'fixed',
top: '0',
left: 'auto'
});
} else {
$('.float').css({
position: 'static'
});
}
});
$(window).resize(function(){
if(scroll) {
scrollToBottom();
}
});
$(window).scroll(function(){
documentHeight = $(document).height();
scrollPosition = $(window).height() + $(window).scrollTop();
if(documentHeight <= scrollPosition) {
scroll = true;
}
else {
scroll = false;
}
});
scrollToBottom();
});
function scrollToBottom() {
$('.ui-widget-overlay').width($(document).width());
$('.ui-widget-overlay').height($(document).height());
$("html, body").scrollTop($(document).height());
if($( "#settings" ).dialog("isOpen")) {
$('.ui-widget-overlay').width($(document).width());
$('.ui-widget-overlay').height($(document).height());
$( "#settings" ).dialog("option", "position", "center");
}
}
function updateLog() {
$('#start').css('opacity','0.2');
$('#start').css('pointer-events','none');
$("#results").empty();
$.getJSON('?ajax=1&lastsize='+lastSize + '&grep='+grep + '&invert='+invert, function(data) {
lastSize = data.size;
$.each(data.data, function(key, value) {
if (value.includes('macro-user-callerid') && value.includes('Local/'+grep) && value.includes('CALLERID(name)=')) {
var str = value.split('CALLERID(name)=');
var num = str[1].replace(/\D+/g, '');
$.ajax({
type: "POST",
url: "curl.php?num="+num,
success: function(data) {
if (data!=''){
var res = JSON.parse(data);
var text='<form><h2>Информация о входящем звонке</h2><h3><a href="'+res.contract_url+'" target="_blank" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" style="font-size:80%;"><span class="ui-button-text">Открыть в биллинге</span></a></h3><fieldset id="user-details"><label for="tel">Телефон:</label><input type="text" name="tel" value="'+num+'"/><label for="addres">Адрес:</label><input type="text" name="addres" value="'+res.adress.replace("'", "")+'"/><label for="fio">ФИО:</label><input type="text" name="fio" value="'+res.fio+'" /><label for="lc">Счёт:</label><input type="text" name="lc" value="'+res.lc+'" /><label for="balance">Баланс:</label><input type="text" name="balance" value="'+res.balance+'" /></fieldset>';
text+='<div style="width: 100%;text-align: right;float: left;"><a class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" onclick=\'intervalID = setInterval("updateLog()", 1000)\'><span class="ui-button-text">Далее</span></a></div></form>';
$("#results").html(text);
clearInterval(intervalID);
}
else {
clearInterval(intervalID);
text='<form id="add_new"><h2>Информация о входящем звонке</h2> <h3 id="add_new_result">Клиент не найден</h3> <div id="add_cont"><label for="tel">Телефон:</label><input type="text" id="tel_update_tel" name="tel" value="'+num+'"/><label for="lc">Счёт:</label><input type="text" id="tel_update_lc" name="lc"/><a id="addnum_to_contract" onclick="update_num()" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"><span class="ui-button-text">Добавить телефон к учетной записи</span></a></div><a class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" onclick=\'intervalID = setInterval("updateLog()", 1000)\'><span class="ui-button-text">Далее</span></a></form>';
$("#results").html(text);
}
}
});
}
});
if(scroll) {
scrollToBottom();
}
});
}
/* ]]> */
</script>
<script type="text/javascript">
function update_num(){
var tel=$("#tel_update_tel").val();
var lc=$("#tel_update_lc").val();
$.ajax({
type: "POST",
url: "curl_send_phone.php?tel="+tel+"&lc="+lc,
success: function(response){
var res2 = JSON.parse(response);
if (res2.result) {
if (res2.result==true) {
$("#add_cont").fadeOut();
$("#add_new_result").html('Номер успешно добавлен к карточке абонента');
}
else {
$("#add_cont").fadeOut();
$("#add_new_result").html('Произошла ошибка, попробуйте позже');
}
}
}
});
}
</script>
<link href='//fonts.googleapis.com/css?family=Yanone+Kaffeesatz' rel='stylesheet' type='text/css' />
<style>
body {margin: 0 auto;background: #f5f5f5;color: #555;width: 800px;font-family: 'Yanone Kaffeesatz', arial, sans-serif;}
h1 {color: #555;margin: 0 0 20px 0;}
label {font-size: 20px;color: #666;}
fieldset {border: none;}
#user-details {float: left;width: 40%;}
#requests-message {float: left;width: 45%;}
#user-message {float: left;width: 100%;}
textarea {width: 390px;height: 175px;}
form {float: left;border: 1px solid #ddd;padding: 30px 40px 20px 40px;margin: 75px 0 0 0;width: 715px;background: #fff;-webkit-border-radius: 10px;-moz-border-radius: 10px;border-radius: 10px;background: -webkit-gradient(linear, 0% 0%, 0% 40%, from(#EEE), to(#FFF));background: -moz-linear-gradient(0% 40% 90deg,#FFF, #EEE); -webkit-box-shadow:0px 0 50px #ccc;-moz-box-shadow:0px 0 50px #ccc;box-shadow:0px 0 50px #ccc;}
input, textarea {padding: 8px;margin: 4px 0 20px 0;background: #fff;width: 220px;display:block;font-size: 14px;color: #555;border: 1px #ddd solid;-webkit-box-shadow: 0px 0px 4px #aaa;-moz-box-shadow: 0px 0px 4px #aaa;box-shadow: 0px 0px 4px #aaa;-webkit-transition: background 0.3s linear;}
input{width:90%}
input:hover, textarea:hover {background: #eee;}
#user-message>label {display: block;}
</style>
</head>
<body>
<div id="settings" title="Введите Ваш внутренний номер телефона">
<input id="grep" type="text" value=""/>
<div id="invert" style="display:none">
<input type="radio" value="1" id="invert1" name="invert" /><label for="invert1">Yes</label>
<input type="radio" value="0" id="invert2" name="invert" checked="checked" /><label for="invert2">No</label>
</div>
</div>
<div class="float" >
<button id="grepKeyword">Внутренний номер</button>
<span style="display:none">Tailing file: <?php echo $this->log; ?></span> <span id="grepspan" >Ваш номер телефона: ""</span> <span style="display:none" id="invertspan">Inverted: false</span>
<a class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" style="font-size:80%;float:right" onclick='intervalID = setInterval("updateLog()", 1000)' id="start"><span class="ui-button-text">Начать</span></a>
</div>
<div id="results">
</div>
<script type="text/javascript">
function update_num(){
var tel=$("#tel_update_tel").val();
var lc=$("#tel_update_lc").val();
$.ajax({
type: "POST",
url: "curl_send_phone.php?tel="+tel+"&lc="+lc,
success: function(response){
var res2 = JSON.parse(response);
if (res2.result) {
if (res2.result==true) {
$("#add_cont").fadeOut();
$("#add_new_result").html('Номер успешно добавлен к карточке абонента');
}
else {
$("#add_cont").fadeOut();
$("#add_new_result").html('Произошла ошибка, попробуйте позже');
}
}
}
});
}
</script>
</body>
</html>
<?php
}
}
3. Создаем файлы которые и будут отправлять запросы api
curl.php — получает инфу о клиенте
<?php
$num = $_REQUEST['num'];
if ($_REQUEST['num']!='')
{
$url = "адрес к api";
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $url);
$result = curl_exec($ch);
curl_close($ch);
print($result);
}
?>
curl_send_phone.php — записывает номер телефона в карточку клиента
<?php
if ( ($_REQUEST['tel']!='') && ($_REQUEST['uid']!='') )
{
$tel = $_REQUEST['tel'];
$uid = $_REQUEST['uid'];
$url = "адрес к api";
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $url);
$result = curl_exec($ch);
curl_close($ch);
print($result);
}
?>
4. Кладем эти 4-ре файла в /var/www/html/ (ну или в другое место, в зависимости от Вашей конфигурации Asterisk-а)
P.S. Не пишите, что мол есть AMI, оно не столь гибкое.