Страница 1 из 1

Интеграция OTRS и Asterisk

Добавлено: 08 июн 2012, 15:45
alex
Наконец, реализовали возможность совершать звонки из OTRS так же, как у нас это уже давно реализовано в Dokuwiki.

Работает это все следующим образом: при просмотре заявки агентом он может в один клик набрать мобильный, рабочий или домашний номер клиента. Звонок сначала приходит на внутренний номер агента, указанный в его профиле, а после того, как агент снимает трубку идет дозвон до клиента. Можно добавить компании клиента поле "Рабочий Телефон" и тогда для всех клиентов принадлежащих компании будет отображаться этот номер.
Чтобы указать внутренний номер агентам, можно использовать динамические поля агента (добавить поле extension). Изначально, мы использовали для этого поле "Комментарий".

Как добавлять поля клиентов написано тут: http://doc.otrs.org/3.0/ru/html/custome ... backend-db
Аналогично добавляются поля и для компаний.

"Звонилка" состоит из следующих частей:
1. JavaScript в шаблоне AgentTicketZoom.dtl
Javascript находит на странице телефон, разбирает поле с телефонами (по запятым) и каждый номер оборачивает в ссылку на вызов ajax-запроса "для позвонить". Если в номере содержится +, то всё, что после него, считается "добавочным" и ajax-агенту не передаётся. Чтобы поле отлавливалось скриптом, оно должно содержать $Text{"Phone"} (локализация слова "Phone"), например "Мобильный Телефон", "Рабочий Телефон" и т.д. Этого можно добиться, используя кастомную локализацию.
2. PHP-скрипт phony.php на сервере OTRS
AJAX-запрос вызывает скрипт phony.php, находящийся на сервере OTRS. Данный скрипт обязательно должен быть на том же домене, что и otrs. Он читает данные из файла сессии агента и передает их на сервер телефонии скрипту connect.php
3. PHP-скрипт connect.php на сервере asterisk
Скрипт connect.php, который находится на сервере asterisk "дает команду" совершить звонок. Он формирует так называемый call-файл и помещает его в специальный каталог asterisk-а, откуда тот его незамедлительно считывает и выполняет. Скрипт обязательно должен быть хорошо защищен от злоумышленников!

ToDo: надо бы добавить javascript также в шаблоны AgentTicketEmail и AgentTicketPhone

Re: Интеграция OTRS и Asterisk

Добавлено: 08 июн 2012, 15:48
alex
Javascript в шаблоне AgentTicketZoom.dtl:

Код: Выделить всё

--- AgentTicketZoom.dtl.orig	2012-06-07 22:48:01.000000000 +0400
+++ AgentTicketZoom.dtl	2012-06-08 11:21:35.000000000 +0400

@@ -786,6 +773,48 @@
     Core.Agent.TicketZoom.Init({ ArticleTableHeight: parseInt('$Env{"UserTicketZoomArticleTableHeight"}', 10)});
     Core.Config.Set('Ticket::Frontend::HTMLArticleHeightDefault', parseInt("$Config{"Ticket::Frontend::HTMLArticleHeightDefault"}" || 0, 10));
     Core.Config.Set('Ticket::Frontend::HTMLArticleHeightMax', parseInt("$Config{"Ticket::Frontend::HTMLArticleHeightMax"}" || 0, 10));
+
+    window.callPhony = function(phone) {
+        $.post("/phony.php", { phone: phone } );
+    };
+
+    function addPhony(jqelem) {
+        var phones = $(jqelem).attr("title").split(",");
+
+        jqelem.empty();
+        jqelem.removeAttr("title");
+
+        var first = true;
+        for (var i = 0; i < phones.length; i ++) {
+            var phoneNumber = phones[i].trim().split("+");
+
+            if (phoneNumber.length == 2)
+                var addPart = phoneNumber[1];
+            phoneNumber = phoneNumber[0];
+
+            var a = document.createElement("a");
+            a.href = "javascript:callPhony('" + phoneNumber + "')";
+            a.title = phoneNumber;
+            var text = document.createTextNode(phoneNumber);
+            a.appendChild(text);
+
+            if (!first)
+                jqelem.append(", ");
+            first = false;
+            jqelem.append(a);
+            if (addPart)
+                jqelem.append("+" + addPart);
+        }
+    }
+
+    $(document).ready(function(){
+        $("div.Content fieldset.TableLike.FixedLabelSmall label")
+        .each(function(index) {
+            if ($(this).text().search("$Text{"Phone"}") >= 0)
+                addPhony($(this).next("p.Value"));
+        });
+    });
 //]]></script>
 <!-- dtl:js_on_document_complete -->
 <!-- dtl:block:TicketZoomInit -->

Re: Интеграция OTRS и Asterisk

Добавлено: 08 июн 2012, 15:53
alex
phony.php

Код: Выделить всё

<?php
$config = array(
    'agenturl' => "http://amp.__________.ru/phony/connect.php",
// Данные для http-basic аутентификации к скрипту connect.php
    'agentuser' => "agentuser",
    'agentpass' => "agentpass",
// Путь к каталогу, где лежат сессии. PHP-скрипт должен иметь права на чтение файлов из него!
    'sessionpath' => "/opt/otrs-3.1.5/var/sessions/"
);

// === library ===

function call($caller, $callee) {
    global $config;
    $agent = $config['agenturl'];
    $user = $config['agentuser'];
    $pass = $config['agentpass'];

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $agent."?from=$caller&to=$callee");
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPAUTH,  CURLAUTH_BASIC);
    curl_setopt($ch, CURLOPT_USERPWD, $user.':'.$pass);
    $ret = curl_exec($ch);
    curl_close($ch);
}

function parseSession($sessionId) {
    global $config;
    $sessionFile = $config['sessionpath'] . $sessionId;
    if (!is_readable($sessionFile))
        return false;
    $sessionData = file($sessionFile, FILE_IGNORE_NEW_LINES);

    $sessionParsed = array();
    foreach ($sessionData as $record) {
        list($name, $value) = explode(":", $record);

        $value = base64_decode($value);

        if (preg_match("!([^\[]+)\[([^\]]+)\]!", $name, $matches)) {
            $name = $matches[1];
            $subname = $matches[2];

            if (!is_array($sessionParsed[$name]))
                $sessionParsed[$name] = array();
            $sessionParsed[$name][$subname] = $value;
        } else {
            $sessionParsed[$name] = $value;
        }
    }

    return $sessionParsed;
}

function determineOTRSAgentPhone($sessionId) {
    $session = parseSession($sessionId);
    if (!$session)
        return false;

    return $session['UserDynamicField_extension'];
}

// === entry point ===
$sessionId = $_COOKIE["Session"];
if (!preg_match("![\da-f]{34}!", $sessionId)) {
    header("HTTP/1.1 400 Bad Request");
    die("Wrong usage");
}

$agentPhone = determineOTRSAgentPhone($sessionId);
if (!$agentPhone) {
    header("HTTP/1.1 400 Bad Request");
    die("Wrong usage");
}

$phone = $_POST["phone"];
$phone = preg_replace("![^\d]+!", "", $phone);

call($agentPhone, $phone);

?>

Re: Интеграция OTRS и Asterisk

Добавлено: 08 июн 2012, 15:57
alex
Скрипт connect.php:

Код: Выделить всё

<?php
    header("Content-Type: text/plain; charset=utf-8");

    $rndfile = uniqid().'.call';
    $tmp = '/tmp';
    $dst = '/var/spool/asterisk/outgoing';

    file_put_contents("$tmp/$rndfile",
        'Channel: Local/'.$_GET["from"].'@from-internal'. "\n".
        'CallerID: Phony <' . $_GET["to"] . '>'. "\n".
        'MaxRetries: 5'."\n".
        'RetryTime: 3'."\n".
        'Context: from-internal'."\n".
        'Extension: '.$_GET["to"]
    );

    rename("$tmp/$rndfile", "$dst/$rndfile");
?>
Обязательно, защищайте этот скрипт всеми способами!

Re: Интеграция OTRS и Asterisk

Добавлено: 03 дек 2012, 15:26
adrenaline69
Пожалуйста как можно поподробнее по Javascript AgentTicketZoom.dtl
Не совсем понятно по этому скрипту. что в нем нужно изменять ? что добавлять? не просто же выделить и вставить в файл AgentTicketZoom.dtl.
Cпасибо!.

Re: Интеграция OTRS и Asterisk

Добавлено: 08 дек 2012, 10:36
alex
Нужно найти в этом файле строчку

Код: Выделить всё

Core.Config.Set('Ticket::Frontend::HTMLArticleHeightMax', parseInt("$Config{"Ticket::Frontend::HTMLArticleHeightMax"}" || 0, 10));
и вставить после нее то, что отмечено плюсами (плюсы, естественно, нужно убрать).

Re: Интеграция OTRS и Asterisk

Добавлено: 24 сен 2013, 13:29
BOYARIN
2 alex:

Все работает, спасибо за отличную идею реализации.
Правда Extension (из поля "Комментарий") я передаю прямо через JS (без парсинга сессии).

У меня вопрос: как же все-таки добавить динамические поля для агента? (чтобы сделать по красоте поле Extension, а не комментарий).
Весь инет перерыл - не могу найти.

Последняя инстанция - файл User.pm, но че-то как-то совсем не хочется его править.

Re: Интеграция OTRS и Asterisk

Добавлено: 25 сен 2013, 08:59
skeleton
Кто подскажет куда конкретно класть, в какую папку и прочее phony.php connect.php и что куда прикручивать.
Как то вся процедура поверхностно написана. можно конкретики в каждом действии что делать.

Re: Интеграция OTRS и Asterisk

Добавлено: 14 окт 2013, 14:46
alex
BOYARIN писал(а):2 alex:

Все работает, спасибо за отличную идею реализации.
Правда Extension (из поля "Комментарий") я передаю прямо через JS (без парсинга сессии).

У меня вопрос: как же все-таки добавить динамические поля для агента? (чтобы сделать по красоте поле Extension, а не комментарий).
Весь инет перерыл - не могу найти.

Последняя инстанция - файл User.pm, но че-то как-то совсем не хочется его править.
Добавил скриншот, вроде бы это все, что нужно сделать, но точно уже не помню.

Re: Интеграция OTRS и Asterisk

Добавлено: 12 окт 2016, 09:13
belko54
alex писал(а):Javascript в шаблоне AgentTicketZoom.dtl:

Код: Выделить всё

--- AgentTicketZoom.dtl.orig	2012-06-07 22:48:01.000000000 +0400
+++ AgentTicketZoom.dtl	2012-06-08 11:21:35.000000000 +0400

@@ -786,6 +773,48 @@
     Core.Agent.TicketZoom.Init({ ArticleTableHeight: parseInt('$Env{"UserTicketZoomArticleTableHeight"}', 10)});
     Core.Config.Set('Ticket::Frontend::HTMLArticleHeightDefault', parseInt("$Config{"Ticket::Frontend::HTMLArticleHeightDefault"}" || 0, 10));
     Core.Config.Set('Ticket::Frontend::HTMLArticleHeightMax', parseInt("$Config{"Ticket::Frontend::HTMLArticleHeightMax"}" || 0, 10));
+
+    window.callPhony = function(phone) {
+        $.post("/phony.php", { phone: phone } );
+    };
+
+    function addPhony(jqelem) {
+        var phones = $(jqelem).attr("title").split(",");
+
+        jqelem.empty();
+        jqelem.removeAttr("title");
+
+        var first = true;
+        for (var i = 0; i < phones.length; i ++) {
+            var phoneNumber = phones[i].trim().split("+");
+
+            if (phoneNumber.length == 2)
+                var addPart = phoneNumber[1];
+            phoneNumber = phoneNumber[0];
+
+            var a = document.createElement("a");
+            a.href = "javascript:callPhony('" + phoneNumber + "')";
+            a.title = phoneNumber;
+            var text = document.createTextNode(phoneNumber);
+            a.appendChild(text);
+
+            if (!first)
+                jqelem.append(", ");
+            first = false;
+            jqelem.append(a);
+            if (addPart)
+                jqelem.append("+" + addPart);
+        }
+    }
+
+    $(document).ready(function(){
+        $("div.Content fieldset.TableLike.FixedLabelSmall label")
+        .each(function(index) {
+            if ($(this).text().search("$Text{"Phone"}") >= 0)
+                addPhony($(this).next("p.Value"));
+        });
+    });
 //]]></script>
 <!-- dtl:js_on_document_complete -->
 <!-- dtl:block:TicketZoomInit -->
[/quote]
Код уже устарел? на OTRS 3.3.8 не взлетает(((