Перейти к содержанию

Примеры

HTTP запросы в скриптах

Бот может обмениваться данными с другими системами, наиболее часто это происходит с использованием протокола HTTP и REST API. Запросы можно выполнять через скрипты, это позволяет:

  • отправлять данные, полученные в ходе беседы, в другие системы
  • получать данные с внешних ресурсов и использовать их в беседе

Пример выполнения GET запроса на внешний ресурс

В данном примере показаны следующие ключевые моменты:

  • выполнение HTTP GET запроса и получение данных в формате JSON
  • сохранение полученных данных в переменные бота
  • обработка ошибок в сценарии с помощью ветви "Переход в случае ошибок"
TypeScript
//Импортируем модуль для работы с HTTP запросами
//HTTP запросы в скриптах можно выполнять только с помощью axios
const axios = require('axios');

//Выполнение запроса через метод axios.get
const response = await axios.get('https://jsonplaceholder.typicode.com/posts/1');

//Содержимое ответа должно быть следующим
/*
{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
 */

//Сохраняем в логи статус и данные, которые вернул сервер в результате запроса
context.output.logger.log('Статус', response.status);
context.output.logger.log('Данные', response.data);

//Если выполнение запроса завершилось успешно, то записываем данные в переменные,
//с помощью которых можно отобразить результат пользователю
if (response.status === 200) {
    context.output.variables.zagolovok_stati = response.data.title;
    context.output.variables.tekst_stati = response.data.body;
}

По ссылке можно импортировать рабочего бота со скриптом из примера. Результатом взаимодействия с ботом и выполнения скрипта будет ответ в виде заголовка и текста тестовой статьи, которую вернул внешний ресурс.

В примере выше отсутствует блок try-catch для обработки ошибок выполнения запроса. В таком случае рекомендуем использовать ветвь "Переход в случае ошибки" у элемента "Скрипт" в конструкторе сценария бота:

image

Пример выполнения POST запроса в ELMA365 для создания элемента приложения "Заявка"

В данном примере показаны следующие ключевые моменты:

  • создание элемента приложения в ELMA365 с помощью HTTP запроса к API ELMA365 с использованием токена авторизации в заголовке запроса
  • передача данных из переменной бота в ELMA365
  • сохранение полученных данных в переменные бота
  • обработка ошибок через блок try/catch
  • в конструкторе бота с помощью элемента "Условное ветвление" и логической переменной в зависимости от результата выполнения запроса показываем ответ от сервера или сообщение об ошибке
TypeScript
//Импортируем модуль для работы с HTTP запросами
//HTTP запросы в скриптах можно выполнять только с помощью axios
const axios = require('axios');

//Адрес и токен доступа к ELMA365, они должны быть заполнены в настройках бота,
//на вкладке Интеграции
const elma365url = context.input.settings.integrations.elma365.apiUrl;
const elma365xToken = context.input.settings.integrations.elma365.xToken;

//Конфигурация запроса
const request = {
    method: 'post',
    baseURL: elma365url,
    url: '/pub/v1/app/other/prilozhenie/create',
    data: {
        //Все входящие значения создаваемых приложений или запускаемых бизнес-процессов
        //должны передаваться в структуре context
        context: {
            //Переменная с именем "Название" должна быть создана
            __name: context.input.variables.nazvanie,
        },
    },
    //X-Token должен быть передан в заголовке запроса
    headers: {
        'X-Token': elma365xToken,
    },
};

//Для обработки ошибок используется блок try-catch
try {
    //Выполнение запроса
    const response = await axios(request);
    //Сохраняем в лог статус код результата запроса
    context.output.logger.log('Статус', response.status);
    //Сохраняем в переменные идентификатор и имя созданного элемента приложения
    context.output.variables.id_elementa_prilozheniya = response.data.item.__id;
    context.output.variables.imya_elementa_prilozheniya = response.data.item.__name;
    //С помощью логической переменной сообщаем об удачном статусе выполнения
    context.output.variables.uspeshno = true;
    //Любой статус код не равный 2XX приведет к возникновению ошибки,
    //обработка которой находится в блоке catch
} catch (e) {
    //Подобным образом можем проверить код статуса ошибки и отобразить
    //соответствующее сообщение
    if (e.response.status === 401) {
        //Записываем в текстовую переменную сообщение об ошибке
        context.output.variables.oshibka = 'Неверно указан токен авторизации';
    } else if (e.response.status === 400) {
        //Если сервер получил запрос, но вернул ответ с сообщением Bad Request,
        //сохраняем в переменную сообщение об ошибке, которую вернул сервер
        context.output.variables.oshibka = e.response.data.error;
    } else {
        //Записываем в текстовую переменную сообщение об ошибке
        context.output.variables.oshibka = e.message;
    }
    //Записываем в лог сообщение об ошибке с ее логгированием
    context.output.logger.error('Во время создания элемента приложения произошла ошибка', e);
}

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

Обратите внимание, для корректной работы бота со скриптом из примера необходимо перейти в **Настройки бота > Интеграции ** и указать актуальные данные.

Подробнее о работе с HTTP запросами с помощью библиотеки axios можно ознакомиться на официальном сайте.

Проверка канала в элементе IF-ELSE

Простое условие

В скриптах доступна возможность обратиться к переменной окружения бота. С помощью неё можно получить текущий канал, в котором был вызван бот.

Пример переменной окружения
Text Only
{
  subjects: {
    agentStage: {
      AgentId: null,
      DialogsVersioningEnabled: true,
      TrainResult: [Object],
      Settings: null,
      TenantId: '507f1f77bcf86cd799439020',
      Id: '633bfcaee8546a6985b45576',
      Version: '1.0',
      CreatedBy: '630f4b0d903f48b459ca5c15',
      ModifiedBy: '630f4b0d903f48b459ca5c15',
      CreatedOn: '2022-10-04T13:28:14.2052956+04:00',
      ModifiedOn: '2022-10-04T13:28:51.0106851+04:00'
    },
    agent: {
      Type: 1,
      Name: 'Проверка канала в условии',
      Description: "Простой бот 'Проверка канала в условии'",
      StagingAgentStageId: '633bfcaee8546a6985b45577',
      ProductionAgentStageId: '633bfcaee8546a6985b45576',
      CreationStatus: [Object],
      TenantId: '507f1f77bcf86cd799439020',
      Id: '633bfcaee8546a6985b45578',
      Version: '1.0',
      CreatedBy: '630f4b0d903f48b459ca5c15',
      ModifiedBy: '630f4b0d903f48b459ca5c15',
      CreatedOn: '2022-10-04T13:28:14.2074951+04:00',
      ModifiedOn: '2022-10-04T13:28:14.2074951+04:00'
    },
    person: {
      FirstName: '[unknown]',
      LastName: '[unknown]',
      MiddleName: '[none]',
      EMail: '[unknown]',
      Tel: '[unknown]',
      AdditionalInfo: '[none]',
      GroupIds: [],
      TenantId: null,
      Id: '000000000000000000000000',
      Version: null,
      CreatedBy: null,
      ModifiedBy: null,
      CreatedOn: '0001-01-01T00:00:00+00:00',
      ModifiedOn: '0001-01-01T00:00:00+00:00'
    }
  },
  accounts: {
    contact: {
      ChannelId: 'directline',
      AgentId: null,
      AgentStageId: '633bfcaee8546a6985b45576',
      PersonId: '000000000000000000000000',
      AgentStageAccountId: '000000000000000000000000',
      PersonAccountId: '000000000000000000000000',
      ExternalBotId: 'bot',
      ExternalUserId: 'r_1664875743',
      State: 0,
      TenantId: null,
      Id: '000000000000000000000000',
      Version: null,
      CreatedBy: null,
      ModifiedBy: null,
      CreatedOn: '0001-01-01T00:00:00+00:00',
      ModifiedOn: '0001-01-01T00:00:00+00:00'
    },
    person: {
      ChannelId: 'directline',
      PersonId: '000000000000000000000000',
      ExternalId: 'r_1664875743',
      ExternalName: 'user',
      State: 0,
      TenantId: null,
      Id: '000000000000000000000000',
      Version: null,
      CreatedBy: null,
      ModifiedBy: null,
      CreatedOn: '0001-01-01T00:00:00+00:00',
      ModifiedOn: '0001-01-01T00:00:00+00:00'
      structureId: '633bfcd3e8546a6985b4558d'
    },
    secrets: {}
}

Доступ к данному объекту можно получить в скриптах с помощью контекста:

TypeScript
1
2
3
// Предварительно в боте должна быть создана переменная с типа «Текст», 
// в которую будет записан текущий канал
context.output.variables.channelId = context.input.environment.accounts.agentStage.ChannelId

Далее необходимо настроить условное ветвление и проверять канал в переменной:

image

Пример условий в редакторе сценариев:

image

Результат работы условия:

image

Сложное условие

При работе в ELMA ChatDesk могут возникнуть ситуации, когда необходимо проверить, что общение с ботом происходит через Линии ELMA 365 и Telegram.

Пример переменной окружения ChatDesk + Telegram:

Text Only
1
2
3
4
5
6
7
8
9
{
  conversationId: '634011e4874aaf55e06c24a3',
  serviceUrl: 'https://example.t-elma365.ru/pub/v1/messenger/accept-bot-message',
  channelData: {
    elma365: { session: {}, client: {}, options: {} },
    originalChannelId: 'telegram_bot'
  },
  structureId: '63403524874aaf55e06c267c'
}
TypeScript
1
2
3
4
5
//Предварительно в боте должны быть созданы перменные с типом "текст", 
//в которую будут записаны каналы
context.output.variables.channelId = context.input.environment.accounts.agentStage.ChannelId
//Записываем в переменную значение поля originalChannelId, если объект channelData существует
context.output.variables.originalChannelId = context.input.environment.properties.channelData?.originalChannelId;

Далее необходимо настроить условное ветвеление и проверять канал в переменной:

image

Пример условий в редакторе сценариев:

image

Результат работы условия:

image

Получить имя файла и content-type

С помощью ботов можно обмениваться файлами, боты могут принимать и отправлять файлы в чате с клиентом. Бот может принимать файлы и проверять, соответствует ли файл нужному формату по атрибуту content type.

В данном примере показаны следующие ключевые моменты:

  • получение из файловой переменной типа файла и имени
  • сохранение полученных данных в переменные бота
TypeScript
let n = '', t = '';

// в переменной может  быть сохранен один или несколько файлов
// во втором случае в переменной будет массив 
if (Array.isArray(context.input.variables.fail)) {
    // берем первый файл в маассиве если их несколько
    n = context.input.variables.fail[0].name;
    t = context.input.variables.fail[0].contentType;
} else {
    n = context.input.variables.fail.name;
    t = context.input.variables.fail.contentType;
}

// записываем атрибуты в переменные, чтобы в сценарии можно было их использовать, например, в условии
context.output.variables.imya_faila = n;
context.output.variables.tip_faila = t;

В скрипте используется переменная с именем Файл имеет с типом файл.

image

Получение conversationId

Если требуется получить ИД текущей беседы (conversationId), можно воспользоваться следующим примером скрипта:

TypeScript
context.output.variables.conversationid = context.input.environment.properties.conversationId;

В данном примере в переменную с именем conversationId записывается ИД текущей беседы.

По ссылке можно скачать и импортировать рабочего бота со скриптом из примера. После запуска бота появится сообщение с идентификатором беседы.

Получение имени пользователя Telegram из ELMA365

Данный пример скрипта позволяет получить данные о пользователе из ELMA365, который общается с ботом через Telegram. Пример актуален для бота, подключенного к линии ELMA365, взаимодействие с которой осуществляется через Telegram.

В примере показаны следующие ключевые моменты:

  • получение информации о сессии и о клиенте в ELMA365 с помощью HTTP запроса к API ELMA365 с использованием токена авторизации в заголовке запроса
  • сохрениене полученных имени пользователя и логина в переменные
TypeScript
// Импортируем модуль для работы с HTTP запросами
// HTTP запросы в скриптах можно выполнять только с помощью axios
const axios = require('axios');

// Адрес и токен доступа к ELMA365, они должны быть заполнены в настройках бота,
// на вкладке Интеграции
let token = context.input.settings.integrations.elma365.xToken;
let baseUrl = context.input.settings.integrations.elma365.apiUrl;

// Идентификатор сессии линий
let sessionId = context.input.environment.properties?.channelData?.elma365?.session?.id

// Загружаем данные о сессии
const sessionInfo = (await axios({
    method: 'get',
    url: baseUrl + 'pub/v1/messenger/bot-api/sessions/' + sessionId,
    headers: {
        'X-Token': token
    }
})).data;

// Данные о сессии имеют такой вид 
/*
  {
      "id":"b5ea61f5-81d5-413a-8625-b1cf3605a61c",
      "name":" Наиль  - 2023-09-14",
      "channelId":"cf90bc0a-617b-4cd3-9b96-c50a73999857",
      "clients":["e67a6c37-ff54-43a8-b69b-7d9dd8e0797c"],
      "userId":null,
      "userGroupId":null,
      "lineId":"6cdad124-5fe2-4c92-8c2f-3b304b792982",
      "state":"assigned_to_bot"
   }
*/

// Нам нужен идентификатор клинта
const clientId = sessionInfo.clients[0];

// Загружаем данные о клиенте
const clientInfo = (await axios({
    method: 'get',
    url: baseUrl + 'pub/v1/messenger/bot-api/clients/' + clientId,
    headers: {
        'X-Token': token
    }
})).data;

// Данные о клиенте имеют следующий вид
/*
{
    "id":"e67a6c37-ff54-43a8-b69b-7d9dd8e0797c",
    "integrationId":"cf90bc0a-617b-4cd3-9b96-c50a73999857",
    "createdAt":"2023-04-21T09:04:59.115151721Z",
    "updatedAt":"2023-04-21T09:04:59.115151845Z",
    "externalId":"226262212|226262212",
    "username":"nocodenocry",
    "fullName":"Наиль",
    "phoneNumber":"",
    "avatar":"",
    "avatarId":"b3c9105e-1bae-4fcc-8033-17f31bdcd781",
    "extraData":{},
    "applicationItem":null
}
*/

// Нам нужны fullName и username
let fullName = clientInfo.fullName;
let username = clientInfo.username;

// Записываем полученные данные в переменные бота
context.output.variables.klient_imya = fullName;
context.output.variables.klient_login = username;

По ссылке можно скачать и импортировать сценарий со скриптом из примера. После запуска сценария появится сообщение с именем и логином пользователя Telegram.

Постраничное отображение кнопок

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

TypeScript
// Максимальное количество кнопок на странице
const MAX_PAGE_SIZE = 4;
// Названия кнопок, отвечающих за навигацию по страницам
const NEXT_PAGE_BUTTON_NAME = 'Next';
const PREVIOUS_PAGE_BUTTON_NAME = 'Previous';

onInputDialogBegin("employee", async (botEvent) => {
  // Список содержит ФИО сотрудников, которые предлагаются на выбор пользователю
  // Для примера данные уже содержатся в массиве, но могут быть загружены извне
  const employees = [
    "Кожевников А. И.",
    "Орлов С. А.",
    "Павлова С. К.",
    "Руднева В. Н.",
    "Козлова К. Т.",
    "Кожевников Л. Ф.",
    "Панов Д. Л.",
    "Орлов Д. Д.",
    "Полякова М. А.",
    "Дмитриев А. М.",
    "Шубин Т. Т.",
    "Калинина М. М.",
    "Гаврилова В. Н.",
    "Кузнецова Н. Ф.",
    "Овсянников А. В.",
    "Николаев М. К.",
    "Козина В. Т.",
    "Давыдова А. Н.",
    "Кузин Н. М.",
    "Евдокимов Л. А.",
    "Савин А. П.",
    "Хохлов К. В.",
    "Беликов Р. И."
  ];

  // Определяем максимальное количество страниц
  const maxPageCount = ~~(employees.length / MAX_PAGE_SIZE);
  // В отдельной переменной храним номер текущей страницы
  let currentPage = context.input.variables.current_page ?? 0;
  // Если пользователь выбрал переход на следующую страницу, то увеличиваем номер текущей
  if (context.input.variables.employees === NEXT_PAGE_BUTTON_NAME) {
    currentPage = currentPage + 1;
  }
  // Если пользователь выбрал переход на предыдущую страницу, то уменьшаем номер текущей
  if (context.input.variables.employees === PREVIOUS_PAGE_BUTTON_NAME) {
    currentPage = currentPage - 1;
  }

  // Определяем кнопки, которые будут находиться на текущей странице
  const currentPageButtons = employees.slice(currentPage * MAX_PAGE_SIZE, (currentPage + 1) * MAX_PAGE_SIZE);
  // Добавляем кнопки, отвечающие за навигацию по страницам
  if (currentPage > 0) {
    currentPageButtons.push(PREVIOUS_PAGE_BUTTON_NAME);
  }
  if (currentPage < maxPageCount) {
    currentPageButtons.push(NEXT_PAGE_BUTTON_NAME);
  }

  // Передаем кнопки текущей страницы для отображения пользователю
  context.output.result.suggestedActions = currentPageButtons;
  // Сохраняем в переменную номер текущей страницы
  context.output.variables.current_page = currentPage;
});

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