Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 40 Next »

Переписка с пользователями

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

Короткий опрос пользователя и возврат ответов

Сценарий ведет небольшую переписку с пользователем и возвращает ответы в зависимости от реакции пользователя.

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

 Нажмите здесь, чтобы развернуть пример сценария
//
// Сценарий простой переписки с пользователем
//
// Приветственное сообщение и первый вопрос
сообщениеПользователю.сШаблоном("Добрый день! Сейчас я помогу!") +
задатьПользователюВопрос("Я правильно понял, что вы заблудились в лесу?").сВариантамиОтвета("Да", "Нет").сохранитьРезультат("answer_1")+
//
// Если пользователь выберет нет - сценарий завершается
если("{answer_1} == Нет").то(
    сообщениеПользователю.сШаблоном("Ок, тогда я отключаюсь. Всего хорошего!") +
    завершить
)+
//
// Сообщения пользователю и начало опроса
сообщениеПользователю.сШаблоном("Если вы заблудились в лесу - главное сохранять спокойствие!") +
сообщениеПользователю.сШаблоном("Ваша главная задача - решить куда идти и выйти из леса.") +
задатьПользователюВопрос("Посмотрите вокруг - вы видите деревья?").сВариантамиОтвета("Да", "Нет").сохранитьРезультат("answer_2")+
//
// Пользователь ответил Нет - сценарий возвращает сообщение для этого случая и завершается 
если("{answer_2} == Нет").то(
    сообщениеПользователю.сШаблоном("Поздравляю! Вы не в лесу. Проблема решена. Всего хорошего") +
    завершить
)+
//
// Пользователь ответил Да - сценарий продолжает передавать сообщения пользователю
сообщениеПользователю.сШаблоном("Посмотрите на мох на стволах деревьев. Мох зеленый, зеленый цвет успокаивает.") +
сообщениеПользователю.сШаблоном("Обратите внимание на какой стороне дерева чаще всего встречается мох - это направление на север.") +
сообщениеПользователю.сШаблоном("Теперь важно вспомнить как вы пришли сюда и идти обратно.") +
задатьПользователюВопрос("Вы можете вспомнить откуда пришли?").сВариантамиОтвета("Да","Нет").сохранитьРезультат("answer_3") +
//
// Пользователь ответил Нет - сценарий возвращает сообщение для этого случая и завершается
если("{answer_3} == Нет").то(
    сообщениеПользователю.сШаблоном("Тогда вам все равно куда идти. Идите на север, ориентируясь на мох. Всего хорошего и удачи!") +
    завершить  
) + 
//
// Пользователь выбрал вариант Да - сценарий возвращает сообщение для этого случая и завершается
сообщениеПользователю.сШаблоном("Отлично! Идите обратно. Всего хорошего и удачи!") +
завершить

Короткий опрос пользователя и передача запроса операторам

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

 Нажмите здесь, чтобы развернуть пример сценария
//
// Сценарий первичного анкетирования и передачи запроса операторам
//
// Начинается опрос
задатьПользователюВопрос("Уточните, пожалуйста, что для Вас является приоритетным?").сВариантамиОтвета("Получение максимальной суммы","Простота получения").сохранитьРезультат("UserSaid_Priority")+
задатьПользователюВопрос("Укажите, на какие цели предназначается займ?").сохранитьРезультат("UserSaid_Priority_2")+
сообщениеПользователю.сШаблоном("Пожалуйста не закрывайте окно чата оператор подберет для Вас наиболее выгодное предложение!")+
//
// завершаем сценарий и передаем диалог операторам
завершитьИПеревестиНаОператора

Анкетирование пользователя и сохранение результатов в профиле пользователя

Сценарий проверит данные в профиле пользователя по списку и попросит пользователя указать отсутствующие данные. Список переменных указывается в самом сценарии. Для каждого вопроса предусмотрена кнопка Пропустить, нажатие пользователя на эту кнопку фиксируется в переменной пользователя как символ -.

 Нажмите здесь, чтобы развернуть пример сценария
//
// Сценарий заполнения данных пользователя
//
установитьПеременную("enteredEmail", "") +
установитьПеременную("enteredPhone", "") +
установитьПеременную("enteredFIO", "") +
установитьПеременную("enteredTall", "") +
установитьПеременную("enteredRegion", "") +
установитьПеременную("enteredLink", "") +
установитьПеременную("enteredCheck", "") +
//
// Опрос
если("{userFullName} == ").то(
	задатьПользователюВопрос("Укажите как вас зовут").сВариантамиОтвета("Пропустить").сохранитьРезультат("enteredFIO") 	
) +
если("{userEmail} == ").то(
	задатьПользователюВопрос("Укажите адрес вашей электронной почты").сВариантамиОтвета("Пропустить").сохранитьРезультат("enteredEmail") 	
) +
если("{userPhone} == ").то(
	задатьПользователюВопрос("Укажите номер вашего телефона").сВариантамиОтвета("Пропустить").сохранитьРезультат("enteredPhone") 	
) +
если("{userPayload.рост} == ").то(
	задатьПользователюВопрос("Укажите ваш рост").сВариантамиОтвета("Пропустить").сохранитьРезультат("enteredTall") 	
) +
если("{userPayload.регион} == ").то(
	задатьПользователюВопрос("Укажите ваш город").сВариантамиОтвета("Пропустить").сохранитьРезультат("enteredRegion") 	
) +
если("{userPayload.ссылка} == ").то(
	задатьПользователюВопрос("Укажите ссылку на ваш профиль").сВариантамиОтвета("Пропустить").сохранитьРезультат("enteredLink") 	
) +
//
// Замена нажатой кнопки Пропустить на символ -
если("{enteredFIO} == Пропустить").то(
	установитьПеременную("enteredFIO", "-")
) +
если("{enteredEmail} == Пропустить").то(
	установитьПеременную("enteredEmail", "-")
) +
если("{enteredPhone} == Пропустить").то(
	установитьПеременную("enteredPhone", "-")
) +
если("{enteredTall} == Пропустить").то(
	установитьПеременную("enteredTall", "-")
) +
если("{enteredRegion} == Пропустить").то(
	установитьПеременную("enteredRegion", "-")
) +
если("{enteredLink} == Пропустить").то(
	установитьПеременную("enteredLink", "-")
) +
//
// Проверка данных и сохранение тех переменных пользователя для которых в опросе были указаны данные
если("{enteredFIO} != ").то(
	установитьПеременнуюПользователю("userFullName", "{enteredFIO}") +
	установитьПеременную("enteredCheck", "1")
) +
если("{enteredEmail} != ").то(
	установитьПеременнуюПользователю("userEmail", "{enteredEmail}") +
	установитьПеременную("enteredCheck", "1")
) +
если("{enteredPhone} != ").то(
	установитьПеременнуюПользователю("userPhone", "{enteredPhone}") +
	установитьПеременную("enteredCheck", "1")
) +
если("{enteredTall} != ").то(
	установитьПеременнуюПользователю("userPayload.рост", "{enteredTall}") +
	установитьПеременную("enteredCheck", "1")
) +
если("{enteredRegion} != ").то(
	установитьПеременнуюПользователю("userPayload.регион", "{enteredRegion}") +
	установитьПеременную("enteredCheck", "1")
) +
если("{enteredLink} != ").то(
	установитьПеременнуюПользователю("userPayload.ссылка", "{enteredLink}") +
	установитьПеременную("enteredCheck", "1")
) +
//
// Если хоть одно значение было указано сценарий сообщит со сохранении данных
если("{enteredCheck} == 1").то(
	сообщениеПользователю.сШаблоном("Спасибо. Все записал.")
) + 
//
завершить

Опрос пользователя и получение ответа из Confluence

Сценарий уточняет вопрос у пользователя и в зависимости от выбора пользователя возвращает в ответ одну из двух статей из Confluence.

Токен для обращения сервиса в Confluence следует получать у администраторов Confluence или по документации.

 Click here to expand...
//
// Сценарий получения ответа из статьи Confluence
//
// Настройки сценария
//
// Укажите адрес установки Confluence
установитьПеременную("confluense_host", "https://ХХХХХХХХХ.atlassian.net/") +
// URL для запросов получения контента статей
установитьПеременную("confluense_url", "wiki/rest/api/content/") +
// Укажите токен для API запросов в Confluence 
установитьПеременную("confluense_token", "ХХХХХХХХХХХХХХХХХ")+
//
// Основной код сценария
//
// Приветственное сообщение и первый вопрос
сообщениеПользователю.сШаблоном("Добрый день! Сейчас я помогу!") +
задатьПользователюВопрос("Подскажите, описание какого релиза вас интересует?").сВариантамиОтвета("5.0.4", "5.0.5").сохранитьРезультат("answer_1")+
//
если("{answer_1} == 5.0.4").то(
	установитьПеременную("article", "3154706435")
) +
если("{answer_1} == 5.0.5").то(
	установитьПеременную("article", "3160571905") 
) +
//
// 
вызвать.внешнийСервис("{confluense_host}{confluense_url}{article}", "GET").сПараметрами(("expand", "body.storage")).сЗаголовками(("Content-Type", "application/json"),("charset", "utf-8"),("Authorization", "{confluense_token}")).сохранитьРезультат(("body.storage.value","body"))+
//
// Проверка результатов запроса, в случае ошибки сценарий сообщит об ошибке и завершится 
если("{http_code} > 210").то(
	комментарий("Сценарий не смог получить данные. {http_code}")+
	сообщениеПользователю.сШаблоном("Что-то сломалось и я не могу получить для вас ответ.")+
	завершить
)+
//
// 
сообщениеПользователю.сШаблоном("{body}")+
завершить

Стартовые и приветственные сценарии

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

Такие сценарии размещаются в Приветствии или /wiki/spaces/KIT/pages/1606811704.

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

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

 Нажмите здесь, чтобы развернуть пример сценария
//
// Сценарий описывает различные приветствия в зависимости от типа и ИД канала
//
если("{channelType} == Telegram").то(
    сообщениеПользователю.сШаблоном("Добро пожаловать в наш Telegram")
)+
если("{channelId} == 2f7ce72f-8003-4e17-b879-e3c71f239149").то(
    сообщениеПользователю.сШаблоном("Здравствуйте!")
)+
//
// Завершение сценария и передача запроса в базы знаний
завершитьСНовымЗапросом("{platformInMessageQuery}")

Сообщение об отсуствии операторов поддержки вне рабочего времени

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

 Нажмите здесь, чтобы развернуть пример сценария

//
// Сценарий сообщает об отсутствии операторов поддержки в ночное время
//
// Настройка сценария
// Смещение текущего времени службы поддержки относительно GMT в часах
установитьПеременную("HourOffset", "3")+
// время начала работы службы поддержки
установитьПеременную("startHour", "9")+
// время окончания работы службы поддержки (не включая)
установитьПеременную("endHour", "22") +
//
// Основной код сценария
// 
// проверка текущего времени
выполнитьJs("""
var serverTime = new Date();
var serverTimeStamp = serverTime.getTime();
var timeZoneOffset = serverTime.getTimezoneOffset();

var currentTimeStamp = serverTimeStamp + (parseInt(HourOffset) *  3600000) - (timeZoneOffset * 60000);

var currentDate = new Date(currentTimeStamp);
var currentHour = parseInt(currentDate.getHours());

var start = parseInt(startHour);
var stop = parseInt(endHour);

if (currentHour >= start && currentHour < stop) {
	var workTime = 1;
} else {
	var workTime = 0;	
}

var exit = {'workTime':workTime};
exit;
""") +
//
// Сейчас рабочее время, сценарий передает запрос в базы знаний без уведомлений пользователя
если("{workTime} == 1").то(
	комментарий("Рабочее время. Продолжаем штатно") + 
	завершитьСНовымЗапросом("{platformInMessageQuery}")
)+
//
// Сейчас нерабочее время, перед сообщением пользователю сценарий уточняет наличие ответа в базе знаний
запросВБазыЗнаний("{platformInMessageQuery}", "botKnows")+
//
// В базе знаний есть ответ, сценарий уведомляет пользователя об отсутствии операторов и передает запрос в базы знаний
если("{botKnows} == true").то(
	комментарий("Бот знает ответ, продолжаем не взирая на график.") + 
	сообщениеПользователю.сШаблоном("Операторы службы поддержки сейчас не работают, но Вы можете получить консультацию у нашего бота. Операторы смогут подключиться с {startHour}:00 по МСК.")+ 
	завершитьСНовымЗапросом("{platformInMessageQuery}")
)+
//
// В базе знаний нет ответа, сценарий сообщает об отсутствии операторов службы поддержки, завершается и закрывает диалог
комментарий("Бот не понял. Операторы не работают")+
сообщениеПользователю.сШаблоном("Сейчас служба поддержки не работает. Просим писать нам с {startHour}:00 по {endHour}:00 МСК.<br>Unfortunately you contacted us outside of our support service hours. We will reply to you in the morning. Thank you for waiting.")+
завершитьИЗакрытьДиалог()

Меню наиболее частых запросов

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

Если пользователь вместо выбора варианта напишет свой запрос, то сценарий передаст его в базы знаний.

 Нажмите здесь, чтобы развернуть пример сценария
//
// Сценарий проверяет наличие ответа и если ответа нет то предлагает пользователю меню запросов
// 
// Проверка наличия ответа на запрос в базах знаний
запросВБазыЗнаний("{platformInMessageQuery}", "botKnows")+
//
// В базе знаний есть ответ, сценарий передает запрос в базу знаний
если("{botKnows} == true").то(	
	завершитьСНовымЗапросом("{platformInMessageQuery}")
)+
//
// В базе знаний нет прямого ответа на запрос, сценарий предлагает выбрать из готовых вариантов
задатьПользователюВопрос("Здравствуйте 😊<br>Что Вас интересует?").сВариантамиОтвета("Первое","Второе","Компот").сохранитьРезультат("userChoice")+
//
// Если пользователь выбрал первый вариант, то сценарий передает запрос "Первые блюда" в базу знаний
если("{userChoice} == Первое").то(
	завершитьСНовымЗапросом("Первые блюда")
)+
//
// Если пользователь выбрал второй вариант, то сценарий передает запрос "Вторые блюда" в базу знаний
если("{userChoice} == Второе").то(
	завершитьСНовымЗапросом("Вторые блюда")
)+
//
// Если пользователь выбрал третий вариант, то сценарий отправляет ответ и завершается
если("{userChoice} == Компот").то(
	сообщениеПользователю.сШаблоном("Закончился. Всего хорошего!") +
	завершить
)+
//
// Если пользователь не выбирал вариантов а написал другой запрос, то он передается в базы знаний
завершитьСНовымЗапросом("{userChoice}")

Создание тегов к диалогу

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

Переменные диалога могут применяться для сохранения данных относящихся к отдельному диалогу - тегирования, сохранения комментарием или выбору тематик.

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

 Нажмите здесь, чтобы развернуть пример сценария
//
// Сценарий создает переменные диалога 
//
// Настройки сценария
//
// Сценарий описывает варианты для одноуровневого списка приоритетов
установитьПеременную("priorities_list","""{"1": "Критичный", "2": "Срочный", "3": "Средний", "4": "Низкий"}""") +
//
// Сценарий описывает варианты многоуровневневого списка тематик через JS код
выполнитьJs("""
var network = [{"label":"Разрыв соединения","value":"11"}, {"label":"Низкая скорость","value":"12"}];
var equipment = [{"label":"Клиентский роутер","value":"21"}, {"label":"Коммутатор","value":"22"},{"label":"Кабели","value":"23"}];
var other = [{"label":"Кривые руки","value":"31"}, {"label":"Проблемы с учетной записью","value":"32"}, {"label":"Проблемы с документацией","value":"33"}];

var topics = {};
topics[JSON.stringify(network)] = 'Сетевые проблемы';
topics[JSON.stringify(equipment)] = 'Проблемы с оборудованием';
topics[JSON.stringify(other)] = 'Прочие проблемы';

var exit = {'topics': JSON.stringify(topics)};
exit;
""") +
//
// Основной код сценария
//
// Создание переменной выбора приоритета из списка
установитьПеременнуюВДиалог(
    ключ = "1_priority",
    значение = "",
    название = "Выбор срочности",
    редактируемое = true,
    показыватьОператору = true,
    обязательное = true,
    множественныйВыбор = true,
    тип = "Выпадающий список",
    варианты = "{priorities_list}"
)+
//
// Создание переменной выбора сервиса из списка
установитьПеременнуюВДиалог(
    ключ = "2_topicId",
    значение = "",
    название = "Выбор тематики",
    редактируемое = true,
    показыватьОператору = true,
    обязательное = false,
    множественныйВыбор = false,
    тип = "Выпадающий список",
	варианты = "{topics}"
)+
//
// Создание переменной указания адреса
установитьПеременнуюВДиалог(
    ключ = "3_address",
    значение = "",
    название = "Адрес",
    редактируемое = true,
    показыватьОператору = true,
    обязательное = false,
    множественныйВыбор = false,
    тип = "Текст"
)+
//
// Создание переменной указания комментария к заявке
установитьПеременнуюВДиалог(
    ключ = "4_comment",
    значение = "",
    название = "Комментарий к вызову",
    редактируемое = true,
    показыватьОператору = true,
    обязательное = false,
    множественныйВыбор = false,
    тип = "Текст"
)+
завершить

Создание тегов к диалогу и автоматическое предзаполнение полей

Сценарий создает переменную для выбора продукта и автоматически предзаполняет ее на основе классификации запроса пользователя.

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

Если классификация проходит с недостаточной уверенностью, то переменная диалога будет пустой.

Сценарий удобен тем, что список вариантов для выбора хранится в отдельной базе знаний и изменять его может любой супервизор сервиса без вмешательства в текст сценария. База знаний может находиться в режиме “Не используется” и применяться только для хранения спсика продуктов. Автоматическая классификация и предзаполнение переменной позволят значительно экономить время оператора на заполнение переменных диалога

 Нажмите здесь, чтобы развернуть пример сценария
//
// Сценарий создает переменные диалога и автоматически предзаполняет часть из них 
// на основе классификации запроса пользователя 
//
// Настройки сценария
//
// Настройки для проведения классификации запроса пользователя
//
// Адрес API ендпойнтов для запросов баз знаний 
установитьПеременную("kb_qna_enpoint", "https://chat.autofaq.ai/core-api/query/api/v1/query") +
установитьПеременную("kb_crud_endpoint", "https://chat.autofaq.ai/core-api/crud/api/v1/services/") +
//
// Укажите токен для запроса в базу знаний, брать из поля "user token" в разделе Настройки - Общие - Параметры прямого подключения к базам знаний через API
установитьПеременную("autorizationToken", "ХХХХХХХХХ")+
//
// Укажите ID и токен базы знаний -  классификатора продуктов, брать в настройках базы знаний
установитьПеременную("kb_product_id", "ХХХХ")+
установитьПеременную("kb_product_token", "ХХХХХХХХХХХ")+
//
// Укажите минимальный уровень уверенности ответа для успешной классификации
установитьПеременную("minimumScore", "10") +
//
// Основной код сценарий
//
// Сценарий отправляет запрос на классификацию сообщения пользователя
вызвать.внешнийСервис("{kb_qna_enpoint}","POST").сЗаголовками(("Content-Type", "application/json"),("charset", "utf-8"),("AUTOFAQ-User-Token", "{autorizationToken}")).сТеломСообщения("""{"service_token":"{kb_product_token}","service_id":"{kb_product_id}","query":"{platformInMessageQuery}"}""").сохранитьРезультатКакСтроку("answer")+
//
// Проверка резльтатов запрос, если запрос завершился с ошибкой сценарий оставляет комментарий для администратора сервиса 
если("{http_code} != 200").то(  
  комментарий("При запросе на классификацию возникла ошибка {http_code}, текст ошибки {answer}")
)+
//
// Разбор ответа от классификатора, если результаты классификации содержат результат с уверенностью выше установленной, то сценарий вернет название этого документа
выполнитьJs("""
var docName = '';
var docId = '';
var docScore = 0.0;

try {
    var answerParsed = JSON.parse(answer);
} catch(err) {    
    var answerParsed = {};
}

if (answerParsed.hasOwnProperty('results') && answerParsed.results.length > 0)  {
    if (Math.floor(answerParsed.results[0].score * 100) > minimumScore) {
        docName = answerParsed.results[0]['name'];
        docId = answerParsed.results[0]['document_id'].toString();
        docScore = answerParsed.results[0]['score'];
    }
}
var exit = {'docName':docName, 'docId':docId, 'docScore':docScore};
exit;
""")+
//
// Сценарий оставляет комментарий с результатами классификации для администратора сервиса
комментарий("Результаты классификации: определен документ {docName} с уверенностью {docScore}")+
//
// Сценарий получает полный список документов из базы знаний - классификатора для отображения в переменной диалога
вызвать.внешнийСервис("{kb_crud_endpoint}{kb_product_id}","GET").сЗаголовками(("Content-Type", "application/json"),("charset", "utf-8"),("AUTOFAQ-User-Token", "{autorizationToken}")).сПараметрами(("include_documents","1")).сохранитьРезультатКакСтроку("answerDocs")+
//
// Проверка резльтатов запрос, если запрос завершился с ошибкой сценарий оставляет комментарий для администратора сервиса 
если("{http_code} != 200").то(
  // добавление комментария в диалог и завершение сценария
  комментарий("При запросе на получение списка документов возникла ошибка {http_code}, текст ошибки {answerDocs}")
)+
//
// Разбор ответа из базы знаний, сценарий на выходе сохранит список документов в формате "ID документа" - "Название документа" 
выполнитьJs("""
try {
    var answerParsed = JSON.parse(answerDocs);
} catch(err) {    
    var answerParsed = {};
}

var docs = {};

if (answerParsed.hasOwnProperty('documents') && answerParsed.documents.length > 0) {
    for (var i = 0; i < answerParsed.documents.length; i++)  {
        docs[answerParsed.documents[i].document_id] = answerParsed.documents[i].name;    
    } 
}
var exit = {'docs':JSON.stringify(docs)};
exit;
""") +
//
// Сценарий создает переменню диалога для выбора продукта и предзаполняет его
установитьПеременнуюВДиалог(
    ключ = "service",
    значение = "{docId}",
    название = "Выберите продукт",
    редактируемое = true,
    показыватьОператору = true,
    обязательное = true,
    множественныйВыбор = false,
    тип = "Выпадающий список",
    варианты = "{docs}"
)+
завершить

Автоматическое определение группы

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

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

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

 Нажмите здесь, чтобы развернуть пример сценария
//
// Сценарий автоматически определяет группу по тексту запроса пользователя  
// актуально при подключении пользовательского канала одновременно к нескольким группам
// в этом случае АФ не будет запрашивать выбор группы у пользователя 
//
// Настройки сценария
//
// Настройки для проведения классификации запроса пользователя
//
// Адрес API ендпойнтов для запросов баз знаний 
установитьПеременную("kb_qna_enpoint", "https://chat.autofaq.ai/core-api/query/api/v1/query/batch") +
//
// Укажите токен для запроса в базы знаний, брать из поля "user token" в разделе Настройки - Общие - Параметры прямого подключения к базам знаний через API
установитьПеременную("autorizationToken", "ХХХХХХХХХХХХХ")+
//
// Укажите порог минимальной уверенности для автоопределения группы, в процентах
установитьПеременную("minimumConfidence", "50") +
//
// Укажите количество групп
установитьПеременную("groups_amount", "3")+
//
// Укажите ID и токены баз знаний групп - по паре переменных на каждую группу по аналогии
// Данные первой группы 
установитьПеременную("kb_ids_1", "ХХХХ")+
установитьПеременную("kb_tokens_1", "ХХХХХХХХХХХХХХ")+
// Данные второй группы
установитьПеременную("kb_ids_2", "ХХХХ, ХХХХ")+
установитьПеременную("kb_tokens_2", "ХХХХХХХХХХХХХ, ХХХХХХХХХХХХ")+
// Данные третей группы
установитьПеременную("kb_ids_3", "ХХХХ")+
установитьПеременную("kb_tokens_3", "ХХХХХХХХХХХХХХХХХ")+
//
// Сценарий сохраняет текст запроса для отправки в базы знаний
установитьПеременную("queryText", "{platformInMessageQuery}")+
//
выполнитьJs("""
var g_num = Number(groups_amount);
var kb_ids = {};
kb_ids[0] = kb_ids_1.split(',').map(function(item) {return item.trim();});
kb_ids[1] = kb_ids_2.split(',').map(function(item) {return item.trim();});
kb_ids[2] = kb_ids_3.split(',').map(function(item) {return item.trim();});

var kb_tokens = {};
kb_tokens[0] = kb_tokens_1.split(',').map(function(item) {return item.trim();});
kb_tokens[1] = kb_tokens_2.split(',').map(function(item) {return item.trim();});
kb_tokens[2] = kb_tokens_3.split(',').map(function(item) {return item.trim();});

var exit = {};
for (var i = 0; i < g_num; i++) {
    var query_temp = [];
    for (var j = 0; j < kb_ids[i].length; j++) {
        var q = {'service_id':Number(kb_ids[i][j]),'service_token':kb_tokens[i][j],'query':queryText,'top_k':1};
        var z = query_temp.push(q);
    }
    var qq = 'query_text_' + (i+1).toString();
    exit[qq] = JSON.stringify(query_temp);
}
exit;
""")+
//
// Сценарий отправляет запросы на классификацию сообщения пользователя в базы знаний группы 1
вызвать.внешнийСервис("{kb_qna_enpoint}","POST").сЗаголовками(("Content-Type", "application/json"),("charset", "utf-8"),("AUTOFAQ-User-Token", "{autorizationToken}")).сТеломСообщения("{query_text_1}").сохранитьРезультатКакСтроку("answer_1")+
// Проверка резльтатов запрос, если запрос завершился с ошибкой сценарий оставляет комментарий для администратора сервиса 
если("{http_code} != 200").то(  
  комментарий("При запросе на классификацию возникла ошибка {http_code}, текст ошибки {answer_1}")
)+
//
// Сценарий отправляет запросы на классификацию сообщения пользователя в базы знаний группы 2
вызвать.внешнийСервис("{kb_qna_enpoint}","POST").сЗаголовками(("Content-Type", "application/json"),("charset", "utf-8"),("AUTOFAQ-User-Token", "{autorizationToken}")).сТеломСообщения("{query_text_2}").сохранитьРезультатКакСтроку("answer_2")+
// Проверка резльтатов запрос, если запрос завершился с ошибкой сценарий оставляет комментарий для администратора сервиса 
если("{http_code} != 200").то(  
  комментарий("При запросе на классификацию возникла ошибка {http_code}, текст ошибки {answer_2}")
)+
//
// Сценарий отправляет запросы на классификацию сообщения пользователя в базы знаний группы 3
вызвать.внешнийСервис("{kb_qna_enpoint}","POST").сЗаголовками(("Content-Type", "application/json"),("charset", "utf-8"),("AUTOFAQ-User-Token", "{autorizationToken}")).сТеломСообщения("{query_text_3}").сохранитьРезультатКакСтроку("answer_3")+
// Проверка резльтатов запрос, если запрос завершился с ошибкой сценарий оставляет комментарий для администратора сервиса 
если("{http_code} != 200").то(  
  комментарий("При запросе на классификацию возникла ошибка {http_code}, текст ошибки {answer_3}")
)+
//
// Разбор ответов
выполнитьJs("""
var answers = {};
var results = {};
var res = 0.0;
var minimumConfidenceLevel = parseFloat(minimumConfidence) / 100;

try {
    answers[0] = JSON.parse(answer_1);
    answers[1] = JSON.parse(answer_2);
    answers[2] = JSON.parse(answer_3);
} catch(err) {
    answers[0] = [];
    answers[1] = [];
    answers[2] = [];
}
var keys = Object.keys(answers);
for (var i = 0; i < keys.length; i++) {
    res = 0.0;

    for (var j = 0; j < answers[i].length; j++) {
        if (answers[i][j].hasOwnProperty('results') && answers[i][j].results.length > 0) {
            if (answers[i][j].results[0].score > res && answers[i][j].results[0].score > minimumConfidenceLevel) {
                res = answers[i][j].results[0].score;
            }
        }
    }
    results[i] = res;
}
var position = Object.keys(results).reduce(function(a, b){ return results[a] > results[b] ? a : b });
if (results[Object.keys(results)[position]] == 0.0) {position = -1;}
try {
    var group = Number(position) + 1;
} catch(err) {
    var group = 0;
}
var exit = {'group':JSON.stringify(group)};
exit;
""")+
//
// Сценарий определил группу, ответ из баз знаний которой пришел с максимальной уверенностью и переводит запрос на эту группу
// Идентификаторы групп можно получить в разделе настроек Группы
комментарий("Определил группу номер {group}")+
// Сценарий переводит запрос на группу 1
если("{group} == 1").то(
    комментарий("Перевожу чат на группу Вторая линия HR")+
    перевестиНаГруппу("ХХХХХ-ХХХХ-ХХХХ-ХХХХ-ХХХХХ").иЗавершить
)+
// Сценарий переводит запрос на группу 2
если("{group} == 2").то(
    комментарий("Перевожу чат на группу Первая линия")+
    перевестиНаГруппу("ХХХХХ-ХХХХ-ХХХХ-ХХХХ-ХХХХХ").иЗавершить
)+
// Сценарий переводит запрос на группу 3
если("{group} == 3").то(
    комментарий("Перевожу чат на группу Вторая линия IT")+
    перевестиНаГруппу("ХХХХХ-ХХХХ-ХХХХ-ХХХХ-ХХХХХ").иЗавершить
)+
//
// Сценарий не смог определить группу, пользователю будет предложено выбрать группу самому
комментарий("Не смог определить группу")+
завершить

Аутентификация пользователей

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

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

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

 Нажмите здесь, чтобы развернуть пример сценария
//
// Приветственный сценарий с авторизацией пользователя и отправкой проверочного кода на почту
//
// Настройки сценария
// Укажите адреса для API вызовов  
установитьПеременную("get_user_url", "https://itsm.company.ru/api/v1/getUser")+
установитьПеременную("post_url", "http://porter/api/postman/mail")+
//
// Укажите API ключ для запросов на получение данных о пользователях
установитьПеременную("apikey", "ХХХХХХХХХ==")+
//
// Сценарий считывает табельный номер из профиля пользователя
установитьПеременную("user_tabNumber", "{userPayload.tabNumber}") +
//
// Если табельный номер проставлен, то сценарий сразу передает запрос в базы знаний без процедуры аутентификации
// В дальнейшем сбда можно вставить проверку времени действия аутентификации или проверку на увольнение сотрудника
если("{user_tabNumber} != ").то(
  завершитьСНовымЗапросом("{platformInMessageQuery}")
)+
//
// Приветственное сообщение для неавторизованных пользователей
сообщениеПользователю.сШаблоном("Привет, я - бот-помощник. Для работы с ботом приготовьте, пожалуйста, свой табельный номер и проверьте доступ к рабочей электронной почте.") +
//
// Задаем пользователю вопрос с просьбой ввести табельный номер, для того чтобы использовать его в сервисах для получения данных о пользователе.
задатьПользователюВопрос("Введите свой табельный номер.").сохранитьРезультат("entered_tabNumber") +
//
// Запрос в ITSM систему на получение данных о пользователе по табельному номеру
вызвать.внешнийСервис("{get_user_url}","GET").сПараметрами(("tabNumber","{entered_tabNumber}")).сЗаголовками(("Content-Type", "application/json"),("apikey","{apikey}")).сохранитьРезультатКакСтроку("answer") +
//
// Проверка результатов запроса, в случае ошибки сценарий сообщит об ошибке, завершится и закроет диалог
если("{http_code} > 200").то(  
    комментарий("При запросе на поиск пользователя возникла ошибка {http_code}, текст ошибки {answer}")+
    сообщениеПользователю.сШаблоном("При поиске учетной записи возникла ошибка {http_code}. Обратитесь в техническую поддержку.")+ 
    завершитьИЗакрытьДиалог()  
  )+
//
// Разбор ответа от ITSM системы, получение адреса электронной почты пользователя и генерация проверочного кода
// В дальнейшем можно добавить получение и сохранение данных пользователя помимо электронной почты
выполнитьJs("""
var error = 0;
var user_email = '';

try {
    var answerParced = JSON.parse(answer);
} catch(err) {    
    var answerParced = {};
}  

if (answerParced.hasOwnProperty('email')) {
  user_email = answerParced.email;
  var splited = user_email.split('@');
  
  var firstPart = splited[0];
  if (firstPart.length > 4) {
    var tempString = firstPart.slice(2,firstPart.length-2);
    var draftString = '***************************************'.slice(0,tempString.length);
    var firstPartString = firstPart.slice(0,2) + draftString + firstPart.slice(firstPart.length-2,firstPart.length);
  }   

  if (firstPart.length == 4) {var firstPartString = firstPart.slice(0,1) + '**';}
  if (firstPart.length == 3) {var firstPartString = firstPart.slice(0,1) + '*';}
  if (firstPart.length == 2) {var firstPartString = firstPart[0] + '*';}
  if (firstPart.length < 2) {var firstPartString =  '*';}

  var res = firstPartString+'@'+splited[1];

  var pass_code = Math.floor(Math.random() * (9999 - 1000 + 1) + 1000);
} else {
  error = 1;
}
    
var exit = {'error':error, 'user_email':user_email, 'user_email_hidden':res, 'pass_code':pass_code};
exit;
""") +
//
// Если при разборе ответа от ITSM системы возникли ошибки, то сценарий сообщит пользователю, завершится и закроет диалог
если("{error} == 1").то(
  комментарий("При разборе ответа возникла ошибка, текст ответа {answer}")+
  сообщениеПользователю.сШаблоном("При поиске учетной записи возникла ошибка. Обратитесь в техническую поддержку.")+ 
  завершитьИЗакрытьДиалог()
) +
//
// Сценарий отправляет письмо с кодом на адрес полученный из ITSM системы
вызвать.внешнийСервис("{post_url}","POST").сЗаголовками(("Content-Type", "application/json")).сТеломСообщения("""{"to": "{user_email}","from":"no-reply@autofaq.ai", "subject":"Подключение к боту","body":"Кто-то пытается подключиться к боту используя ваш табельный номер. Если это сделали не Вы, то обратитесь пожалуйста в службу технической поддержки. Если это вы, то введите этот секретный код {pass_code}. <br>Внимание! Никому не сообщайте этот код!"}""").сохранитьРезультатКакСтроку("answer_email") +
//
// Проверка результатов запроса, в случае ошибки сценарий сообщит об ошибке, завершится и закроет диалог
если("{http_code} > 200").то(  
    комментарий("При отправке письма с проверочным кодом возникла ошибка {http_code}, текст ошибки {answer_email}")+
    сообщениеПользователю.сШаблоном("При отправке письма с проверочным кодом возникла ошибка {http_code}. Пожалуйста, попробуйте подключиться к боту позднее.")+ 
    завершитьИЗакрытьДиалог()  
)+
//
// Сценарий выводит отправленный пользователю пароль и адрес почты в комментарий диалога для администратора сервиса
комментарий("Пользователю на адрес {user_email} был отправлен код {pass_code}")+
//
// Метка для повторного ввода кода
установитьМетку("point_password_enter") +
//
// Сценарий просит пользователя ввести секретный код
задатьПользователюВопрос("На вашу электронную почту {user_email_hidden} был отправлен код. Пожалуйста введите его").сохранитьРезультат("entered_pass_code") +
//
// ПЕрвая проверка корректности введенного кода, если код введен неверно то сценарий переход на метку для повторного ввода, но не более 3 раз
если("{entered_pass_code} != {pass_code}").то(
  сообщениеПользователю.сШаблоном("Вы ввели неверный код. Попробуйте ввести еще раз. Максимальное число попыток - 3.")+  
  перейтиНаМетку("point_password_enter").неБольше(3)
) +
//
// Финальная проверка корректности кода, если на этом этапе код указан неверно, то сценарий сообщит об ошибке, завершится и закроет диалог
если("{entered_pass_code} != {pass_code}").то(
  сообщениеПользователю.сШаблоном("Вы три раза ввели неверный код. Обратитесь в техническую поддержку.")+  
  завершитьИЗакрытьДиалог() 
) +
//
// В случае успешного ввода кода сценарий сохраняет адрес электронной почты и табельный номер в профиле пользователя
установитьПеременнуюПользователю("userPayload.tabNumber", "{entered_tabNumber}") +
установитьПеременнуюПользователю("userEmail", "{user_email}") +
сообщениеПользователю.сШаблоном("Вы успешно подключились к боту.") +
// 
// Проверка наличия ответа на запрос пользователя в базах знаний
запросВБазыЗнаний("{platformInMessageQuery}", "botKnows")+
//
// В базах знаний есть прямой ответ, сценарий передает запрос в базы знаний
если("{botKnows} == true").то(  
  завершитьСНовымЗапросом("{platformInMessageQuery}")
)+
//
// В базах знаний нет прямого ответа на запрос пользователя, сценарий предоставляет выбор основных вариантов действий - пользователь может выбрать один из вариантов или написать запрос еще раз
задатьПользователюВопрос("Не совсем поняла, чем помочь. Вот основные варианты действий - выбери вариант или напиши свой запрос иначе.").сВариантамиОтвета("Создать заявку","Посмотреть список заявок").сохранитьРезультат("userChoice") +
//
// Сценарий передает выбор пользователя в базы знаний и завершается
завершитьСНовымЗапросом("{userChoice}")

Генерируем ответ для общих вопрос из GPT

Сценарий интеграции на поступление диалога в систему проверяет последовательно каждый вопрос пользователя, пока уровень уверенности предоставленного ответа из БЗ болталка будет равен или выше заданного уровня.

Для подключения сценария необходимо наличие токена для авторизации в GPT

 Click here to expand...
// Адрес API ендпойнтов для отправки вопроса пользователя в БЗ болталка 
установитьПеременную("kb_qna_enpoint", "https://<url_сервиса>/core-api/query/api/v1/query")+
// Укажите токен авторизации для отправки запроса в базы знаний 
установитьПеременную("autorizationToken", "<ID_токен_сервиса>")+
// Укажите порог уверенности, при котором обращаемся в GPT
установитьПеременную("mConfidence", "0.9") +
// Данные БЗ болталка - ИД БЗ болталка и токен БЗ болталка
установитьПеременную("kb_ids", "<ID_БЗ_болталка>")+
установитьПеременную("kb_tokens", "<токен_БЗ_болталка>")+
// Сохраняем текст запроса для отправки в базы знаний
установитьПеременную("queryText", "{platformInMessageQuery}")+
//Адрес API ендпойнтов GPT и токен авторизации 
установитьПеременную("gpt","https://api.openai.com/v1/chat/completions")+
установитьПеременную("autorizationTokengpt","<токен_gpt>")+
//отправляем вопрос пользователя в БЗ болталка
установитьМетку("запросвболталку")+
выполнитьJs("""
var tag = {"service_id":kb_ids,"service_token":kb_tokens,"query":queryText,"top_k":"1"};
var exit = {'tag':JSON.stringify(tag)};
exit;
""")+
вызвать.внешнийСервис("{kb_qna_enpoint}","POST").сЗаголовками(("Content-Type", "application/json"),("charset", "utf-8"),("AUTOFAQ-User-Token", "{autorizationToken}")).сТеломСообщения("{tag}").сохранитьРезультатКакСтроку("uanswer")+
// Проверка резльтатов запрос, если запрос завершился с ошибкой сценарий оставляет комментарий для администратора сервиса 
если("{http_code} != 200").то(  
 комментарий("При запросе на классификацию возникла ошибка {http_code}, текст ошибки {uanswer}")
)+
//
комментарий("{uanswer}")+
//получаем уровень уверенности ответа топ 1 и сравниваем c заданным нами порогом
выполнитьJs("""
var a;
var result = JSON.parse(uanswer);
var score = result.results[0].score;
if (score >= mConfidence){
  a = 1;
}
else {
  a = 0;
}
var exit = {'score':result.results[0].score, 'a':a};
exit;
""")+
//пропускаем обращение в GPT
если("{a} == 0").то(
  завершитьСНовымЗапросом("{queryText}")
)+
//обращаемся в GPT и проверяем следующий вопрос пользователя на наличие ответа в болталке
если("{a} == 1").то(
выполнитьJs("""
var taggpt = {"model":"gpt-3.5-turbo","temperature":0.7,"messages":[{"role":"system","content":"Я умный чатбот, который является почетным представителем компании Autofaq.ai. На вопрос я дам ответ. На оскорбительный вопрос или оскорбления я отвечу вежливо и учтиво. Email для связи с компанией Autofaq.ai - info@autofaq.ai."},{"role": "user","content":queryText}]};
var exit = {'taggpt':JSON.stringify(taggpt)};
exit;
""")+
вызвать.внешнийСервис("{gpt}","POST").сЗаголовками(("Content-Type", "application/json"),("Authorization","Bearer {autorizationTokengpt}")).сТеломСообщения("{taggpt}").сохранитьРезультатКакСтроку("answer")+
выполнитьJs("""
var result = JSON.parse(answer);
var exit = {'gptanswer':result.choices[0].message.content};
exit;
""")+
сообщениеПользователю.сШаблоном("{gptanswer}")+
задатьПользователюВопрос("Подскажите, чем я могу вам помочь?").сохранитьРезультат("queryText")+
перейтиНаМетку("запросвболталку")
)

Работа с API вызовами

Создание инцидента в ITSM системе через REST API

Классификация запроса и создание инцидента в ITSM системе через REST API

Регистрация запроса в CRM системе через REST API

Получение списка открытых инцидентов

Получение баланса по карте лояльности через SOAP вызов

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

Сценарий проверяет номер телефона в профиле пользователя. Если номер телефона не указан, то сценарий запросит номер у пользователя и после проверки сохранит его в профиле. Сценарий верифицирует введенный номер телефона отправив сообщение с проверочным кодом через сторонний сервис отправки СМС.

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

Все текстовки сообщений сценария настраиваются в начале сценария

 Нажмите здесь, чтобы развернуть пример сценария
// Сценарий получения баланса клубной карты пользователя по номеру его телефона
// - сценарий считывает номер телефона из профиля пользователя, если номер в профиле не указан то сценарий запросит его у пользователя
// - сценарий отправит проверочный код через сервис отправки СМС и запрос ввод кода
// - сценарий получит в сервисе лояльности по номеру телефона список карт пользователя, для каждой карты запросит ее баланс и выведет суммарный баланс пользователю  
//
// Настройки сценария
// 
// Тексты сообщений сценария в сторону пользователя
//
// Текст просьбы указать номер телефона
установитьПеременную("messageAskPhoneNumber","Для уточнения информации по клубной карте напишите, пожалуйста, номер телефона, к которому она привязана.") +
// Текст сообщения в случае отсутствия привязанных к номеру телефона карт
установитьПеременную("messageUnknownPhoneNumber","Вы ввели некорректный номер телефона.") +
// Текст сообщения в случае отсутствия привязанных к номеру телефона карт
установитьПеременную("finalMessageUnknownPhoneNumber","Вы дважды ввели некорректный номер телефона. Я не могу вынести такое, поэтому завершаю свою работу.") +
// Текст сообщения об ошибке при отправке СМС
установитьПеременную("messageErrorSMS","К сожалению, мы не смогли отправить СМС-сообщение на указанный вами номер, попробуйте, пожалуйста, позднее.") +
// Текст сообщения c информацией об отправленном СМС
установитьПеременную("messageSMS","На ваш номер телефона отправлен СМС с проверочным кодом.") +
// Текст сообщения c просьбой ввести секретный код
установитьПеременную("messageAskCode","Пожалуйста, напишите проверочный код из СМС.") +
// Текст сообщения об ошибке в присланном коде
установитьПеременную("messageCodeError","Вы ввели неверный код.") +
// Текст финального сообщения об ошибке в присланном коде
установитьПеременную("messageFinalCodeError","Неверный проверочный код.") +
// Текст сообщения в случае отсутствия привязанных к номеру телефона карт
установитьПеременную("messageNoCardsForUser","К этому номеру телефона не привязана клубная карта.") +
// Текст сообщения в случае ошибки в запросе на получение списка привязанных к номеру телефона карт
установитьПеременную("messageUnknownErrorGetCards","При получении списка карт произошла неизвестная ошибка. Попробуйте позже") +
// Текст сообщения в случае ошибки в запросе на получение баланса по одной из карт
установитьПеременную("messageUnknownErrorGetBalance","При получении списка карт произошла неизвестная ошибка. Попробуйте позже") +
// Текст сообщения в случае непридвиденной ошибки 
установитьПеременную("messageUnknownError","Произошла непредвиденная ошибка. Попробуйте запросить позже.") +
// Текст первой части финального сообщения с размером баланса по всем картам, между частями будет вставлен размер баланса 
установитьПеременную("finalMessage_1","На вашей клубной карте <b>") +
// Текст второй части финального сообщения с размером баланса по всем картам
установитьПеременную("finalMessage_2","</b> баллов.") +
// Текст финального сообщения В случае нулевого баланса по карте 
установитьПеременную("finalMessage_zeroBalance","На вашей клубной карте нет баллов. Грустно.") +
//
// Адрес сервиса по отправке СМС
установитьПеременную("sendSMSUrl","https://XXXXXX.ru/send")+
// Адрес сервиса программы лояльности для получения баланса по клубной карте
установитьПеременную("cardRequestUrl","https://XXXXXX.ru/processing")+ //
// Логин и пароль для отправки запросов в сервис программы лояльности в формате base64 
установитьПеременную("query_token","XXXXXXXXXXXXXXXXX")+
//
// Основная часть сценария
// 
// Служебная переменная для хранения суммарного баланса по всем картам
установитьПеременную("finalBalance","0")+
//
// Сценарий считывает номер телефона из профиля пользователя
установитьПеременную("phoneNumber", "{userPhone}") +
//
// Если в профиле пользователя не был указан номер телефона то сценарий запросит его у пользователя 
если("{phoneNumber} == ").то(
	задатьПользователюВопрос("{messageAskPhoneNumber}").сохранитьРезультат("phoneNumber")
)+
// Метка для повторного запроса номера телефона
установитьМетку("ask_phone_number")+
// Сценарий проверяет введеный текст на соотвествие формату номеру телефона, на выходе очищенный от лишних символов номер из 10 цифр без +7 
выполнитьJs("""
var num = phoneNumber.replace(/[^+\d]/g, '');
if (num.length < 10) {
	var numError = 1;
	var number = '';
} else {
	var numError = 0;
	var number = num.substring(num.length-10,num.length);
}
var exit = {'number': number, 'numError':numError};
exit;
""") +
//
// В случае ошибочного формата номера телефона сценарий вернет пользователю просьбу ввести номер повторно и вернется на шаг ввода номера, но не более 2-х раз
если("{numError} != 0").то(
	сообщениеПользователю.сШаблоном("{messageUnknownPhoneNumber}")+
	перейтиНаМетку("ask_phone_number").неБольше(1)
)+ 
//
// Если после двух попыток пользователь все равно ввел неверный номер телефона, то сценарий вернет соответствущее сообщение и завершит работу
если("{numError} != 0").то(
	сообщениеПользователю.сШаблоном("{finalMessageUnknownPhoneNumber}")+
	завершить
)+ 
//
// Сценарий генерирует секретный код для верификации номера телефона через смс 
выполнитьJs("""
var smscode = String(Math.floor(Math.random() * 898) + 101).substring(0,3);
var exit = {'smscode':smscode};
exit;
""") +
//
// Сценарий записывает проверочный комментарий с информацией о отправленном коде
комментарий("Отправлен код {smscode} на номер {number}")+
//
// Сценарий отправляет запрос на отправку СМС с секретным кодом 
вызвать.внешнийСервис("{sendSMSUrl}", "POST").сЗаголовками(("Content-Type", "application/json"), ("charset", "utf-8")).сТеломСообщения("""{"phone":"{number}","auth":"{query_token}","text":"Проверочный код {smscode}"}""").сохранитьРезультат(("Status", "status"),("Error","error")) +
//
// Если запрос вернул ошибку сценарий записывает отладочный комментарий, возвращает пользователю сообщение об ошибке и завершает работу
если("{http_code} > 210").то(
  комментарий("При отправке запроса на отправку СМС возникла ошибка: {http_code}")+
  сообщениеПользователю.сШаблоном("{messageErrorSMS}") +
  завершить
)+
//
// Если в результате попытки отправить СМС сценарий получает ошибку, то сценарий возвращает пользователю соответствующее сообщение и завершается
если("{status} == ERROR").то(
	комментарий("При отправке запроса на отправку СМС произошла ошибка, статус - {http_body}")+
	сообщениеПользователю.сШаблоном("{messageErrorSMS}") +
	завершить
)+
//
// Сценарий сообщает об отправленном СМС с кодом
сообщениеПользователю.сШаблоном("{messageSMS}")+
//
// Метка для повторного запроса секретного кода 
установитьМетку("getCode")+
//
// В случае успешной отправки СМС, сценарий просит пользователя ввести код 
задатьПользователюВопрос("{messageAskCode}").сохранитьРезультат("smsCodeEntered")+
// Сценарий сохраняет отладочный комментарий с текстом введенного кода
комментарий("Введен проверочный код {smsCodeEntered}")+
//
// Сценарий очищает введенный код от возможных служебных символов
выполнитьJs("""
var code = smsCodeEntered.replace(/[^+\d]/g, '');
var exit = {'smsCodeEntered': code};
exit;
""") +
//
// В случае если введенный и отправленный код не совпадают то сценарий возвращает соотвествующее сообщение и переходит обратно на ввод кода, но не более двух раз
если("{smsCodeEntered} != {smscode}").то(
	сообщениеПользователю.сШаблоном("{messageCodeError}")+
	перейтиНаМетку("getCode").неБольше(1)
) +
//
// В случае если пользователь ввел неверный код 2 раза то сценарий сообщает об ошибке и завершает работу
если("{smsCodeEntered} != {smscode}").то(
	сообщениеПользователю.сШаблоном("{messageFinalCodeError}")+
	завершить
)+
//
// Сценарий сохраняет введенный номер телефона в профиле пользователя чтобы не спрашивать в следующий раз
комментарий("Номер телефона +7{number} сохранен в профиле пользователя")+
установитьПеременнуюПользователю("userPhone", "+7{number}") +
//
// Сценарий фомирует запрос на получение баланса списка карт по номеру телефона
выполнитьJs("""
var queryTemplate = <<EOF
<?xml version="1.0" encoding="UTF-8"?>                    
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
<soapenv:Header/>
<soapenv:Body>
<ProcessRequest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<request>
<CardRequest>
<RequestID>XXXXXXXX</RequestID>
<DateTime>2022-10-12T07:07:06.21</DateTime>
<Organization>XXXXXXXX</Organization>
<POS>XXXXXXXX</POS>
<Phone>+7${number}</Phone>
</CardRequest>
</request>
<orgName>XXXXXXXX</orgName>
</ProcessRequest>
</soapenv:Body>
</soapenv:Envelope>
EOF
 
var exit = {'queryEncoded': queryTemplate.replace(/(\r\n|\n|\r)/gm,'')};
exit;
""")+
//
// Сценарий отправляет запрос на получение списка карт по номеру телефона в сервис лояльности
вызвать.внешнийСервис("{cardRequestUrl}", "POST").сЗаголовками(("Content-Type", "text/xml;charset=UTF-8"), ("Authorization", "Basic {query_token}")).сТеломСообщения("{queryEncoded}").сохранитьРезультатКакСтроку("answerCardRequest")+
//
// Если запрос вернул ошибку сценарий записывает отладочный комментарий, возвращает пользователю сообщение об ошибке и завершает работу
если("{http_code} > 210").то(
  комментарий("При отправке запроса CardRequest возникла ошибка: {http_code}, {answerCardRequest}")+
  сообщениеПользователю.сШаблоном("{messageUnknownErrorGetCards}")+
  завершить
)+
//
// Сценарий разбирает ответа от сервиса лояльности
выполнитьJs("""
var parser = new marknote.Parser();
var error = 1;
var ReturnCode = '';
var messageFrom = '';
var message = messageUnknownErrorGetCards;
var cardList = [];

try {
	var doc = parser.parse(answerCardRequest);
	var body = doc.getRootElement();
	var CardResponse = body.getChildElement("ProcessRequestResponse").getChildElement("ProcessRequestResult").getChildElement("CardResponse");
	var cards = CardResponse.getChildElements("Card");
	ReturnCode = CardResponse.getChildElement("ReturnCode").getText();
	messageFrom = CardResponse.getChildElement("Message").getText();
} catch(e) {
  ReturnCode = '-1';
}

if (ReturnCode == '1') {
	message = messageNoCardsForUser;
	error = 1;
}

if (ReturnCode == '0') {
	for (var i in cards){ 
		var CardNumber = cards[i].getChildElement("CardNumber").getText(); 
  		var CardTypeID = cards[i].getChildElement("CardTypeID").getText();
  		if (CardTypeID == '1' || CardTypeID == '4') {
  			cardList.push(CardNumber);
  		}  		
	}

	if (cardList.length > 0) {
		message = 'OK';
		error = 0;
	} else {
		message = messageNoCardsForUser;
		error = 1;
	}
}
  		
var exit = {'error': error, 'message': message, 'ReturnCode': ReturnCode, 'cardList': JSON.stringify(cardList), 'messageFrom':messageFrom};
exit;
""")+
//
// Сценарий сохраняет отладочные комментарии с ReturnCode и списком карточек пользователя (отбираются карты только тип 1 и тип 4)
комментарий("Код ответа от сервиса лояльности {ReturnCode}")+
комментарий("Получен список карт пользователя {cardList}")+
//
// Если запрос прошел с ошибкой сценарий сообщит об этом пользователю и завершится. В комментарии будет отображет текст ошибки сервиса лояльности
если("{error} == 1").то(
	сообщениеПользователю.сШаблоном("{message}")+
	комментарий("Запрос неуспешен. Текст ошибки из сервиса лояльности: {messageFrom}")+
	завершить
) +
// 
// Метка для запроса баланса по следующей карте из списка карт 
установитьМетку("balance_request")+
//
// Сценарий выбирает первую карту из списка для получения баланса карты
выполнитьJs("""
try {	
	var cardListParced = JSON.parse(cardList);
} catch(e) {
  var cardListParced = [];
}

var cardId = '';
if (cardListParced.length > 0) {
	cardId = cardListParced.shift();
} 
var cardListLength = cardListParced.length;

var exit = {'cardId': cardId, 'cardList': JSON.stringify(cardListParced), 'cardListLength':cardListLength};
exit;
""")+
//
// Сценарий оставляет отладочный комментарий с номуром выбранной карты
комментарий("Получаем данные о балансе карты {cardId}")+
//
// Аварийная проверка - если номер карта не определен, то сценарий возвращает пользователю сообщение об ошибке и завершается
если("{cardId} == ").то(
	сообщениеПользователю.сШаблоном("{messageUnknownError}")+
	завершить
)+
//
// Сценарий формирует запрос на получение баланса карты
выполнитьJs("""
var queryTemplate = <<EOF
<?xml version="1.0" encoding="UTF-8"?>                    
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
<soapenv:Header/>
<soapenv:Body>
<ProcessRequest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<request>
<BalanceRequest>
<RequestID>XXXXXXXX</RequestID>
<DateTime>2022-07-16T08:01:06.21</DateTime>
<Organization>XXXXXXXX</Organization>
<POS>XXXXXXXX</POS>
<Card>
<CardNumber>${cardId}</CardNumber>
</Card>
</BalanceRequest>
</request>
<orgName>XXXXXXXX</orgName>
</ProcessRequest>
</soapenv:Body>
</soapenv:Envelope>
EOF
 
var exit = {'queryEncoded': queryTemplate.replace(/(\r\n|\n|\r)/gm,'')};
exit;
""")+
//
// Сценарий отправляет запрос на получение баланса карты
вызвать.внешнийСервис("{cardRequestUrl}", "POST").сЗаголовками(("Content-Type", "text/xml;charset=UTF-8"), ("Authorization", "Basic {query_token}")).сТеломСообщения("{queryEncoded}").сохранитьРезультатКакСтроку("answerBalanceRequest")+
//
// В случае ошибки при выполнении запроса сценарий записывает отладочный комментарий, сообщает пользователю об ошибке и завершается 
если("{http_code} > 210").то(
  комментарий("При отправке запроса BalanceRequest возникла ошибка: {http_code}, {answerBalanceRequest}")+
  сообщениеПользователю.сШаблоном("{messageUnknownErrorGetCards}")+
  завершить
)+
//  
// Сценарий разбирает ответ системы лояльности c данными баланса по карте, полученный баланс суммируется с общим балансом 
выполнитьJs("""
var parser = new marknote.Parser();
var error = 1;
var ReturnCode = '';
var messageFrom = '';
var balance = 0;
var message = messageUnknownErrorGetBalance;

var _finalBalance = parseInt(finalBalance);
var doc = parser.parse(answerBalanceRequest);
var body = doc.getRootElement();
var BalanceResponse = body.getChildElement("ProcessRequestResponse").getChildElement("ProcessRequestResult").getChildElement("BalanceResponse");

try {
  messageFrom = BalanceResponse.getChildElement("Message").getText();
  ReturnCode = BalanceResponse.getChildElement("ReturnCode").getText();
} catch(e) {
  ReturnCode = '-2';
}

if (ReturnCode != '0') {
	error = 1;
}

if (ReturnCode == '0') {	
	try {
		var CardBalance = BalanceResponse.getChildElement("CardBalance").getText();

		balance = parseInt(CardBalance);
		_finalBalance = _finalBalance + balance;
	} catch(e) {
  		balance = 0;
	}	
}
  		
var exit = {'error': error, 'message': message, 'ReturnCode': ReturnCode, 'balance': balance, 'finalBalance': _finalBalance};
exit;
""")+
//
// Сценарий оставляет отладочный комментарий о полученном статусе ответа и балансе по каждой карте
комментарий("Для карты {cardId} получен код ответа от системы лояльности {ReturnCode} с балансом {balance}, суммарный баланс {finalBalance}")+
//
// Если в списке карт еще остались карты то сценарий переходит назад и запрашивает баланс у системы лояльности до тех пор пока список карт не опустеет 
если("{cardListLength} != 0").то(
	перейтиНаМетку("balance_request").неБольше(5)
)+ 
//
// В случае если баланс по картам нулевой, то сценарий отдает пользователю соотвествующее сообщение и завершает работу 
если("{finalBalance} == 0").то(
	сообщениеПользователю.сШаблоном("{finalMessage_zeroBalance}")+
	завершить
) +
//
// В случае если баланс не нулевой, то сценарий формирует финальное сообщение пользователю с указанием его суммарного баланса и завершает работу
сообщениеПользователю.сШаблоном("{finalMessage_1}{finalBalance}{finalMessage_2}")+
завершить

Получение данных через SQL запрос

Сценарии вызываемые операторами

Добавление и редактирование адреса Email в профиле пользователя

Сценарий позволяет оператору добавить или отредактировать адрес электронной почты в профиле пользователя. Если адрес электронной почты уже указан, то он будет заполнен в поле редактирования.

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

 Click here to expand...
// 
// Сценарий добавления и редактирования комментария в профиле пользователя 
// Сценарий создает переменную пользователя и вносит все правки туда, все изменения будут доступны во всех переписках этого пользователя
// Сценарий предназначен для подключения в Интеграцию на событие "Нажата кнопка оператора"
//
// Сценарий считывает данные из переменной пользователя, если переменная не существует то будет получена пустая строка 
установитьПеременную("_email", "{userEmail}")+
// 
// Сценарий собирает форму для оператора
добавитьПолеВФормуОператора(
  форма = "Обновление данных пользователя",
  переменная = "formName",
  название = "",
  значение = "Редактирование EMAIL пользователя",
  тип = "Нередактируемый текст",
  описание = "",
  обязательное = false
) +
// Добавляем поля пользователя
добавитьПолеВФормуОператора(
  форма = "Обновление данных пользователя",
  переменная = "_email",
  значение = "{_email}",
  название = "Email",
  тип = "Однострочный редактируемый текст",
  описание = "",
  обязательное = true
) +
//  
// Сценарий отображает форму оператора
показатьФормуОператору(имя = "Обновление данных пользователя") +
//
// Сценарий обрабатывает сохранение данных пользователя в случае когда оператор нажимает Продолжить на форме
установитьПеременнуюПользователю("userEmail", "{_email}") +
комментарий("Обовлены данные пользователя") +
//
// Сценарий завершает свою работу
завершить

Добавление и редактирование комментария к профилю пользователя

Сценарий позволяет оператору добавить комментарий к профилю пользователя. Если коментарий уже создан и заполнен, то его текст будет показан оператору.

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

 Нажмите здесь, чтобы развернуть пример сценария
// 
// Сценарий добавления и редактирования комментария в профиле пользователя 
// Сценарий создает переменную пользователя и вносит все правки туда, все изменения будут доступны во всех переписках этого пользователя
// Сценарий предназначен для подключения в Интеграцию на событие "Нажата кнопка оператора"
//
// Сценарий считывает данные из переменной пользователя, если переменная не существует то будет получена пустая строка 
установитьПеременную("_comment", "{userPayload.comment}")+
// 
// Сценарий собирает форму для оператора
добавитьПолеВФормуОператора(
  форма = "Обновление данных пользователя",
  переменная = "formName",
  название = "",
  значение = "Редактирование комментария о пользователе",
  тип = "Нередактируемый текст",
  описание = "",
  обязательное = false
) +
// Добавляем поля пользователя
добавитьПолеВФормуОператора(
  форма = "Обновление данных пользователя",
  переменная = "_comment",
  значение = "{_comment}",
  название = "Комментарий",
  тип = "Многострочный редактируемый текст",
  описание = "Комментарий",
  обязательное = true
) +
//  
// Сценарий отображает форму оператора
показатьФормуОператору(имя = "Обновление данных пользователя") +
//
// Сценарий обрабатывает сохранение данных пользователя в случае когда оператор нажимает Продолжить на форме
установитьПеременнуюПользователю("userPayload.comment", "{_comment}") +
комментарий("Обовлены данные пользователя") +
//
// Сценарий завершает свою работу
завершить

Отправка текста переписки на электронную почту

Сценарий позволяет оператору отправить текст переписки и все файлы из переписки на электронную почту.

Сценарий может быть использован как для отправки переписки самому пользователю так и для отправки текста переписки в произвольные внешние службы. Сценарий поддерживает форматирование письма HTML или plain-text.

 Нажмите здесь, чтобы развернуть пример сценария

// Сценарий отправляет на указанный адрес текст переписки с пользователем.
// Дополнительно к пистму прикладываются все файлы в переписке меньше установленного размера.
// Сценарий может быть использован как для отправки переписки самому пользователю так и для отправки письма в сторонний сервис. 
//
// Настройки сценария
// 
// Формат текста письма. Установите true для HTML и false для обычного текста.
установитьПеременную("formated_text", "true") +
// Максимальный размер файлов для отправки в качестве вложения. Сценарий сообщит о пропущенных файлах и оператору и получателю письма.
установитьПеременную("maximumFileSize", "10000") +
// Предустановка адреса email пользователя в качестве получателя для быстрой отправки переписки пользователю на его почту.
установитьПеременную("useUserEmail", "true") +
// Адрес отправителя 
установитьПеременную("from_email", "noreply@autofaq.ai") +
// Предустановленная тема письма. Оператор сможет сменить тему перед отправкой письма.
установитьПеременную("subject", "Переписка с пользователем {userFullName}")+
//
// Адрес сервиса по отправке писем, встроенного в AutoFAQ
установитьПеременную("host", "http://porter/api/postman/mail") +
//
// Основной код сценария
//
// Сценарий предустанавливает адрес получателя из данных пользователя или оставляет поле пустым
если("{useUserEmail} == true").то(
  установитьПеременную("_email", "{userEmail}") 
)+
// Сценарий собирает форму для сбора данных у пользователя - адрес и комментарий к письму
добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "formName",
  название = "",
  значение = "Сценарий отправит электронное письмо с текстом переписки на указанный адрес. Все файлы в переписке будут приложены к письму.",
  тип = "Нередактируемый текст",
  описание = "",
  обязательное = false
) +
добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "email",
  значение = "{_email}",
  название = "Укажите адрес email",
  тип = "Однострочный редактируемый текст",
  обязательное = true
) +
добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "subject",
  значение = "{subject}",
  название = "Укажите тему писма",
  тип = "Однострочный редактируемый текст",
  обязательное = true
) +
добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "comment",
  значение = "",
  название = "Комментарий к письму",
  тип = "Многострочный редактируемый текст",
  обязательное = false
) +
//  
// Сценарий отображает форму для опроса оператора
показатьФормуОператору(имя = "Отправка текста переписки на электронную почт") +
//
// Сценарий проверяет файлы в переписке и формирует ссылки для отправки файлов
выполнитьJs("""
try {
  var parsedExtFiles = JSON.parse(externalFiles);
} catch(e) {
  var parsedExtFiles = [];
}

var numOfFiles = parsedExtFiles.length;
var filesMessage = '';
var filesUrls = [];
var sentFilesNum = 0;

if (numOfFiles > 0) {
  for (var f = 0; f < numOfFiles; f++) {
    if (parseInt(JSON.parse(parsedExtFiles[f])["size"]) < parseInt(maximumFileSize)) {      
      filesUrls.push( 'http://bot-platform-back:8090/api/files/' + JSON.parse(parsedExtFiles[f])['id'] );
      sentFilesNum += 1;
    } else {
      filesMessage = filesMessage + (JSON.parse(parsedExtFiles[f])['name']) + ', ';      
    }
  }
}

if (filesMessage != '') {
  filesMessage = 'При вложении были пропущены файлы ' + filesMessage.slice(0,filesMessage.length-2) + ' из-за ограничения по размеру вложений ' + maximumFileSize + ' байт';
} 
if (sentFilesNum > 0) {
    filesMessage = 'К письму были приложены файлы - ' + sentFilesNum.toString() + ' шт. ' + filesMessage;
} else {
  filesMessage = 'К письму нет вложенных файлов. ' + filesMessage;
}

var exit = {'filesMessage': filesMessage, 'filesUrls': filesUrls};
exit;
""") +
//
// Сценарий сообщает оператору о вложенных и пропущенных файлах
комментарий("{filesMessage}") +
//
// Сценарий получает историю сообщений и формирует запрос на отправку письма
выполнитьJs("""
var rawText = '';

var msgTypes = ['Question', 'AnswerOperator', 'AnswerChatterbox','AnswerOperatorWithBot','AnswerOperator', 'AnswerBot']; 
var labelMap = {
    'Question': 'Пользователь:',
    'AnswerOperator': 'Оператор:',
    'AnswerBot': 'Бот:',
    'AnswerChatterbox': 'Бот:',
    'AnswerOperatorWithBot': 'Оператор:'
  }

if (conversation.channelUser.fullName) {
  if (conversation.channelUser.email) {
    var line_1 = '<div>Переписка с пользователем <b>' + conversation.channelUser.fullName + '</b> (' + conversation.channelUser.email + ')</div>';
  } else {
    var line_1 = '<div>Переписка с пользователем <b>' + conversation.channelUser.fullName + '</b> (адрес EMAIL не зарегистрирован)</div>';
  }
} else {
  if (conversation.channelUser.email) {
    var line_1 = '<div>Переписка с пользователем ' + conversation.channelUser.email + ' (ФИО не указано)</div>';
  } else {
    var line_1 = '<div>Переписка с неизвестным пользователем</div>'
  }
}

if (conversation.messages.length > 0) {
  var first_event = conversation.messages[0];
  var line_2 = '<div>Дата начала диалога ' + first_event.ts.slice(8,10) + '.' + first_event.ts.slice(5,7) + '.' + first_event.ts.slice(0,4);
  line_2 = line_2 + ' ' + first_event.ts.slice(11,13) + ':' + first_event.ts.slice(14,16) + '</div><br>';
} else {
  var line_2 = '<div>Дата начала диалога не указана</div><br>';
}

if (comment == '' || comment == 'NaN') {
  var line_3 = '<div>Оператор не оставил комментарий к переписке.</div>';  
} else {
  var line_3 = '<div><b>Комментарий оператора к переписке:</b> ' + comment + '</div>';
}
line_3 = line_3 + '<div>' + filesMessage + '</div><br>';

var messages = conversation.messages.filter(function (str) {return  msgTypes.indexOf(str.tpe) > -1;});
for (var i = 0; i < messages.length; i++) {
  var message = messages[i];
  var label = labelMap[message.tpe] || '';
  if (label) {
    if (formated_text != 'true') {
      clean_txt = message.txt.replace(/<a[^>]*href="([^"]+)"[^>]*>(?:.*?<\/a>)?/g, '$1').replace(/<\/?[^>]+(>|$)/g, "").replace(/(\r\n|\n|\r)/gm, '\n');
      rawText = rawText + '\n' + message.ts.slice(11,16) + ' ' + label + '\n' + clean_txt;
    } else {
      clean_txt = message.txt.replace(/(\r\n|\n|\r)/gm, '');
      rawText = rawText + '<div>' + message.ts.slice(11,16) + ' ' + label + '</div><div>' + clean_txt + '</div>';
    }
    
  }
}
if (rawText == '') {
  var line_4 = '<div><b>Содержание переписки недоступно.</b></div>'
} else {
  var line_4 = '<div><b>Содержание переписки</b></div>';
}

if (formated_text == 'true') {
  var final_text = '<html><head></head><body>' + line_1 + line_2 + line_3 + line_4 + rawText + '</body></html>';
} else {
  var final_text = line_1 + '\n' + line_2 + '\n' + line_3 + '\n\n' + line_4;
  final_text = final_text.replace(/<\/?[^>]+(>|$)/g, "") + '\n' + rawText;
}

var request = {
  from: from_email,
  to: email,
  subject: subject,
  body: final_text,
  files: JSON.parse(filesUrls)
}

var exit = {'request': JSON.stringify(request)};
exit;
""")+
// 
// Сценарий отправляет запрос на формирование письма
вызвать.внешнийСервис("{host}", "POST").сЗаголовками(("Content-Type","application/json")).сТеломСообщения("{request}").сохранитьРезультатКакСтроку("answer")+
// 
// Если сервис по отправке писем ответил ошибкой сценарий сообщает об этом пользователю и завершает работу
если("{http_code} > 202").то(
  //если запрос завершен с ошибкой сценарий помещает в диалог соответствующий комментарий и завершает свою работу
  комментарий("При отправке письма возникла ошибка {http_code} - {answer}")+
  завершить
)+
//
// Если сервис по отправке писем успешно отправляет письмо то сценарий сообщает об отправке и завершает свою работу
комментарий("Письмо с перепиской отправлено на адрес {email}") + 
завершить

Работа с таблицами Google Sheets

Простой поиск в таблице

Сценарий запрашивает у пользователя данные для поиска, ищет указанный текст по всем строкам и колонкам таблицы и сообщает о результатах поиска в формате “Да” или “Нет”

 Нажмите здесь, чтобы развернуть пример сценария
//
// Сценарий ищет текст в таблице Google Sheets по значению в любой колонке и возвращает сообщение "Да" если текст найден или "Нет" если не найден
//
// Укажите логин сервисного аккаунта Google
установитьПеременную("email", "autofaqaccount@autofaqproject.iam.gserviceaccount.com") +
// Укажите идентификатор таблицы Google Sheet
установитьПеременную("sheetId", "1UyTG1ag6kb-tKCD0Wc5nD21zE38TvYSiMMQ_vzwxWbs")+
// Укажите название листа в таблице Google Sheet
установитьПеременную("sheetName", "Sheet1")+
//
// Настройка источника текста для поиска - спросить у пользователя или взять текст изначального запроса
// запрос - изначальный запрос
// вопрос - спросить у пользователя
установитьПеременную("querySource", "вопрос")+
//
// Основной код сценария 
//
// Получение текста для поиска в таблице
если("{querySource} == запрос").то(
	установитьПеременную("searchQuery", "{platformInMessageQuery}")
)+
если("{querySource} == вопрос").то(
	задатьПользователюВопрос("Скажи номер").сохранитьРезультат("searchQuery")
)+
//
// Получение временного токена для работы с гугл таблицей
вызвать.внешнийСервис("https://denisk.autofaq.ai/gtoken","GET").сЗаголовками(("Content-Type", "application/json"),("charset", "utf-8")).сПараметрами(("email","{email}")).сохранитьРезультат(("message","message"),("token","token"),("status","status"))+
//
// Проверка результатов запроса, в случае ошибки сценарий сообщит об ошибке и завершится 
если("{http_code} > 210").то(
	комментарий("Сценарий не смог получить токен для работы с таблицей. {message}")+
	сообщениеПользователю.сШаблоном("Спасибо за участие, но что-то сломалось и я не могу связаться с Гугл.")+
	завершить
)+
если("{status} == error").то(
	комментарий("Сценарий не смог получить токен для работы с таблицей. {message}")+
	сообщениеПользователю.сШаблоном("Спасибо за опрос, но что-то сломалось и я не смог сохранить результаты.")+
	завершить
)+
//
// Отправка данных в гугл таблицу
вызвать.внешнийСервис("https://sheets.googleapis.com/v4/spreadsheets/{sheetId}/values/{sheetName}","GET").сЗаголовками(("Authorization", "Bearer {token}"),("Content-Type","application/json")).сПараметрами(("majorDimension", "COLUMNS")).сохранитьРезультатКакСтроку("result")+
если("{http_code} > 210").то(
	комментарий("Сценарий не смог отправить запрос на поиск в гугл таблице. Код {http_code}, ответ {result}")+
	сообщениеПользователю.сШаблоном("Спасибо за участие, но что-то сломалось и я не могу связаться с Google.")+
	завершить
)+
//
// Разбор ответа от гугл таблицы
выполнитьJs("""
var error = 0;
var message = '';
var rowIndex = 0;

try {
    var parsedresult = JSON.parse(result);
} catch(err) {
    var exit = {'error': 1, 'message':'Ошибка разбора ответа от Гугл'};
    var parsedresult = {};
}

if (parsedresult.hasOwnProperty('values')) {

    for (var i = 0; i < parsedresult.values.length; i++) {
        rowIndex = rowIndex + (parsedresult.values[i].indexOf(searchQuery) > -1 ? 1 : 0);
    }

    if (rowIndex > 0) { 
        var exit = {'error': 0, 'message':'Да'};
    } else {
        var exit = {'error': 0, 'message':'Нет'};
    }

} else {
    var exit = {'error': 1, 'message':'В ответе из таблицы не найдено данных'};
}

exit;
""") +
//
// В случае ошибки при поиске данных сценарий сообщит об ошибке и завершится
если("{error} == 1").то(
    сообщениеПользователю.сШаблоном("При получении данных из таблицы возникла ошибка. {message}")+
    завершить
) +
//
// Если при поиске данных ошибки не возникло, то сценарий сообщит о результате поиска и завершится 
сообщениеПользователю.сШаблоном("{message}")+
завершить

Поиск в таблице и возврашение всех данных из строки с заголовками

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

 Нажмите здесь, чтобы развернуть пример сценария
//
// Сценарий ищет строку в таблице Google Sheets по значению в первой колонке и возвращает данные из всей строки
//
// Укажите логин сервисного аккаунта Google
установитьПеременную("email", "account_name@project_name.iam.gserviceaccount.com") +
// Укажите идентификатор таблицы Google Sheet
установитьПеременную("sheetId", "ХХХХХХ-ХХХХХХХХХХХХХ")+
// Укажите название листа в таблице Google Sheet
установитьПеременную("sheetName", "Sheet1")+
//
// Укажите источник текста для поиска - спросить у пользователя или взять текст изначального запроса
// запрос - текст запроса, с которым пользователь пришел в сценария
// вопрос - спросить у пользователя
установитьПеременную("querySource", "вопрос")+
//
// Основной код сценария 
//
// Получение текста для поиска в таблице
если("{querySource} == запрос").то(
	установитьПеременную("searchQuery", "{platformInMessageQuery}")
)+
если("{querySource} == вопрос").то(
	задатьПользователюВопрос("Скажи номер").сохранитьРезультат("searchQuery")
)+
//
// Получение временного токена для работы с гугл таблицей
вызвать.внешнийСервис("https://denisk.autofaq.ai/gtoken","GET").сЗаголовками(("Content-Type", "application/json"),("charset", "utf-8")).сПараметрами(("email","{email}")).сохранитьРезультат(("message","message"),("token","token"),("status","status"))+
//
// Проверка результатов запроса, в случае ошибки сценарий сообщит об ошибке и завершится 
если("{http_code} > 210").то(
	комментарий("Сценарий не смог получить токен для работы с таблицей. {message}")+
	сообщениеПользователю.сШаблоном("Спасибо за участие, но что-то сломалось и я не могу связаться с Гугл.")+
	завершить
)+
если("{status} == error").то(
	комментарий("Сценарий не смог получить токен для работы с таблицей. {message}")+
	сообщениеПользователю.сШаблоном("Спасибо за опрос, но что-то сломалось и я не смог сохранить результаты.")+
	завершить
)+
//
// Отправка запроса в Google Sheets
вызвать.внешнийСервис("https://sheets.googleapis.com/v4/spreadsheets/{sheetId}/values/{sheetName}","GET").сЗаголовками(("Authorization", "Bearer {token}"),("Content-Type","application/json")).сПараметрами(("majorDimension", "COLUMNS")).сохранитьРезультатКакСтроку("result")+
//
// Проверка результатов запроса, в случае ошибки сценарий сообщит об ошибке и завершится
если("{http_code} > 210").то(
	комментарий("Сценарий не смог отправить запрос на поиск в гугл таблице. Код {http_code}, ответ {result}")+
	сообщениеПользователю.сШаблоном("Спасибо за участие, но что-то сломалось и я не могу связаться с Google.")+
	завершить
)+
//
// Разбор ответа от гугл таблицы
выполнитьJs("""
var error = 0;
var message = '';
var rowIndex = 0;

try {
	var parsedresult = JSON.parse(result);
} catch(err) {
	var exit = {'error': 1, 'message':'Ошибка разбора ответа от Гугл'};
	var parsedresult = {};
}

if (parsedresult.hasOwnProperty('values') && parsedresult.values[0].length > 1) {
	var rowIndex = parsedresult.values[0].indexOf(searchQuery);
	
	if (rowIndex > 0) { 
		for (var i = 0; i < parsedresult.values.length; i++) {
			if (parsedresult.values[i].length > rowIndex) {
				message = message + parsedresult.values[i][0].toString() + ': ' + parsedresult.values[i][rowIndex] + '<br>';
			}			
		}
		var exit = {'error': 0, 'message':message};
	} else {
		var exit = {'error': 1, 'message':'Значение не найдено в первой колонке таблицы'};
	}

} else {
	var exit = {'error': 1, 'message':'В ответе из таблицы не найдено данных'};
}

exit;
""") +
//
// В случае ошибки при поиске данных сценарий сообщит об ошибке и завершится
если("{error} == 1").то(
	сообщениеПользователю.сШаблоном("При получении данных из таблицы возникла ошибка. {message}")+
	завершить
) +
//
// Если при поиске данных ошибки не возникло, то сценарий сообщит найденные данные и завершится 
сообщениеПользователю.сШаблоном("Найдены следующие данные")+
сообщениеПользователю.сШаблоном("{message}")+
завершить

Опрос пользователя и запись данных в новую строку таблицы

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

Сценарий получает адрес электронной почты из данных пользователя. Если адрес не указан - запрашивает его у пользователя. Сценарий ищет адрес в первой колонке таблицы. Если адрес электронной почты пользователя найден, то сценарий завершает работу. Если адрес не найден - проводит опрос и сохраняет данные в новую строку таблицы.

 Нажмите здесь, чтобы развернуть пример сценария
//
// Сценарий ищет текст в таблице Google Sheets по значению в любой колонке и возвращает сообщение "Да" если текст найден или "Нет" если не найден
//
// Укажите логин сервисного аккаунта Google
установитьПеременную("email", "account_name@project_name.iam.gserviceaccount.com") +
// Укажите идентификатор таблицы Google Sheet
установитьПеременную("sheetId", "ХХХХХХ-ХХХХХХХХХХХХХ")+
// Укажите название листа в таблице Google Sheet
установитьПеременную("sheetName", "Sheet1")+
//
// Основной код сценария 
//
//
// Получение временного токена для работы с гугл таблицей
вызвать.внешнийСервис("https://denisk.autofaq.ai/gtoken","GET").сЗаголовками(("Content-Type", "application/json"),("charset", "utf-8")).сПараметрами(("email","{email}")).сохранитьРезультат(("message","message"),("token","token"),("status","status"))+
//
// Проверка результатов запроса, в случае ошибки сценарий сообщит об ошибке и завершится 
если("{http_code} > 210").то(
	комментарий("Сценарий не смог получить токен для работы с таблицей. {message}")+
	сообщениеПользователю.сШаблоном("Спасибо за участие, но что-то сломалось и я не могу связаться с Гугл.")+
	завершить
)+
если("{status} == error").то(
	комментарий("Сценарий не смог получить токен для работы с таблицей. {message}")+
	сообщениеПользователю.сШаблоном("Спасибо за опрос, но что-то сломалось и я не смог сохранить результаты.")+
	завершить
)+
//
// Получение адреса электронной почты
установитьПеременную("enteredEmail", "{userEmail}") +
если("{enteredEmail} == ").то(
	задатьПользователюВопрос("Укажите адрес вашей электронной почты").сохранитьРезультат("enteredEmail") +
	установитьПеременнуюПользователю("userEmail", "{enteredEmail}")
) +
//
// Отправка запроса на поиск адреса в гугл таблицу
вызвать.внешнийСервис("https://sheets.googleapis.com/v4/spreadsheets/{sheetId}/values/{sheetName}","GET").сПараметрами(("majorDimension","COLUMNS")).сЗаголовками(("Authorization", "Bearer {token}"),("Content-Type","application/json")).сохранитьРезультатКакСтроку("checkLoginResult") +
//
// Проверка результатов запроса, в случае ошибки сценарий сообщит об ошибке и завершится 
если("{http_code} > 210").то(
	комментарий("Сценарий не смог отправить запрос на поиск в гугл таблице. Код ответа {http_code}, ответ {checkLoginResult}")+
	сообщениеПользователю.сШаблоном("Спасибо за участие, но что-то сломалось и я не могу связаться с центром.")+    
    завершить
)+
выполнитьJs("""
var checkLoginResultParsed = JSON.parse(checkLoginResult);
if ("values" in checkLoginResultParsed) {
	if (checkLoginResultParsed['values'][0].indexOf(userLogin) != -1) {
		var checkLoginResultData = 1;
	} else {
		var checkLoginResultData = 0;
	}
} else {
	var checkLoginResultData = 0;
}
var exit = {'checkLoginResultData':checkLoginResultData};
exit;
""") +
//
// Если адрес почты пользователя найден в таблице то сценарий сообщает об этом пользователю и завершается
если("{checkLoginResultData} == 1").то(
	комментарий("Логин пользователя {userLogin} уже найден в таблице, поэтому опрос останавливаю")+	
	сообщениеПользователю.сШаблоном("Вы уже проходили этот опрос. Второй раз спрашивать нет необходимости")+	
	завершить
)+
//
// Сценарий начинает опрос пользователя
задатьПользователюВопрос("Пожалуйста назовите ваше ФИО").сохранитьРезультат("fio") +
задатьПользователюВопрос("Пожалуйста назовите ваш отдел").сохранитьРезультат("otdel") +
задатьПользователюВопрос("Пожалуйста назовите вашу должность").сохранитьРезультат("dolznost") +
задатьПользователюВопрос("Оцените удобство нашего сервиса").сВариантамиОтвета("Все устраивает", "Возникают трудности", "Комментарий").сохранитьРезультат("udobstvo_main") +
установитьПеременную("udobstvo_add", " ")+
если("{udobstvo_main} == Возникают трудности").то(задатьПользователюВопрос("Какие у вас трудности?").сохранитьРезультат("udobstvo_add")) +
если("{udobstvo_main} == Комментарий").то(задатьПользователюВопрос("Напишите ваш комментарий").сохранитьРезультат("udobstvo_add")) +
// Открытые вопросы
задатьПользователюВопрос("Напишите какие дополнительные сервисы Вы хотели бы видеть").сохранитьРезультат("new_services") +
задатьПользователюВопрос("Напишите Ваши предложения и пожелания").сохранитьРезультат("suggestions") +
// 
выполнитьJs("""
var now = new Date();
var login = enteredEmail;
var udobstvo = udobstvo_main + udobstvo_add;

var requestBody = {"range": sheetName+"!A1:O1", "majorDimension": "ROWS", "values":[[now, login, fio, otdel, dolznost, udobstvo, new_services, suggestions]]};
var requestBodyJSON = JSON.stringify(requestBody);

var exit = {'requestBodyJSON':requestBodyJSON};
exit;
""") +
//
// Получение временного токена для работы с гугл таблицей
вызвать.внешнийСервис("https://denisk.autofaq.ai/gtoken","GET").сЗаголовками(("Content-Type", "application/json"),("charset", "utf-8")).сПараметрами(("email","{email}")).сохранитьРезультат(("message","message"),("token","token"),("status","status"))+
//
// Проверка результатов запроса, в случае ошибки сценарий сообщит об ошибке и завершится 
если("{http_code} > 210").то(
	комментарий("Сценарий не смог получить токен для работы с таблицей. {message}")+
	сообщениеПользователю.сШаблоном("Спасибо за участие, но что-то сломалось и я не могу связаться с центром.")+    
    завершить
)+
если("{status} == error").то(
	комментарий("Сценарий не смог получить токен для работы с таблицей. {message}")+
	сообщениеПользователю.сШаблоном("Спасибо за опрос, но что-то сломалось и я не смог сохранить результаты.")+    
    завершить
)+
//
// Отправка запроса на добавление строки в таблицу
вызвать.внешнийСервис("https://sheets.googleapis.com/v4/spreadsheets/{sheetId}/values/Sheet1!A1:O1:append","POST").сПараметрами(("valueInputOption","USER_ENTERED")).сЗаголовками(("Authorization", "Bearer {token}"),("Content-Type","application/json")).сТеломСообщения("{requestBodyJSON}").сохранитьРезультатКакСтроку("result")+
//
// Проверка результатов запроса, в случае ошибки сценарий сообщит об ошибке и завершится 
если("{http_code} > 210").то(
	комментарий("Сценарий не смог отправить запрос на добавление строчки в гугл таблице. Код ответа {http_code}")+
	сообщениеПользователю.сШаблоном("Спасибо за участие, но что-то сломалось и я не могу связаться с центром.")+    
    завершить
)+
комментарий("Добавил строчку в гугл таблицу.")+
сообщениеПользователю.сШаблоном("Все записал. Большое спасибо за участие!")+
завершить

Прочие примеры сценариев

Предлагать случайно один из нескольких вариантов ответа

Сценарий на вопрос пользователя предлагает случайно один из нескольких вариантов ответа

 Нажмите здесь, чтобы развернуть пример сценария
//количество сообщений для случайного отображения
//
установитьПеременную("krandom","3")+
//сообщения, которые будут случайно выдаваться
//
установитьПеременную("message1","Первое сообщение")+
установитьПеременную("message2","Второе сообщение")+
установитьПеременную("message3","Третье сообщение")+
//генерируем случайное число от 0 до krandom
//
выполнитьJs("""
var a = Number(krandom);
function getRandomInt(max) {
  return Math.floor(Math.random() * max);
}
var numbermessage = getRandomInt(a);
numbermessage = String(numbermessage);
var ext = {'numbermessage':numbermessage};
ext;
""")+
//отображаем сообщение в зависимости от сгенерированного числа
//
если("{numbermessage} == 0").то(
  сообщениеПользователю.сШаблоном("{message1}")
  )+
если("{numbermessage} == 1").то(
  сообщениеПользователю.сШаблоном("{message2}")
  )+
если("{numbermessage} == 2").то(
  сообщениеПользователю.сШаблоном("{message3}")
  )+
завершитьИЗакрытьДиалог()
  • No labels