Скрипт правила обработки звонков

Описание скрипта

Скрипт задает правило обработки событий со звонками и условия его выполнения.

Место настройки скрипта

Форма "Редактирование правила обработки входящих звонков", вызванная на странице настройки модуля CTI, см. IP-телефония.

Алгоритм работы скрипта

  • Проверка поступления звонка на один из номеров единой службы поддержки. Номера задаются в скрипте (параметр GROUP_NUMBERS).
  • Идентификация оператора телефонии по номеру телефона, на который распределился звонок. В скрипте указывается код атрибута класса "Сотрудник" (employee) (USER_ATTR), по которому осуществляется поиск соответствия номеру распределенного звонка. Поиск осуществляется по полному совпадению.
  • Проверка наличия лицензии у оператора телефонии.
  • Поиск сотрудника-получателя услуг по номеру звонящего. В скрипте указывается код атрибута класса "Сотрудник" (employee) (CLIENT_ATTR), по которому осуществляется поиск соответствия номеру звонящего. Если по номеру звонящего найдено несколько сотрудников, то для предустановки значения на форме создания заявки берется первый найденный. Если по номеру звонящего ничего не найдено, может открываться форма добавления без привязки к контрагенту
  • Проверка, является ли сотрудник-оператор телефонии участником команды, ответственной за телефонию, авторизован ли он в системе и находится ли в интерфейсе оператора на момент совершения вызова.

Когда выполняется скрипт

Момент запуска скрипта зависит от типа подключения к серверу IP-телефонии:

  • Подключение к серверу телефонии со стороны Service Desk (только для Asterisk) — сервер SMP постоянно подключен к серверу IP-телефонии и слушает все происходящие события на сервере.

    Скрипт инициируется следующими событиями:

    • Поступление звонка свободному оператору. Звонки, поступившие на групповой номер, но не распределенные на оператора, скрипт не инициируют.
    • Поднятие трубки для звонка, который ранее находился в очереди к оператору.

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

  • Вызов методов REST API Service Desk со стороны сервера телефонии — сервер телефонии выполняет подключение к серверу SMP при поступлении звонка свободному оператору или поднятии трубки и отправляет REST-запрос с данными о звонке.

    Скрипт инициируется при получении REST-запроса, в скрипт в качестве параметров передаются данные из запроса.

  • Подключение к сервису Naumen Contact Center со стороны Service Desk (WSS) — сервер SMP постоянно подключен к сервису Naumen Contact Center и слушает все происходящие там событиям. Аналогично поведению при подключению к Asterisk.

Результат выполнения скрипта

В результате выполнения скрипта в сторону клиентского приложения (браузера пользователя) отправляется сообщение (Comet), открывающее в браузере:

  • форму добавления объекта или карточку объекта (если найден один объект);
  • список объектов, найденных в результате поиска;
  • стандартное сообщение, что объекты не найдены.

Сообщение (Comet) отправляется только участникам ответственной команды.

Обработка событий на сервере телефонии происходит параллельно. Каждое новое событие инициирует скрипт не дожидаясь окончания обработки предыдущего события.

Переменные и их значения

Глобальные переменные:

  • user — пользователь, инициализировавший событие. Является объектом класса "Сотрудник" (employee). Если событие инициализировал суперпользователь, то user=null.
  • ip — ip-адрес рабочего места пользователя user. Если действие выполняется автоматически системой (а не пользователем), то переменная не определяется.
  • appVersion — версия приложения.
  • api — используется для обращения к методам api, например api.utils, api.ldap, api.timing, см. Методы API;
  • modules — используется для обращения к скриптовому модулю и конкретному методу, определенному в нем, с помощью конструкции: modules.{код модуля}.{имя метода}({параметры метода}...), см. Скрипт текста модуля;
  • logger — используется для отладки скриптов и позволяет вывести в лог на указанный уровень переданную строку, см. Оформление и отладка скриптов.
  • utils — синоним api.utils.

Переменные контекста:

  • GROUP_NUMBERS — номера службы поддержки;
  • USER_ATTR — атрибут для поиска сотрудника (оператора телефонии), на которого распределился звонок;
  • CLIENT_ATTR — атрибут для поиска сотрудника (получателя услуг);
  • MODE — режим открытия страницы с карточкой звонящего сотрудника: new (в новом окне), current (в текущем окне);
  • connectedNumber — номер, с которым произошло соединение;
  • callerNumber — номер звонящего абонента;
  • callStatus — условие открытия формы добавления объекта или карточки объекта:

    Для Asterisk:

    • Ringing (в момент поступления входящего звонка оператору);
    • Connected (в момент поднятия трубки оператором телефонии).

    Для Naumen Contact Center (NCC):

    • NEW (в момент поступления входящего звонка);
    • CONNECTED (в момент, когда пользователь принял звонок);
    • ENDED (в момент, когда пользователь закончил разговор и положил трубку).
  • sessionId — идентификатор сессии звонка в Naumen Contact Center;
  • originalMessage — объект сообщения от Naumen Contact Center.

Примеры скрипта

1. Скрипт обработки входящего звонка, открывающий форму добавления. Подключение к Asterisk.

Copy
import static ru.naumen.core.shared.Constants.Employee.NOT_LICENSED_USER

public class Constants {
    //номера единой службы поддержки
    static def GROUP_NUMBERS = ['600']; 
    //атрибут для поиска сотрудника, у которого откроется форма объекта
    static def USER_ATTR = 'internalPhoneNumber'; 
    //атрибут для поиска контрагента
    static def CLIENT_ATTR = 'cityPhoneNumber'; 
    //режим открытия формы добавления: //new - в новом окне, current - в текущем окне
    static def MODE = 'new'; 
}
 
def processCall(callerNumber, connectedNumber, groupNumber, callStatus) {
// Проверка поступления звонка на один из номеров единой службы поддержки.
    if (!Constants.GROUP_NUMBERS.contains(groupNumber)) {
    return false;
}
 
// Определение адресатов вызова - лицензированных сотрудников, чей номер телефона совпадает с номером, с которым произошло соединение
def users = utils.find('employee', [(Constants.USER_ATTR) : connectedNumber]).findAll {
it.license != NOT_LICENSED_USER };
// Определение инициатора вызова, т.е. сотрудника- получателя услуг, чей номер телефона совпадает с номером звонящего абонента
def client = utils.findFirst('employee', [(Constants.CLIENT_ATTR) : callerNumber, ('removed') : false]);
def clientTitle = client ? client.title : callerNumber;
// Если ни один адресат вызова не найден, то в лог записывается соответствующее сообщение 
if (users.empty) {
    logger.info("Сотрудник с номером телефона ${connectedNumber} не найден в системе (звонок с номера ${callerNumber} от ${clientTitle})");
    return false;
}
// Проверка, что сотрудник является участником команды, ответственной за телефонию, авторизован в системе и находится в интерфейсе оператора
def offlineUsers = users.findAll {
 user -> !api.cti.isListenMessages(user.login)
};
// Запись в логе системы адресатов вызова, не авторизованных в системе на момент совершения вызова
offlineUsers.each() { user ->
    logger.info("Сотрудник ${user.title} с номером телефона ${connectedNumber} не авторизован в системе на момент совершения звонка (звонок с номера ${callerNumber} от ${clientTitle})");
}
// Генерация ссылки на форму добавления запроса с указанием инициатора вызова в качестве контрагента запроса
def url = api.web.add('serviceCall', client, [:]);

// Если адресата вызова определить не удалось, в ссылку добавляется параметр, при котором на форме добавления запроса отображается контент "Выбор контрагента"
if (!client) {
//выбор контрагента появляется только, если проставить этот флаг
    url += '!{\"fast\":\"true\"}'; 
}
// Для каждого авторизованного адресата вызова открывается ссылка, сгенерированная на предыдущем шаге
(users - offlineUsers).each() { user ->
    api.cti.open(user.login, url, Constants.MODE);
}
return true;
}
processCall(callerNumber, connectedNumber, groupNumber, callStatus);

2. Скрипт обработки входящего звонка, открывающий карточку объекта. Подключение к Asterisk.

Copy
import static ru.naumen.core.shared.Constants.Employee.NOT_LICENSED_USER
public class Constants {
//номера единой службы поддержки
static def GROUP_NUMBERS = ['600'];
//атрибут для поиска сотрудника, у которого откроется карточка
static def USER_ATTR = 'internalPhoneNumber'; 
// режим открытия страницы с результатами быстрого поиска по номеру, с которого производится вызов или карточки звонящего сотрудника: 
// new - в новом окне, current - в текущем окне 
static def MODE = 'new'; 
}
// Проверка поступления звонка на один из номеров службы поддержки
def processCall(callerNumber, connectedNumber, groupNumber, callStatus) {
if (!Constants.GROUP_NUMBERS.contains(groupNumber)) {
return false;
}
// Определение адресатов вызова - лицензированных сотрудников, чей номер телефона совпадает с номером, с которым произошло соединение
def users = utils.find('employee', [(Constants.USER_ATTR) : connectedNumber]).findAll {
 it.license != NOT_LICENSED_USER };
// Если ни один адресат вызова не найден, то в лог записывается соответствующее сообщение
if (users.empty) {
logger.info("Сотрудник с номером телефона ${connectedNumber} не найден в системе (звонок с номера ${callerNumber})");
return false;
}
// Проверка, что сотрудник является участником команды, ответственной за телефонию, авторизован в системе и находится в интерфейсе оператора
def offlineUsers = users.findAll { user -> !api.cti.isListenMessages(user.login)};
offlineUsers.each() { user ->
logger.info("Сотрудник ${user.title} с номером телефона ${connectedNumber} не авторизован в системе на момент совершения звонка (звонок с номера ${callerNumber})");
}
// Формирование ссылки на результаты быстрого поиска по номеру, с которого производится вызов. 
//Если был найден только один объект, то его карточка должна открыться автоматически
def url = api.web.getBaseUrl() + "#search:${callerNumber}";
// Для каждого авторизованного адресата вызова открывается ссылка, сгенерированная на предыдущем шаге
(users - offlineUsers).each() { user ->
api.cti.open(user.login, url, Constants.MODE);
}
return true;
}
processCall(callerNumber, connectedNumber, groupNumber, callStatus);

3. Скрипт для обработки звонка. Подключение к Naumen Contact Center (NCC)

Copy
def client = utils.findFirst('employee', ['cityPhoneNumber' : callerNumber, 'removed' : false]);
def user = utils.findFirst('employee', ['internalPhoneNumber' : connectedNumber, 'removed' : false]);
if (!user) {
   logger.info("Сотрудник с номером телефона ${connectedNumber} не найден в системе (звонок с номера ${callerNumber})");
   return false;
}
// проверка доступности оператора
if(!api.cti.isListenMessages(connectedNumber)){
   logger.info("Сотрудник ${connectedNumber} не авторизован в системе на момент совершения звонка (звонок с номера ${callerNumber})");
   return false;
}
if('CONNECTED'!=callStatus)
{
  return false;
}

def url = api.web.add('serviceCall', client, [:]);
if (client == null) {
//выбор контрагента появляется только, если проставить этот флаг
  url += '!{\"fast\":\"true\"}';  
}
// Открытие URL в браузере оператора телефонии, на текущей вкладке
api.cti.open(connectedNumber,url,'current')
return true