Страница 1 из 4
Реализация интеграции CRM и Ast
Добавлено: 20 апр 2012, 07:23
userall200
Имеется самописная CRM с web-интерфейсом, требуется сделать модуль для интеграции ее с Asterisk (открытие карт клиента, звонки кликом из CRM, история звонков итд). Входящие звонки поступают в очередь.
С помощью каких средств это делается. Как правильнее получить список текущих звонков очереди (лог queue_log, CDR, AMI).
Алгоритм обработки очереди ringall, как можно будет определить какой именно из текущих вызовов пришел конкретному оператору для открытия нужной карточки?
Заранее спасибо.
Re: Реализация интеграции CRM и Ast
Добавлено: 20 апр 2012, 07:54
userall200
Т.е. при звонке оператора абоненту вызывать функцию CURL (
http://url?callerid=номер_оператора&num ... исе_звонка). В вызываемом скрипте уже делать INSERT в БД с данной информацией об исходящем звонке? А в конце тоже вызывать CURL и апдейтить поле продолжительность.
Не совсем понял про входящие звонки. Думаю, может сделать программу, к которой можно обратиться по http, передав ей id оператора. Она в свою очередь делает AMI запрос в Asterisk и определяет текущее соединение с этим оператором и открывает карту клиента для этого соединения. Программу вызывать при поднятии трубки... Или можно сделать более "красиво".
PS: У операторов стоят IP-телефоны, но возможно заменить на x-lite.
Re: Реализация интеграции CRM и Ast
Добавлено: 20 апр 2012, 10:20
ded
Чтобы открывать карту клиента - не надо заменять ИП-телефоны на софтфоны.
http://asterisk.ru/news/175
Re: Реализация интеграции CRM и Ast
Добавлено: 20 апр 2012, 10:37
userall200
У нас с CRM работают через Web интерфейс, ставить дополнительный софт не хотелось бы. Думаю реализовать еще вариант на AJAX (в момент поступления звонка в очередь из диалплана с помощью AGI звонок будет добавляться в таблицу и с помощью AJAX эти данные будут отображаться на панели оператора). При ответе на звонок оператор будет щелкать нужную запись и переходить к карте звонка. Но решение кривое, так как оператор должен сопоставить отвеченный звонок с этим списком. Нужно как-то определять, когда именно оператор взял трубку, в этот момент должна открываться карточка.
Re: Реализация интеграции CRM и Ast
Добавлено: 20 апр 2012, 10:43
ded
Вы все способы прочитали по ссылке?
Re: Реализация интеграции CRM и Ast
Добавлено: 20 апр 2012, 11:26
SVoy
userall200 писал(а):Думаю реализовать еще вариант на AJAX (в момент поступления звонка в очередь из диалплана с помощью AGI звонок будет добавляться в таблицу и с помощью AJAX эти данные будут отображаться на панели оператора).
можно обойтись и без agi. JS каждые 2 сек через php-скрипт запрашивает AMI на наличие звонков. Как только звонок есть - всплывает окно..
Re: Реализация интеграции CRM и Ast
Добавлено: 20 апр 2012, 11:30
Aven
Не лучшая идея.
Надо так:
1) Серверная часть каждую секунду получает текущие соединения и пишет в базу
2) Клиентская часть каждую секунду прется в базу и если есть звонок отображает окно
Так нагрузка будет минимальная и можно легко разносить на разные серверы.
Re: Реализация интеграции CRM и Ast
Добавлено: 20 апр 2012, 12:07
SVoy
ясно, что нелучшая, но для легкого старта пойдет. А если пользоваться будет пару человек то и переделывать нет нужды
Re: Реализация интеграции CRM и Ast
Добавлено: 20 апр 2012, 12:47
userall200
Сохранять в БД нужно, чтобы потом была возможность анализа. Например, нужно логировать пропущенные вызовы с целью перезванивания после освобождения оператора. Кроме того к карте звонка должен быть приложен файл записи разговора, карта должна связываться с клиентом итд, так что без БД не обойтись и хочется сделать один раз и не переделывать.
В связи с этим спрашиваю, как правильно построить архитектуру этой системы.
Интересует, как оператору дать удобную возможность определить, какой именно звонок поступил к нему из общего списка очереди для открытия карты звонка (операторов несколько и в очереди может быть несколько звонков). Думаю, реализовать AJAX скрипт, который будет по AMI раз в 1 сек связываться с Ast и получать id установленного канала для данного оператора и как только оператор поднял трубку через секунду у него откроется нужный клиент при совпадении Caller ID с клиентом или просто карта нужного звонка.
Re: Реализация интеграции CRM и Ast
Добавлено: 20 апр 2012, 12:52
ded
Код: Выделить всё
#!/bin/bash
# Background the curl process
curl -u username:PASSWORD -d text="$1"
-d user="recipient"
http://my-pbx.my-company.net/cgi-bin/opencard.php &
# username & password is optional, not mandatory.
Положить скрипт в /var/lib/asterisk/agi-bin и не забыть сделать chmod +x для него. Вызывать скрипт из диал-плана. скрипт будет обращаться к opencard.php, который будет лежать в /var/www/cgi-bin и обращаться к 80-му порту. Любителям копипасты советую не беспокоиться, без глубокого знания предмета ничего работать не будет.
Код: Выделить всё
<?php
// #!/usr/bin/php -q
header("Content-Type: text/xml\r\n", true);
$in = 'argv'; // argv / post
$out = 'std'; // std / soap
$src_num_field = 'Caller ID:';
$username = 'admin';
$secret = 'amp111';
require_once "phpagi.php";
if (!empty($GLOBALS['HTTP_RAW_POST_DATA']))
{
$in = 'post';
$out = 'soap';
}
if ($in == 'post') {
$str = $GLOBALS['HTTP_RAW_POST_DATA'];
$num = getStr($str, '<number', "</number>", 1, 0, 0, 1);
$num = trim(strip_tags($num));
} elseif (!empty($_GET['num'])) {
$num = $_GET['num'];
$out = 'soap';
} else {
$num = !empty($argv[1]) ? $argv[1] : '0';
}
if (empty($num)) {
$out = 'soap';
}
logg("$in,$out,$num: " . print_r($GLOBALS, 1));
$caller = getCallerId($num, $username, $secret, $src_num_field, $out);
if ($out == 'soap') {
$xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"
xmlns:ns1=\"urn:ibf-motors-caller-service-get\">
<SOAP-ENV:Body>
<ns1:getCaller>
<result>
<clid>$caller</clid>
</result>
</ns1:getCaller>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>";
$result = $xml;
} else {
$result = $caller;
}
logg($result);
echo $result;
function getCallerId($num, $username, $secret, $src_num_field, $out = 'std')
{
// '/etc/asterisk/manager.conf'
$agi_am = new AGI_AsteriskManager(NULL, array('username' => $username, 'secret' => $secret));
if (!$agi_am->connect()) {
if ($out == 'std') {
echo 'Can`t connect!';
}
return 0;
}
$channels = $agi_am->Command("CORE SHOW CHANNELS");
if (!isset($channels['data'])) {
if ($out == 'std') {
echo 'Can`t get channel data!';
}
return 0;
}
$channels = explode("\n", $channels['data']);
logg(print_r($channels,1));
if ($out == 'std') {
print_r($channels);
}
foreach ($channels as $channel) {
//if (strpos($channel, "/$num,") || strpos($channel, " $num@")) {
if (strpos($channel, "/$num@")) {
$channel = substr($channel, 0, strpos($channel, ' '));
$channel_data = $agi_am->Command("CORE SHOW CHANNEL $channel");
if ($out == 'std') {
echo $channel_data['data'];
}
$sp1 = strpos($channel_data['data'], $src_num_field) + strlen($src_num_field);
$sp2 = strpos($channel_data['data'], "\n", $sp1);
$caller = substr($channel_data['data'], $sp1, $sp2 - $sp1);
$caller = trim($caller);
return $caller;
//$caller2 = getStr($channel_data['data'], $src_num_field, "\n", 0, 0, 0, 1);
//return "$caller/$caller2";
}
}
return 0;
}
function logg($str) {
$log = @fopen('/var/tmp/' . date('Ymd'), 'a+');
if ($log) {
$ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '?';
fputs($log, "\n\n" . date('Y-m-d H:i:s ') . $ip . ' ' . $str);
fclose($log);
}
}
/**
* Returns part of the string based on search criteria
*
* @param string $str - Input string
* @param string or array of $part1 - Cut from
* @param string or array of $part2 - Cut to
* @param boolean $inc1 - Include or not $part1
* @param boolean $inc2 - Include or not $part2
* @param int $start - offset
* @param boolean $asString - return only first string
* @return string
*/
function getStr($str, $part1, $part2, $inc1, $inc2, $start = 0, $asString = 0)
{
if ($part1 == '' || $part2 == '') return '';
$orig_str = $str;
$str = strtolower($str);
$ra = array();
while ($start < strlen($str)) {
$part1_key = $part1;
$part2_key = $part2;
if (is_array($part1)) { // If Begining criteria is array
$pos1 = false;
$part1_key = $part1[0];
foreach ($part1 as $part1s) {
$pos1_v = strpos($str, $part1s, $start);
if (!($pos1_v === false)) {
if ($pos1_v == 0) {
$pos1 = $start;
$part1_key = $part1s;
break;
} elseif (($pos1 > $pos1_v) || ($pos1 === false)){
$pos1 = $pos1_v;
$part1_key = $part1s;
}
}
}
} else {
$pos1 = strpos($str, $part1, $start);
}
if ($pos1 === false) {
$start = strlen($str);
} else {
$sss = $pos1 + strlen($part1_key);
if (is_array($part2)) { // If Ending criteria is array
$pos2 = false;
$part2_key = $part2[0];
foreach ($part2 as $part2s) {
$pos2_v = strpos($str, $part2s, $sss);
if (!($pos2_v === false)) {
if ($pos2_v == 0) {
$pos2 = $sss;
$part2_key = $part2s;
break;
} elseif (($pos2 > $pos2_v) || ($pos2 === false)){
$pos2 = $pos2_v;
$part2_key = $part2s;
}
}
}
} else {
$pos2 = strpos($str, $part2, $sss);
}
$len = $pos2-$pos1;
if ($inc1) {
$spos = $pos1;
} else {
$spos = $pos1 + strlen($part1_key);
$len -= strlen($part1_key);
}
if ($inc2) {
$len += strlen($part2_key);
}
$fstr = substr($orig_str, $spos, $len);
if ($asString) {
return $fstr;
}
array_push($ra, $fstr);
$start = ($pos2 > $start)?($pos2):(strlen($str));
}
}
if (/*(is_array($ra) && (count($ra) == 1)) ||*/ $asString){
$ra = array_shift($ra);
}
return $ra;