← Назад к справке

Открытые линии

Операторы и передача диалога

Рабочая справка по операторам открытых линий: как смотреть оператора сессии, присоединяться, перехватывать и передавать диалог через PHP и REST.

Таблицы можно использовать для чтения состояния диалога. Управляющие действия — взять, присоединиться, перехватить или передать — лучше выполнять штатными методами открытых линий: через \Bitrix\ImOpenLines\Operator в коробочном PHP или через REST.

Операторы

Оператор сессии хранится в данных открытой линии, а участники чата — в IM-связях.

Где смотреть оператора

В сессиях открытых линий обычно полезны поля OPERATOR_ID, CHAT_ID, STATUS, CONFIG_ID и даты сессии.

Для чтения можно использовать SessionTable. Для действий с диалогом лучше не менять эти поля напрямую, а использовать \Bitrix\ImOpenLines\Operator.

Получить диалоги оператора

<?php

use Bitrix\Main\Loader;
use Bitrix\ImOpenLines\Model\SessionTable;

Loader::includeModule('imopenlines');

/**
 * Получает последние сессии оператора.
 */
function fetchOperatorOpenLineSessions(int $operator_id, int $limit): array
{
    $sessions = [];

    $session_result = SessionTable::getList([
        'select' => [
            'ID',
            'CHAT_ID',
            'USER_CODE',
            'CONFIG_ID',
            'STATUS',
            'OPERATOR_ID',
            'DATE_CREATE',
            'CLOSE_DATE',
        ],
        'filter' => [
            '=OPERATOR_ID' => $operator_id,
        ],
        'order' => [
            'ID' => 'DESC',
        ],
        'limit' => $limit,
    ]);

    while ($session = $session_result->fetch()) {
        $sessions[] = $session;
    }

    return $sessions;
}

$sessions = fetchOperatorOpenLineSessions($operator_id, 50);

print_r($sessions);

Действия с диалогом

Для действий с диалогом нужен CHAT_ID открытой линии. В PHP с ним создаётся объект \Bitrix\ImOpenLines\Operator.

PHP: объект Operator

В коробочном PHP можно работать через класс \Bitrix\ImOpenLines\Operator. Обычно ему передают CHAT_ID открытой линии.

<?php

use Bitrix\Main\Loader;
use Bitrix\ImOpenLines\Operator;

Loader::includeModule('im');
Loader::includeModule('imopenlines');

/**
 * Создаёт объект оператора открытой линии.
 */
function createOpenLineOperator(int $chat_id): ?Operator
{
    if ($chat_id <= 0) {
        return null;
    }

    return new Operator($chat_id);
}

/**
 * Возвращает текст ошибки оператора.
 */
function fetchOpenLineOperatorError(Operator $operator): string
{
    $error = $operator->getError();

    if ($error === null) {
        return '';
    }

    if (method_exists($error, 'getMessage')) {
        return (string) $error->getMessage();
    }

    return print_r($error, true);
}

$operator = createOpenLineOperator($chat_id);

if ($operator === null) {
    echo 'Не указан CHAT_ID';
}

Действия выполняются от пользователя текущего PHP-контекста. Если код запускается из агента, cron или служебного скрипта, отдельно проверь, какой пользователь считается текущим и есть ли у него права на диалог.

PHP: присоединиться и перехватить

Для присоединения и перехвата у Operator есть методы joinSession() и interceptSession(). Они полезны, когда нужно выполнить действие из коробочного PHP-кода без REST-запроса.

<?php

use Bitrix\Main\Loader;
use Bitrix\ImOpenLines\Operator;

Loader::includeModule('im');
Loader::includeModule('imopenlines');

/**
 * Присоединяет текущего оператора к диалогу.
 */
function joinOpenLineDialog(int $chat_id): array
{
    $operator = createOpenLineOperator($chat_id);

    if ($operator === null) {
        return [
            'is_success' => false,
            'error_messages' => ['Не указан CHAT_ID'],
        ];
    }

    $is_success = (bool) $operator->joinSession();

    return [
        'is_success' => $is_success,
        'error_messages' => $is_success ? [] : [fetchOpenLineOperatorError($operator)],
    ];
}

/**
 * Перехватывает диалог текущим оператором.
 */
function interceptOpenLineDialog(int $chat_id): array
{
    $operator = createOpenLineOperator($chat_id);

    if ($operator === null) {
        return [
            'is_success' => false,
            'error_messages' => ['Не указан CHAT_ID'],
        ];
    }

    $is_success = (bool) $operator->interceptSession();

    return [
        'is_success' => $is_success,
        'error_messages' => $is_success ? [] : [fetchOpenLineOperatorError($operator)],
    ];
}

$join_result = joinOpenLineDialog($chat_id);
$intercept_result = interceptOpenLineDialog($chat_id);

print_r($join_result);
print_r($intercept_result);

Если метод вернул false, смотри ошибку через getError(). Частые причины: неверный CHAT_ID, это не чат открытой линии, текущий пользователь не определён или у него нет доступа.

PHP: передать оператору

Для передачи конкретному пользователю можно вызвать $operator->transfer() и передать TRANSFER_ID с ID сотрудника.

<?php

use Bitrix\Main\Loader;
use Bitrix\ImOpenLines\Operator;

Loader::includeModule('im');
Loader::includeModule('imopenlines');

/**
 * Передаёт диалог конкретному оператору.
 */
function transferOpenLineDialogToUser(int $chat_id, int $target_user_id): array
{
    if ($target_user_id <= 0) {
        return [
            'is_success' => false,
            'error_messages' => ['Не указан пользователь для передачи'],
        ];
    }

    $operator = createOpenLineOperator($chat_id);

    if ($operator === null) {
        return [
            'is_success' => false,
            'error_messages' => ['Не указан CHAT_ID'],
        ];
    }

    $is_success = (bool) $operator->transfer([
        'TRANSFER_ID' => $target_user_id,
    ]);

    return [
        'is_success' => $is_success,
        'error_messages' => $is_success ? [] : [fetchOpenLineOperatorError($operator)],
    ];
}

$transfer_result = transferOpenLineDialogToUser($chat_id, $target_user_id);

print_r($transfer_result);

Этот вариант ближе всего к REST-параметру USER_ID, но в PHP удобнее использовать универсальный TRANSFER_ID.

PHP: передать в очередь

Для передачи в очередь другой открытой линии в TRANSFER_ID передаётся строка вида queue123, где 123 — ID линии/очереди.

<?php

use Bitrix\Main\Loader;
use Bitrix\ImOpenLines\Operator;

Loader::includeModule('im');
Loader::includeModule('imopenlines');

/**
 * Формирует идентификатор очереди для передачи.
 */
function buildOpenLineQueueTransferId(int $queue_id): string
{
    return 'queue' . $queue_id;
}

/**
 * Передаёт диалог в очередь другой открытой линии.
 */
function transferOpenLineDialogToQueue(int $chat_id, int $queue_id): array
{
    if ($queue_id <= 0) {
        return [
            'is_success' => false,
            'error_messages' => ['Не указана очередь для передачи'],
        ];
    }

    $operator = createOpenLineOperator($chat_id);

    if ($operator === null) {
        return [
            'is_success' => false,
            'error_messages' => ['Не указан CHAT_ID'],
        ];
    }

    $is_success = (bool) $operator->transfer([
        'TRANSFER_ID' => buildOpenLineQueueTransferId($queue_id),
    ]);

    return [
        'is_success' => $is_success,
        'error_messages' => $is_success ? [] : [fetchOpenLineOperatorError($operator)],
    ];
}

$transfer_result = transferOpenLineDialogToQueue($chat_id, $queue_id);

print_r($transfer_result);

Не путай QUEUE_ID с ID пользователя в очереди. Для передачи в линию нужен именно ID очереди/линии, а в TRANSFER_ID он передаётся с префиксом queue.

PHP: пропустить, завершить, спам

У Operator есть и другие методы для операторских действий. Их проще оборачивать в одинаковый формат результата.

<?php

use Bitrix\Main\Loader;
use Bitrix\ImOpenLines\Operator;

Loader::includeModule('im');
Loader::includeModule('imopenlines');

/**
 * Пропускает диалог к следующему оператору очереди.
 */
function skipOpenLineDialog(int $chat_id): array
{
    $operator = createOpenLineOperator($chat_id);

    if ($operator === null) {
        return [
            'is_success' => false,
            'error_messages' => ['Не указан CHAT_ID'],
        ];
    }

    $is_success = (bool) $operator->skip();

    return [
        'is_success' => $is_success,
        'error_messages' => $is_success ? [] : [fetchOpenLineOperatorError($operator)],
    ];
}

/**
 * Завершает диалог текущего оператора.
 */
function closeOpenLineDialog(int $chat_id): array
{
    $operator = createOpenLineOperator($chat_id);

    if ($operator === null) {
        return [
            'is_success' => false,
            'error_messages' => ['Не указан CHAT_ID'],
        ];
    }

    $is_success = (bool) $operator->closeDialog();

    return [
        'is_success' => $is_success,
        'error_messages' => $is_success ? [] : [fetchOpenLineOperatorError($operator)],
    ];
}

/**
 * Помечает диалог как спам.
 */
function markOpenLineDialogAsSpam(int $chat_id): array
{
    $operator = createOpenLineOperator($chat_id);

    if ($operator === null) {
        return [
            'is_success' => false,
            'error_messages' => ['Не указан CHAT_ID'],
        ];
    }

    $is_success = (bool) $operator->markSpam();

    return [
        'is_success' => $is_success,
        'error_messages' => $is_success ? [] : [fetchOpenLineOperatorError($operator)],
    ];
}

$skip_result = skipOpenLineDialog($chat_id);
$close_result = closeOpenLineDialog($chat_id);
$spam_result = markOpenLineDialogAsSpam($chat_id);

print_r($skip_result);
print_r($close_result);
print_r($spam_result);

Для автоматизации лучше сначала проверить действие на тестовой открытой линии: эти методы меняют реальное состояние диалога, оператора, очереди и уведомлений.

REST: присоединиться к диалогу

Присоединение добавляет оператора к диалогу. В REST для этого используется imopenlines.session.join.

/**
 * Пример параметров REST-запроса для присоединения к диалогу.
 */
function buildJoinDialogPayload(int $chat_id): array
{
    return [
        'CHAT_ID' => $chat_id,
    ];
}

$payload = buildJoinDialogPayload($chat_id);

print_r($payload);

REST: перехватить диалог

Перехват переводит текущий диалог на оператора, который вызывает действие. В REST для этого используется imopenlines.session.intercept.

/**
 * Пример параметров REST-запроса для перехвата диалога.
 */
function buildInterceptDialogPayload(int $chat_id): array
{
    return [
        'CHAT_ID' => $chat_id,
    ];
}

$payload = buildInterceptDialogPayload($chat_id);

print_r($payload);

REST: передать диалог

Передача отправляет диалог другому оператору или в очередь. В REST для этого используется imopenlines.operator.transfer.

/**
 * Готовит параметры передачи диалога оператору.
 */
function buildTransferToUserPayload(int $chat_id, int $user_id): array
{
    return [
        'CHAT_ID' => $chat_id,
        'USER_ID' => $user_id,
    ];
}

/**
 * Готовит параметры передачи диалога в очередь.
 */
function buildTransferToQueuePayload(int $chat_id, int $queue_id): array
{
    return [
        'CHAT_ID' => $chat_id,
        'QUEUE_ID' => $queue_id,
    ];
}

/**
 * Готовит параметры передачи через универсальный TRANSFER_ID.
 */
function buildTransferPayload(int $chat_id, string $transfer_id): array
{
    return [
        'CHAT_ID' => $chat_id,
        'TRANSFER_ID' => $transfer_id,
    ];
}

$user_payload = buildTransferToUserPayload($chat_id, $user_id);
$queue_payload = buildTransferToQueuePayload($chat_id, $queue_id);
$transfer_payload = buildTransferPayload($chat_id, 'queue' . $queue_id);

print_r($user_payload);
print_r($queue_payload);
print_r($transfer_payload);

В новых REST-методах также встречается универсальный параметр TRANSFER_ID: ID пользователя или строка вида queue123.

Разница действий

Названия похожи, но смысл действий разный.

Взять, присоединиться, перехватить, передать

Действие PHP REST Как понимать
Взять Operator::answer() imopenlines.operator.answer Оператор берёт новый или ожидающий диалог в работу.
Присоединиться Operator::joinSession() imopenlines.session.join Оператор добавляется к текущему диалогу.
Перехватить Operator::interceptSession() imopenlines.session.intercept Диалог переходит к оператору, который выполняет перехват.
Передать Operator::transfer() imopenlines.operator.transfer Диалог отправляется другому оператору или в очередь.
Пропустить Operator::skip() imopenlines.operator.skip Диалог уходит следующему оператору по очереди.
Завершить Operator::closeDialog() imopenlines.operator.finish Текущий оператор завершает свой диалог.
Спам Operator::markSpam() imopenlines.operator.spam Диалог помечается как спам и закрывается.

Перед автоматизацией этих действий лучше проверить права пользователя, состояние сессии и текущего оператора. Прямое изменение таблиц для передачи диалога использовать не нужно.

Источники