artem
04.10.2018
1001

Click-call или звонок из web

В этой статье будет описан механизм реализации функции click-call, или как его еще принято называть в CRM-системах «вызов из карточки клиента».

Ресурсы

⦁ Настроенный веб сервер, php версии 5.6 и выше
⦁ Доступ к диалплану Asterisk
⦁ Доступ к AMI Asterisk

Задача: осуществить вызов номера клиента в один клик, с уведомлением если номер текущего оператора занят, при этом идентифицировать каждый звонок по карточкам клиентов.

Принципиальная схема работы

⦁ Заходим в нужную страницу (карточку)
⦁ Кликаем на записанном номере клиента
⦁ Алгоритм средствами AMI и PHP проверяет занятость оператора
⦁ Если оператор занят выводим оповещение и завершаем алгоритм
⦁ Если свободен вызываем оператора, до ответа в канал
⦁ Навешиваем созданному каналу UID карточки клиента
⦁ Вызываем клиента и по ответу объединяем каналы – состоится разговор
⦁ По завершении разговора, отлавливаем UID и записываем в дополнительное поле CDR-таблицы

Реализация

На стороне CRM

Набросаем простую тестовую страницу для демонстрации работы (все файлы можно будет найти в закрепленном архиве)
Её код имеет следующий вид:

<!DOCTYPE html>
<html>
<head>
<title>Clicl_to_call</title>
<link rel=’shortcut icon’ href=’favicon.png’ type=’image/png’>
<link rel=’stylesheet’ type=’text/css’ href=’style.css’>
</head>
<meta charset=’utf-8′>
<body class=’body’>
<form action=” method=’post’>
Источник вызова (оператор)<input type=’number’ name=’src’ /><br />
Назначение вызова (клиент)<input type=’number’ name=’dst’ /><br />
<button type=’submit’ name=’init_call’ value=’send’>Позвонить</button><br />
</form action=” method=’post’>
</body>
</html>
<?php

require_once “settings.php”;
require_once “core_function.php”;

//echo “<pre>”.print_r($_POST,true).”</pre>”;

if(isset($_POST[‘init_call’])){
if (ami_is_busy($_POST[‘src’])){
ami_call($_POST[‘src’],$_POST[‘dst’],’-=#’.date(“H:i:s”).’#=-‘);
echo “<h1>Свободно!</h1>”;
}
else{
echo “<h1>Занято!</h1>”;
}
}

?>

Не смотря на смешанное содержимое файл следует сохранить с расширением *.php
В итоге должно получится следующее:

Тестовая страница

Поскольку страница тестовая, здесь мы будем указывать и номер оператора и номер абонента.

Подключаемые файлы разберем чуть позже.

Закомментированный фрагмент:

//echo “<pre>”.print_r($_POST,true).”</pre>”;

Позволяет отобразить переданные формой аргументы для проверки их корректности. Отладка в действии выглядит следующим образом:

Передача аргументов

Источник – 712, назначение 102, форма была отправлена успешно – что и требовалось. Далее присутствует обработчик нажатия, который вызывает функцию проверки занятости оператора и в зависимости от результата вызывает инициирует вызов, либо уведомляет о невозможности такого.

Остановимся подробнее на самих функциях проверки и вызова. Они содержатся в отдельном файле и имеют следующий код:

function ami_request($ingect){
$answer = ”;
$socket = fsockopen(ami_host, ami_port, $ac_err_num, $ac_err_msg, 1);
if ($socket){
stream_set_timeout($socket, 1);
$action = “Action: loginrn”;
$action .= “Username: “.ami_user.”rn”;
$action .= “Secret: “.ami_pass.”rnrn”;
$action .= $ingect;
$action .= “Action: Logoffrnrn”;

//echo str_replace(“rn”,”<br />”,$action);

fwrite($socket,$action);
sleep(1);
$answer = fread($socket,8192);
fclose($socket);
}
//echo str_replace(“rn”,”<br />”,$answer);
return $answer;
}

function ami_peer_status($exten){
$res=ami_request(“Action: CoreShowChannelsrnrn”);
if(strpos($res,$exten)){
$st=strpos($res,’Local/’);
$fn=strpos($res,’ChannelState’)-2;
return substr($res,$st,$fn-$st);
}
else{
return ”;
}
}

function ami_call($num_from, $num_to, $taskId){
$action = “Action: Originatern”;
$action .= “Channel: Local/$num_from@ext-localrn”;
$action .= “Callerid: CRMrn”;
$action .= “Timeout: 15000rn”;
$action .= “Context: from-internalrn”;
$action .= “Exten: $num_torn”;
$action .= “Priority: 1rn”;
$action .= “Async: yesrnrn”;
if($taskId != ”){
$channel = ami_peer_status($num_from);
$action .= “Action: SetvarrnChannel: $channelrnVariable: USERFIELDrnValue: $taskIdrnrn”;
}
$res = ami_request($action);
echo “<h2>”.$taskId.”</h2>”;
return $res;
}

function ami_is_busy($exten){
$res = ami_peer_status($exten);
if($res != ”){
return false;
}else{
return true;
}
}

Опишем функции по порядку:

ami_request – выполняет обращение на сервер посредством AMI, передает на выполнение указанный в единственном аргументе ряд команд и возвращает результат выполнения.
ami_peer_status – обращается через функцию описанную выше на сервер и получает список активных каналов. В зависимости от наличия в нём внутреннего номера, переданного в качестве аргумента, возвращается пустое значение или имя канала.

ami_call – через AMI отправляет команду Originate для вызова и соединения двух номеров, а так же назначает дополнительную переменную каналу – которая будет отвечать за UID карточки клиента и записываться в CDR.

ami_is_busy – проверяет занятость оператора по номеру и возвращает булев тип значения.

Если раскрыть комментарии можно увидеть запрос и ответ AMI. Получится примерно следующее:

АМИ обращение

Смысл выделенных фрагментов:

При выполнении запроса на список каналов, авторизация пройдена успешно, запрос выполнен, в результате 0 активных каналов.

АМИ вызов

Выполняем вызов с 712го номера на 102, посредством логики АТС, добавляем каналу новую переменную, и устанавливаем ее равной текущему времени (для примера).

Демонстрация работы:

На стороне АТС

Звонок проходит. Оператор отвечает, идет вызов клиента, происходит разговор. Но это еще не всё. Требуется отловить новую переменную канала и записать в CDR, для этого дополним диалплан. Открываем ssh, находим в файле /etc/asterisk/extensions_additional.conf контекст

[macro-hangupcall]

И копируем файл /etc/asterisk/extensions_override_freepbx.conf всё, что касается экстеншена «s».

После чего добавляем в первый приоритет новую строчку:

exten => s,1,ExecIf($[“${CDR(cnam)}”=”CRM”]?Set(CDR(userfield)=${USERFIELD}))

Должно получится что-то похожее:

[macro-hangupcall]
include => macro-hangupcall-custom
exten => s,1,ExecIf($[“${CDR(cnam)}”=”CRM”]?Set(CDR(userfield)=${USERFIELD}))
exten => s,n,Set(CHANNEL(hangup_handler_wipe)=for-sox,s,1)
exten => s,n,ExecIf($[“${CALLFILENAME}”!=”” & “${CDR(recordingfile)}”=””]?Set(CDR(recordingfile)=${CALLFILENAME}.wav-mix.${MON_FMT})) //запись нового файла в CDR
exten => s,n(start),GotoIf($[“${USE_CONFIRMATION}”=”” | “${RINGGROUP_INDEX}”=”” | “${CHANNEL}”!=”${UNIQCHAN}”]?theend)
exten => s,n(delrgi),Noop(Deleting: RG/${RINGGROUP_INDEX}/${CHANNEL} ${DB_DELETE(RG/${RINGGROUP_INDEX}/${CHANNEL})})
exten => s,n(theend),ExecIf($[“${ONETOUCH_RECFILE}”!=”” & “${CDR(recordingfile)}”=””]?Set(CDR(recordingfile)=${ONETOUCH_RECFILE}))
exten => s,n,Hangup
exten => s,n,MacroExit()

Выходим, перечитываем диалплан и проверяем наличие нашей новой команды:

Диалплан

Тестируем. Производим вызов и запоминаем выведенное значение на странице.

Присвоение UID

Проверяем наличие в CDR

Проверка UID в CDR

Вот теперь звонок можно отследить и закрепить за определенной карточкой.

 
avatar
  Подписаться  
Уведомление о

Остались вопросы?

Я - Першин Артём, менеджер компании Voxlink. Хотите уточнить детали или готовы оставить заявку? Укажите номер телефона, я перезвоню в течение 3-х секунд.

VoIP оборудование

ближайшие курсы

ближайшие Вебинары

ONLINE

Why Choose HUGE?

Unlimited pre-designed elements

Each and every design element is designed for retina ready display on all kind of devices

User friendly interface and design

Each and every design element is designed for retina ready display on all kind of devices

100% editable layered PSD files

Each and every design element is designed for retina ready display on all kind of devices

Created using shape layers

Each and every design element is designed for retina ready display on all kind of devices