- Created by Denis Kiselev, last modified on Dec 20, 2022
You are viewing an old version of this page. View the current version.
Compare with Current View Page History
« Previous Version 30 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 или по документации.
// // Сценарий получения ответа из статьи 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 запрос
Сценарии вызываемые операторами
Работа с таблицами 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}")+ сообщениеПользователю.сШаблоном("Спасибо за участие, но что-то сломалось и я не могу связаться с центром.")+ завершить )+ комментарий("Добавил строчку в гугл таблицу.")+ сообщениеПользователю.сШаблоном("Все записал. Большое спасибо за участие!")+ завершить
- No labels