Переписка с пользователями
Простые сценарии, включающие в себя опрос пользователя с пользователем и подготовку ответа на стороне сценария.
Короткий опрос пользователя и возврат ответов
Сценарий ведет небольшую переписку с пользователем и возвращает ответы в зависимости от реакции пользователя.
Во время работы сценария все реплики пользователя будут передаваться в этот сценарий. После завершения сценария сервис будет ждать ответную реакцию пользователя в течение определенного в настройках времени. Если пользователь напишет реплику - она будет передана в базы знаний. Если пользователь не напишет реплику в указанное время - сервис закроет диалог.
Нажмите здесь, чтобы развернуть пример сценария
//
// Сценарий простой переписки с пользователем
//
// Приветственное сообщение и первый вопрос
сообщениеПользователю.сШаблоном("Добрый день! Сейчас я помогу!") +
задатьПользователюВопрос("Я правильно понял, что вы заблудились в лесу?").сВариантамиОтвета("Да", "Нет").сохранитьРезультат("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}")+
завершить
Стартовые и приветственные сценарии
Сценарии, которые автоматически запускаются при поступлении первого сообщения пользователя в диалоге. В такие сценарии удобно вставлять автоматические проверки данных пользователя, заполнение служебных переменных и приветсвенные меню доступных действий.
Такие сценарии размещаются в Приветствии или Интеграции на поступление диалога.
Разные приветствия для пользователей разных каналов
Сценарий приветствует пользователя разными сообщениями в зависимости от типа или идентификатора канала, через который обратился пользователь. После приветствия запрос пользователя передается в базы знаний.
Нажмите здесь, чтобы развернуть пример сценария
//
// Сценарий описывает различные приветствия в зависимости от типа и ИД канала
//
если("{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}")
Работа с 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}")
)+
завершитьИЗакрытьДиалог()