Страница 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;