Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

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

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Code Block
//
// Сценарий получения ответа из статьи 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}")+
завершить

...

Такие сценарии размещаются в Приветствии или Интеграции на поступление диалога.

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

...

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

Code Block
//
// Сценарий сообщает об отсутствии операторов поддержки в ночное время
//
// Настройка сценария
// Смещение текущего времени службы поддержки относительно 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.")+
завершитьИЗакрытьДиалог()

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

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

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

В зависимости от рабочего/нерабочего времени уведомляется пользователя соответствующим сообщением.

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Image Removed
Code Block
//
// Сценарий проверяет наличие ответа и если ответа нет то предлагает пользователю меню запросовсообщает об отсутствии операторов поддержки в ночное время, в выходные и в заданные даты дд.мм.гггг
//

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

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

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

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

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

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Image RemovedImage Removed
Code Block
//
// Сценарий создает переменные диалога 
//
// Настройки сценария
//
// Список приоритетов
установитьПеременную("priorities_list","""{"1": "Критичный", "2": "Срочный", "3": "Средний", "4": "Низкий"}""") +
//
// Список тематик
выполнить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}"поддержки
установитьПеременную("startHour", "9") +
// время окончания работы службы поддержки (не включая)
установитьПеременную("endHour", "18") +
// Основной код сценария
// проверка текущего времени на рабочее время, выходной и праздник
выполнитьJs("""
// устанавливаем список праздничных дат
var holidayDates = ["13.03.2023", "14.03.2023", "04.08.2023"];
//
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 month = currentDate.getMonth()+1; 
var tdate = currentDate.getDate();
var newdate = (tdate < 10 ? '0' : '') + tdate + '.' + (month < 10 ? '0' : '') + month + '.' + currentDate.getFullYear();
var currentDay = currentDate.getDay();
var start = parseInt(startHour);
var stop = parseInt(endHour);
// проверяем дату на наличие в списке праздников
if (holidayDates.indexOf(newdate) != -1) {
	var resultHoliday = 1;
} else {
	var resultHoliday = 0;
}
// получаем признак рабочего/нерабочего времени
if (currentHour >= start && currentHour < stop && currentDay != 0 && currentDay != 6 && resultHoliday != 1 ) {
	var workTime = 1;
} else {
	var workTime = 0;	
}
var exit = {'workTime':workTime,'newdate':newdate};
exit;
""")+
//
// Сейчас рабочее время, сценарий передает запрос в базы знаний без уведомлений пользователя
если("{workTime} == 1").то(
//комментарий("Рабочее время")
сообщениеПользователю.сШаблоном("Рабочее время")
) +
если("{workTime} == 0").то(
	сообщениеПользователю.сШаблоном("Операторы технической поддержки на ваш вопрос в рабочее время. График работы виджета: пн-пт, 9:00-18:00 (МСК) за исключением праздничных и предпраздничных дней.")
)+
завершить

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

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

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

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Image Added
Code Block
//
// Сценарий проверяет наличие ответа и если ответа нет то предлагает пользователю меню запросов
// 
// Проверка наличия ответа на запрос в базах знаний
запросВБазыЗнаний("{platformInMessageQuery}", "botKnows")+
//
// В базе знаний есть ответ, сценарий передает запрос в базу знаний
если("{botKnows} == true").то(	
	завершитьСНовымЗапросом("{platformInMessageQuery}")
)+
//
// СозданиеВ базе переменнойзнаний выборанет сервисапрямого изответа спискана установитьПеременнуюВДиалог(запрос, сценарий предлагает выбрать из ключготовых =вариантов
"2_topicId",
    значение = "",
    название = "Выбор тематики",
    редактируемое = true,
    показыватьОператору = true,
    обязательное = false,
    множественныйВыбор = false,
    тип = "Выпадающий список",
	варианты = "{topics}"
)+
//
// Создание переменной указания адреса
установитьПеременнуюВДиалог(
    ключ = "3_address",
    значение = "",
    название = "Адрес",
    редактируемое = true,
    показыватьОператору = true,
    обязательное = false,
    множественныйВыбор = false,
    тип = "Текст"задатьПользователюВопрос("Здравствуйте 😊<br>Что Вас интересует?").сВариантамиОтвета("Первое","Второе","Компот").сохранитьРезультат("userChoice")+
//
// Если пользователь выбрал первый вариант, то сценарий передает запрос "Первые блюда" в базу знаний
если("{userChoice} == Первое").то(
	завершитьСНовымЗапросом("Первые блюда")
)+
//
// Если пользователь выбрал второй вариант, то сценарий передает запрос "Вторые блюда" в базу знаний
если("{userChoice} == Второе").то(
	задатьПользователюВопрос("Уточните по второмк?").сВариантамиОтвета("Гарнир","Горячее","Компот").сохранитьРезультат("userChoice2")+
	если("{userChoice} == Гарнир").то(
		завершитьСНовымЗапросом("Гарнир")
    )+
    если("{userChoice} == Горячее").то(
		сообщениеПользователю.сШаблоном("Наше горячее самое вкусное") +
        завершить
    )+
    если("{userChoice} == Горячее").то(
		сообщениеПользователю.сШаблоном("Про компот мы ничего не знаем, перевожу на оператора") +
        завершитьИПеревестиНаОператора
    )
)+
//
// СозданиеЕсли переменнойпользователь указаниявыбрал комментариятретий квариант, заявкето установитьПеременнуюВДиалог(сценарий отправляет ответ и  ключзавершается
если("{userChoice} == "4_comment",
    значение = "",
    название = "Комментарий к вызову",
    редактируемое = true,
    показыватьОператору = true,
    обязательное = false,
    множественныйВыбор = false,
    тип = "Текст"
)+
завершить

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

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

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

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

...

Компот").то(
	сообщениеПользователю.сШаблоном("Закончился. Всего хорошего!") +
	завершить
)+
//
// Если пользователь не выбирал вариантов а написал другой запрос, то он передается в базы знаний
завершитьСНовымЗапросом("{userChoice}")

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

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

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

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

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Image RemovedImage AddedImage Added
Code Block
//
// Сценарий создает переменные диалога и автоматически предзаполняет часть из них 
//
на основе классификации запроса пользователя 
// // Настройки сценария
//
// НастройкиСценарий дляописывает проведения классификации запроса пользователя
//
// Адрес 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/варианты для одноуровневого списка приоритетов
установитьПеременную("priorities_list","""{"1": "Критичный", "2": "Срочный", "3": "Средний", "4": "Низкий"}""") +
//
// УкажитеСценарий токенописывает дляварианты запросамногоуровневневого всписка базутематик знаний,через братьJS из поля "user tokenкод
выполнитьJs("""
вvar разделеnetwork Настройки - Общие - Параметры прямого подключения к базам знаний через 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;
""") +
= [{"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,
    тип = "Текст"
)+
завершить

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

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

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

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

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

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Image Added
Code Block
//
// Сценарий создает переменнюпеременные диалога дляи выбораавтоматически продуктапредзаполняет ичасть предзаполняетиз егоних установитьПеременнуюВДиалог(
// на основе классификации ключзапроса =пользователя "service",
//
// Настройки сценария
значение//
= "{docId}",
    название = "Выберите продукт",
    редактируемое = true,
    показыватьОператору = true,
    обязательное = true,
    множественныйВыбор = false,
    тип = "Выпадающий список",
    варианты = "{docs}"
)+
завершить

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

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

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

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

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Code Block
//
// Приветственный сценарий с авторизацией пользователя и отправкой проверочного кода на почту
//
// Настройки сценария
// Укажите адреса для API вызовов  
установитьПеременную("get_user_url", "https://itsm.company.ru/api/v1/getUser")+
установитьПеременную("post_url", "http://porter/api/postman/mail// Настройки для проведения классификации запроса пользователя
//
// Адрес 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", "ХХХХХХХХХХХ")+
//
// Укажите APIминимальный ключуровень дляуверенности запросовответа надля получение данных о пользователяхуспешной классификации
установитьПеременную("apikeyminimumScore", "ХХХХХХХХХ==10") +
//
// Основной код сценарий
//
// Сценарий считываетотправляет табельныйзапрос номерна изклассификацию профилясообщения пользователя
установитьПеременнуювызвать.внешнийСервис("user_tabNumber{kb_qna_enpoint}", "POST"{userPayload.tabNumber}") +
//
// Если табельный номер проставлен, то сценарий сразу передает запрос в базы знаний без процедуры аутентификации
// В дальнейшем сбда можно вставить проверку времени действия аутентификации или проверку на увольнение сотрудника
если("{user_tabNumber} != ).сЗаголовками(("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").то(  
завершитьСНовымЗапросом  комментарий("{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}. Обратитесь в техническую поддержку.")+ 
    завершитьИЗакрытьДиалог()  
  При запросе на классификацию возникла ошибка {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;
""")+
//
// Разбор ответа от 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)  Сценарий оставляет комментарий с результатами классификации для администратора сервиса
комментарий("Результаты классификации: определен документ {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 tempStringanswerParsed = firstPartJSON.slice(2,firstPart.length-2parse(answerDocs);
} catch(err) {  var draftString =
'***************************************'.slice(0,tempString.length);     var firstPartStringanswerParsed = firstPart.slice(0,2) + draftString + firstPart.slice(firstPart.length-2,firstPart.length);
  {};
}

var docs = {};

if (firstPart(answerParsed.hasOwnProperty('documents') && answerParsed.documents.length ==> 40) {
    for (var firstPartStringi = firstPart.slice(0,1) + '**';} i  if (firstPart< answerParsed.documents.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") ; 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. Идентификаторы групп для перевода можно получить в разделе настроек Группы.

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Image Added
Code Block
//
// Сценарий автоматически определяет группу по тексту запроса пользователя  
// актуально при подключении пользовательского канала одновременно к нескольким группам
// в этом случае АФ не будет запрашивать выбор группы у пользователя 
//
// Настройки сценария
//
// Настройки для проведения классификации запроса пользователя
//
// Адрес 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
завершитьСНовымЗапросомвызвать.внешнийСервис("{userChoicekb_qna_enpoint}")

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

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

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

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

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

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

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

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

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

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

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

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Code Block
//
// Сценарий ищет текст в таблице 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,"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")).сПараметрами(,("emailAUTOFAQ-User-Token", "{emailautorizationToken}")).сохранитьРезультат(сТеломСообщения("message","message"),("token","token"),("status","status")){query_text_3}").сохранитьРезультатКакСтроку("answer_3")+
// // Проверка результатоврезльтатов запросазапрос, если взапрос завершился случаес ошибкиошибкой сценарий сообщитоставляет обкомментарий ошибкедля иадминистратора завершитсясервиса 
если("{http_code} >!= 210200").то(  
  	комментарий("СценарийПри незапросе смогна получитьклассификацию токенвозникла для работы с таблицей. {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ошибка {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++) {
    var   exit = {'error': 1, 'message':'Ошибка разбора ответа от Гугл'};if (answers[i][j].hasOwnProperty('results') && answers[i][j].results.length > 0) {
      var parsedresult = {}; }  if (parsedresult.hasOwnProperty('values')) {

    for (var i = 0; i < parsedresult.values.length; i++) {answers[i][j].results[0].score > res && answers[i][j].results[0].score > minimumConfidenceLevel) {
                 rowIndexres = rowIndex + (parsedresult.values[i].indexOf(searchQuery) > -1 ? 1 : 0);answers[i][j].results[0].score;
            }
       if }
(rowIndex > 0) { }
    results[i] = res;
}
var exitposition = {'error': 0, 'message':'Да'};
    } else {
   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 exitgroup = {'error': 0, 'message':'Нет'};
    }

} else {
    Number(position) + 1;
} catch(err) {
    var group = 0;
}
var exit = {'error': 1, 'message':'В ответе из таблицы не найдено данных'};
}

group':JSON.stringify(group)};
exit;
""") +
//
// ВСценарий случаеопределил ошибкигруппу, приответ поиске данных сценарий сообщит об ошибке и завершитсяиз баз знаний которой пришел с максимальной уверенностью и переводит запрос на эту группу
// Идентификаторы групп можно получить в разделе настроек Группы
комментарий("Определил группу номер {group}")+
// Сценарий переводит запрос на группу 1
если("{errorgroup} == 1").то(
    сообщениеПользователю.сШаблономкомментарий("ПриПеревожу получениичат данныхна изгруппу таблицыВторая возникла ошибка. {message}линия HR")+
    завершитьперевестиНаГруппу("ХХХХХ-ХХХХ-ХХХХ-ХХХХ-ХХХХХ").иЗавершить
) +
// //Сценарий Еслипереводит призапрос поискена данныхгруппу ошибки не возникло, то сценарий сообщит о результате поиска и завершится 
сообщениеПользователю.сШаблоном("{message}")+
завершить

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

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

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Image RemovedImage Removed
Code Block
//2
если("{group} == 2").то(
    комментарий("Перевожу чат на группу Первая линия")+
    перевестиНаГруппу("ХХХХХ-ХХХХ-ХХХХ-ХХХХ-ХХХХХ").иЗавершить
)+
// Сценарий ищетпереводит строкузапрос вна таблицегруппу Google Sheets по значению в первой колонке и возвращает данные из всей строки
//
// Укажите логин сервисного аккаунта Google
установитьПеременную("email", "account_name@project_name.iam.gserviceaccount.com") 3
если("{group} == 3").то(
    комментарий("Перевожу чат на группу Вторая линия IT")+
    перевестиНаГруппу("ХХХХХ-ХХХХ-ХХХХ-ХХХХ-ХХХХХ").иЗавершить
)+
//
Укажите// идентификаторСценарий таблицыне Googleсмог Sheet
установитьПеременную("sheetId", "ХХХХХХ-ХХХХХХХХХХХХХ")+
// Укажите название листа в таблице Google Sheet
установитьПеременную("sheetName", "Sheet1")+
//
// Укажите источник текста для поиска - спросить у пользователя или взять текст изначального запроса
// запрос - текст запроса, с которым пользователь пришел в сценария
// вопрос - спросить у пользователя
установитьПеременную("querySource", "вопрос")+определить группу, пользователю будет предложено выбрать группу самому
комментарий("Не смог определить группу")+
завершить

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

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

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

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

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Code Block
//
// Приветственный сценарий с авторизацией пользователя и отправкой проверочного кода на почту
//
// ОсновнойНастройки код сценария 
// // Получение текстаУкажите адреса для поискаAPI ввызовов  таблице
еслиустановитьПеременную("{querySource} == запрос").то(
	установитьПеременную("searchQuery", "{platformInMessageQuery}")
)+
если("{querySource} == вопрос").то(
	задатьПользователюВопрос("Скажи номер").сохранитьРезультат("searchQuery")
)get_user_url", "https://itsm.company.ru/api/v1/getUser")+
установитьПеременную("post_url", "http://porter/api/postman/mail")+
//
// Укажите API ключ для запросов на получение данных о пользователях
установитьПеременную("apikey", "ХХХХХХХХХ==")+
//
// Сценарий считывает табельный номер из профиля пользователя
установитьПеременную("user_tabNumber", "{userPayload.tabNumber}") +
//
// ПолучениеЕсли временноготабельный токенаномер дляпроставлен, работыто ссценарий гуглсразу таблицей
вызвать.внешнийСервис("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}")+
	сообщениеПользователю.сШаблоном("Спасибо за опрос, но что-то сломалось и я не смог сохранить результаты.")+
	завершить
)передает запрос в базы знаний без процедуры аутентификации
// В дальнейшем сбда можно вставить проверку времени действия аутентификации или проверку на увольнение сотрудника
если("{user_tabNumber} != ").то(
  завершитьСНовымЗапросом("{platformInMessageQuery}")
)+
//
// Приветственное сообщение для неавторизованных пользователей
сообщениеПользователю.сШаблоном("Привет, я - бот-помощник. Для работы с ботом приготовьте, пожалуйста, свой табельный номер и проверьте доступ к рабочей электронной почте.") +
//
// Задаем пользователю вопрос с просьбой ввести табельный номер, для того чтобы использовать его в сервисах для получения данных о пользователе.
задатьПользователюВопрос("Введите свой табельный номер.").сохранитьРезультат("entered_tabNumber") +
//
// Отправка запроса в Google SheetsЗапрос в ITSM систему на получение данных о пользователе по табельному номеру
вызвать.внешнийСервис("https://sheets.googleapis.com/v4/spreadsheets/{sheetId}/values/{sheetName}{get_user_url}","GET").сЗаголовкамисПараметрами(("AuthorizationtabNumber", "Bearer {tokenentered_tabNumber}"),).сЗаголовками(("Content-Type", "application/json")).сПараметрами,(("majorDimensionapikey", "COLUMNS{apikey}")).сохранитьРезультатКакСтроку("resultanswer") +
//
// Проверка результатов запроса, в случае ошибки сценарий сообщит об ошибке, завершится и закроет завершитсядиалог
если("{http_code} > 210200").то( 	 
    комментарий("Сценарий не смог отправить запросПри запросе на поиск впользователя гуглвозникла таблице.ошибка Код {http_code}, ответтекст ошибки {resultanswer}")+
	    сообщениеПользователю.сШаблоном("СпасибоПри запоиске участие,учетной нозаписи что-товозникла сломалось и я не могу связаться с Googleошибка {http_code}. Обратитесь в техническую поддержку.")+ 
	завершить )+
   завершитьИЗакрытьДиалог()  
  )+
//
// Разбор ответа от гугл таблицы ITSM системы, получение адреса электронной почты пользователя и генерация проверочного кода
// В дальнейшем можно добавить получение и сохранение данных пользователя помимо электронной почты
выполнитьJs("""
var error = 0;
var messageuser_email = '';
var
rowIndextry ={
0;  try { 	var parsedresultanswerParced = JSON.parse(resultanswer);
} catch(err) {
	var exit = {'error': 1,
'message':'Ошибка разбора ответа от Гугл'}; 	var parsedresultanswerParced = {};
}  

if (parsedresultanswerParced.hasOwnProperty('valuesemail') && 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}")+
завершить

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

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

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

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Image RemovedImage Removed
Code Block
//
// Сценарий ищет текст в таблице Google Sheets по значению в любой колонке и возвращает сообщение "Да" если текст найден или "Нет" если не найден
//
// Укажите логин сервисного аккаунта Google
установитьПеременную("email", "account_name@project_name.iam.gserviceaccount.com") +
// Укажите идентификатор таблицы Google Sheet
установитьПеременную("sheetId", "ХХХХХХ-ХХХХХХХХХХХХХ")+
// Укажите название листа в таблице Google Sheet
установитьПеременную("sheetName", "Sheet1")+
//
// Основной код сценария 
//
//
// Получение временного токена для работы с гугл таблицей
вызвать.внешнийСервис("https://denisk.autofaq.ai/gtoken","GET) {
  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"),).сТеломСообщения("charset", "utf-8")).сПараметрами(("email","{email}")).сохранитьРезультат(("message","message"),("token","token"),("status","status"))+
//
// Проверка результатов запроса, в случае ошибки сценарий сообщит об ошибке и завершится 
если("{http_code} > 210").то(
	комментарий("Сценарий не смог получить токен для работы с таблицей. {message}")+
	сообщениеПользователю.сШаблоном("Спасибо за участие, но что-то сломалось и я не могу связаться с Гугл.")+
	завершить
)+
если("{status} == error").то(
	комментарий("Сценарий не смог получить токен для работы с таблицей. {message}")+
	сообщениеПользователю.сШаблоном("Спасибо за опрос, но что-то сломалось и я не смог сохранить результаты.")+
	завершить{"to": "{user_email}","from":"no-reply@autofaq.ai", "subject":"Подключение к боту","body":"Кто-то пытается подключиться к боту используя ваш табельный номер. Если это сделали не Вы, то обратитесь пожалуйста в службу технической поддержки. Если это вы, то введите этот секретный код {pass_code}. <br>Внимание! Никому не сообщайте этот код!"}""").сохранитьРезультатКакСтроку("answer_email") +
//
// Проверка результатов запроса, в случае ошибки сценарий сообщит об ошибке, завершится и закроет диалог
если("{http_code} > 200").то(  
    комментарий("При отправке письма с проверочным кодом возникла ошибка {http_code}, текст ошибки {answer_email}")+
    сообщениеПользователю.сШаблоном("При отправке письма с проверочным кодом возникла ошибка {http_code}. Пожалуйста, попробуйте подключиться к боту позднее.")+ 
    завершитьИЗакрытьДиалог()  
)+
//
// Получение адреса электронной почты
установитьПеременную("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}")+
	сообщениеПользователю.сШаблоном("Спасибо за участие, но что-то сломалось и я не могу связаться с центром.")+    
    завершить
)+
комментарий("Добавил строчку в гугл таблицу.")+
сообщениеПользователю.сШаблоном("Все записал. Большое спасибо за участие! Сценарий выводит отправленный пользователю пароль и адрес почты в комментарий диалога для администратора сервиса
комментарий("Пользователю на адрес {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}")

Проверка срока действия авторизации

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

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Code Block
//Смещение текущего времени относительно GMT в часах
установитьПеременную("HourOffset", "3")+
//Задаем время в часах для повторной авторизации
установитьПеременную("retryAuth", "8")+
установитьПеременную("dateAuth","{userPayload.dateAuth}")+
//если пользователь ранее не заходил в чат, устанавливаем пользователю дату авторизации
если("{dateAuth} == ").то(
  выполнить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 exit = {'dateAuth':currentDate.toString()};
  exit;
  """) +
  комментарий("{dateAuth}")+
  установитьПеременнуюПользователю("userPayload.dateAuth","{dateAuth}")+
  завершить
)+ 
//если пользователь повторно приходит в чат, выполняем проверку текущего времени и вычисляем разницу в часах между последней авторизацией
выполнитьJs("""
var serverTime = new Date();
var serverTimeStamp = serverTime.getTime();
var timeZoneOffset = serverTime.getTimezoneOffset();
dateAuth = new Date(dateAuth);
var currentTimeStamp = serverTimeStamp + (parseInt(HourOffset) *  3600000) - (timeZoneOffset * 60000);
var currentDate = new Date(currentTimeStamp);
var diff = currentDate - dateAuth;
var hours = diff / (1000 * 60 * 60);
var exit = {'hours':hours};
exit;
""") +
комментарий("тут разница {hours}")+
если("{hours} > {retryAuth}").то(
  комментарий("Разница больще {retryAuth}. Требуется запустить повторную авторизацию")+
  завершить
)+
комментарий("Срок авторизации еще не истек. Можно продолжать диалог")+
завершить

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

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

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

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

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

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

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

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

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


Отправляем запрос в базы знаний через API

Сценарий отправляет запрос в БЗ AF через API QUERY, в зависимости от уровня уверенности переводим на оператора или отдаем ответ пользователю

Code Block
breakoutModewide
languagejs
//Устанавливаем переменную реплики, которую будем отправлять в БЗ
установитьПеременную("queryText","{qwery}")+
//адрес  метода query
установитьПеременную("kb_qna_xplain_enpoint", "https://адрес_системы/core-api/query/api/v1/query")+
//id БЗ
установитьПеременную("kb_id", "10")+
//токен из настроек БЗ
установитьПеременную("kb_token", "494125ce201f471234839f3a485ff237")+
//устанавливаем переменную с попрогом уверенности
установитьПеременную("minScoreXplain", "0.6")+
//собираем тело запроса
выполнитьJs("""
var tags = {"service_id":kb_id,"service_token":kb_token,"query":queryText,"top_k":"1"};
var exit = {'query_text': JSON.stringify(tags)};
exit;
""")+
комментарий("{queryText}")+
//вызов внешнего сервиса
вызвать.внешнийСервис("{kb_qna_xplain_enpoint}","POST").сЗаголовками(("Content-Type", "application/json"),("charset", "utf-8"),("AUTOFAQ-User-Token", "{autorizationToken_clear}")).сТеломСообщения("{query_text}").сохранитьРезультатКакСтроку("answer")+
//проверям ответ на ошибки
если("{http_code} != 200").то(  
  комментарий("При запросе на классификацию возникла ошибка {http_code}, текст ошибки {answer}")
)+
//парсим ответ от БЗ
выполнитьJs("""
var clearQuestion = '';
var score = '';
try {
    var answerParsed = JSON.parse(answer);
} catch(err) {    
    var answerParsed = {};
}
//забираем сам ответ
if (answerParsed.hasOwnProperty('results') && answerParsed.results.length > 0) {
  clearQuestion = answerParsed.results[0].answer;
  scoreXplain = answerParsed.results[0].score;
}
var exit = {'clearQuestion':clearQuestion, 'scoreXplain':scoreXplain};
exit;
""")+
если("{clearQuestion} == ").или("{scoreXplain}<{minScoreXplain}").то(
  комментарий("нет ответа")+
  //Сообщение если нет ответа в xplain
  сообщениеПользователю.сШаблоном("Не нашел ответа на Ваш вопрос, перевожу диалог на оператора.<br>К сожалению, сейчас мы не работаем. Наши операторы ответят Вам в рабочее время с понедельника по пятницу с 9:00 до 18:00 по Московскому времени.")+
  завершитьИПеревестиНаОператора
)+
задатьПользователюВопрос("{clearQuestion}").сВариантамиОтвета("Мой вопрос решен","Нужен оператор").сохранитьРезультат("end_said")+
	если("{end_said} == Мой вопрос решен").то(
		сообщениеПользователю("Благодарим за обращение!")+
		завершитьИЗакрытьДиалог()
	)+
	если("{end_said} == Нужен оператор").то(
		сообщениеПользователю("Перевожу диалог на оператора, ожидкайте")+
		завершитьИПеревестиНаОператора
	)+
завершитьСНовымЗапросом("{end_said}")

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

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

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

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

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

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Code Block
// Сценарий получения баланса клубной карты пользователя по номеру его телефона
// - сценарий считывает номер телефона из профиля пользователя, если номер в профиле не указан то сценарий запросит его у пользователя
// - сценарий отправит проверочный код через сервис отправки СМС и запрос ввод кода
// - сценарий получит в сервисе лояльности по номеру телефона список карт пользователя, для каждой карты запросит ее баланс и выведет суммарный баланс пользователю  
//
// Настройки сценария
// 
// Тексты сообщений сценария в сторону пользователя
//
// Текст просьбы указать номер телефона
установитьПеременную("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 = 
`<?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>`
 
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 = 
`<?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>`
 
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 в профиле пользователя

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

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

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

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

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

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

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

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

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

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

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

Code Block
// Сценарий отправляет на указанный адрес текст переписки с пользователем.
// Дополнительно к пистму прикладываются все файлы в переписке меньше установленного размера.
// Сценарий может быть использован как для отправки переписки самому пользователю так и для отправки письма в сторонний сервис. 
//
// Настройки сценария
// 
// Формат текста письма. Установите 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}") + 
завершить

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

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Image Added
Code Block
// Сценарий отправляет на указанный адрес текст переписки с пользователем.
// Дополнительно к письму прикладываются файлы, выбранные оператором.
// Сценарий может быть использован как для отправки переписки самому пользователю так и для отправки письма в сторонний сервис. 
//
// Настройки сценария
// 
// Формат текста письма. Установите true для HTML и false для обычного текста.
установитьПеременную("formated_text", "true") +
// Максимальный размер файлов для отправки в качестве вложения. Сценарий сообщит о пропущенных файлах и оператору и получателю письма.
установитьПеременную("useUserEmail", "true") +
// Адрес отправителя 
установитьПеременную("from_email", "noreply@autofaq.ai") +
// Предустановленная тема письма. Оператор сможет сменить тему перед отправкой письма.
установитьПеременную("subject", "Переписка с пользователем {userFullName}")+
//
// Адрес сервиса по отправке писем, встроенного в AutoFAQ
установитьПеременную("host", "http://porter/api/postman/mail") +
//дополнительные переменные
установитьПеременную("t1","false")+
установитьПеременную("t2","false")+
установитьПеременную("t3","false")+
установитьПеременную("t4","false")+
установитьПеременную("t5","false")+
//
// Основной код сценария
//
// Сценарий проверяет файлы в переписке
выполнитьJs("""
try {
  var parsedExtFiles = JSON.parse(externalFiles);
} catch(e) {
  var parsedExtFiles = [];
}
var numOfFiles = parsedExtFiles.length;
var filesUrl_01 = ''; var filesUrl_name_01 = '';var filesUrl_size_01 = '';
var filesUrl_02 = ''; var filesUrl_name_02 = '';var filesUrl_size_02 = '';
var filesUrl_03 = ''; var filesUrl_name_03 = '';var filesUrl_size_03 = '';
var filesUrl_04 = ''; var filesUrl_name_04 = '';var filesUrl_size_04 = '';
var filesUrl_05 = ''; var filesUrl_name_05 = '';var filesUrl_size_05 = '';
var idx = 0;
var contentIndex = 0;
if (numOfFiles > 5) {
  numOfFiles = 5;
}
if (numOfFiles > 0) {
  for (var i = 0; i < numOfFiles; i++) {
      idx = idx + 1;
      if (idx == 1) {filesUrl_01 = 'http://bot-platform-back:8090/api/files/' + JSON.parse(parsedExtFiles[i])['id'];filesUrl_name_01 =JSON.parse(parsedExtFiles[i])['name'];contentIndex = 1;filesUrl_size_01 = JSON.parse(parsedExtFiles[i])['size'];}
      if (idx == 2) {filesUrl_02 = 'http://bot-platform-back:8090/api/files/' + JSON.parse(parsedExtFiles[i])['id'];filesUrl_name_02 =JSON.parse(parsedExtFiles[i])['name'];contentIndex = 2;filesUrl_size_02 = JSON.parse(parsedExtFiles[i])['size'];}
      if (idx == 3) {filesUrl_03 = 'http://bot-platform-back:8090/api/files/' + JSON.parse(parsedExtFiles[i])['id'];filesUrl_name_03 =JSON.parse(parsedExtFiles[i])['name'];contentIndex = 3;filesUrl_size_03 = JSON.parse(parsedExtFiles[i])['size'];}
      if (idx == 4) {filesUrl_04 = 'http://bot-platform-back:8090/api/files/' + JSON.parse(parsedExtFiles[i])['id'];filesUrl_name_04 =JSON.parse(parsedExtFiles[i])['name'];contentIndex = 4;filesUrl_size_04 = JSON.parse(parsedExtFiles[i])['size'];}
      if (idx == 5) {filesUrl_05 = 'http://bot-platform-back:8090/api/files/' + JSON.parse(parsedExtFiles[i])['id'];filesUrl_name_05 =JSON.parse(parsedExtFiles[i])['name'];contentIndex = 5;filesUrl_size_05 = JSON.parse(parsedExtFiles[i])['size'];}
    }
}
var exit = {'contentIndex': contentIndex, 'filesUrl_01':filesUrl_01, 'filesUrl_02':filesUrl_02, 'filesUrl_03':filesUrl_03, 'filesUrl_04':filesUrl_04, 'filesUrl_05':filesUrl_05, 'filesUrl_name_01':filesUrl_name_01, 'filesUrl_name_02':filesUrl_name_02, 'filesUrl_name_03':filesUrl_name_03, 'filesUrl_name_04':filesUrl_name_04, 'filesUrl_name_05':filesUrl_name_05, 'filesUrl_size_01': filesUrl_size_01, 'filesUrl_size_02':filesUrl_size_02,'filesUrl_size_03':filesUrl_size_03,'filesUrl_size_04':filesUrl_size_04,'filesUrl_size_05':filesUrl_size_05};
exit;
""") +
//отображаем список файлов для выбора
если("{contentIndex} > 0").то(
  добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "t1_txt",
  значение = "{filesUrl_name_01} размер {filesUrl_size_01} b",
  название = "",
  тип = "Однострочный нередактируемый текст",
  обязательное = false
) +
  добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "t1",
  название = "",
  тип = "Переключатель",
  описание = "",
  обязательное = false
)
)+
если("{contentIndex} > 1").то(
  
  добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "t2_txt",
  значение = "{filesUrl_name_02} размер {filesUrl_size_02} b",
  название = "",
  тип = "Однострочный нередактируемый текст",
  обязательное = false
) +
  добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "t2",
  название = "",
  тип = "Переключатель",
  описание = "",
  обязательное = false
)
)+
если("{contentIndex} > 2").то(
  добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "t3_txt",
  значение = "{filesUrl_name_03} размер {filesUrl_size_03} b",
  название = "",
  тип = "Однострочный нередактируемый текст",
  обязательное = false
) +
  добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "t3",
  название = "",
  тип = "Переключатель",
  описание = "",
  обязательное = false
)
)+
если("{contentIndex} > 3").то(
  добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "t4_txt",
  значение = "{filesUrl_name_04} размер {filesUrl_size_04} b",
  название = "",
  тип = "Однострочный нередактируемый текст",
  обязательное = false
) +
  добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "t4",
  название = "",
  тип = "Переключатель",
  описание = "",
  обязательное = false
)
)+
если("{contentIndex} == 5").то(
  добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "t5_txt",
  значение = "{filesUrl_name_05} размер {filesUrl_size_05} b",
  название = "",
  тип = "Однострочный нередактируемый текст",
  обязательное = false
) +
  добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "t5",
  название = "",
  тип = "Переключатель",
  описание = "",
  обязательное = false
)
)+
// Сценарий предустанавливает адрес получателя из данных пользователя или оставляет поле пустым
если("{useUserEmail} == true").то(
  установитьПеременную("_email", "{userEmail}") 
)+
// Сценарий собирает форму для сбора данных у пользователя - адрес и комментарий к письму
добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "formName",
  название = "",
  значение = "Сценарий отправит электронное письмо с текстом переписки на указанный адрес. Выбранные файлы в переписке будут приложены к письму.",
  тип = "Нередактируемый текст",
  описание = "",
  обязательное = false
) +
добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "email",
  значение = "{_email}",
  название = "Укажите адрес email",
  тип = "Однострочный редактируемый текст",
  обязательное = true
) +
добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "subject",
  значение = "{subject}",
  название = "Укажите тему письма",
  тип = "Однострочный редактируемый текст",
  обязательное = true
) +
добавитьПолеВФормуОператора(
  форма = "Отправка текста переписки на электронную почт",
  переменная = "comment",
  значение = "",
  название = "Комментарий к письму",
  тип = "Многострочный редактируемый текст",
  обязательное = false
) +
//  
// Сценарий отображает форму для опроса оператора
показатьФормуОператору(имя = "Отправка текста переписки на электронную почт") +
//
//собирает выбранные вложения для отправки в письме
комментарий("{filesUrl_01}")+
выполнитьJs("""
var filesUrls = [];
if (t1 == 'true'){
filesUrls.push(filesUrl_01);
}
if (t2 == 'true'){
filesUrls.push(filesUrl_02);
}
if (t3 == 'true'){
filesUrls.push(filesUrl_03);
}
if (t4 == 'true'){
filesUrls.push(filesUrl_04);
}
if (t5 == 'true'){
filesUrls.push(filesUrl_05);
}
var exit = {'filesUrls' : filesUrls};
exit;
""")+
//
// Сценарий получает историю сообщений и формирует запрос на отправку письма
выполнить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 + '<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}") + 
завершить

Работа со спамом

Эти сценарии позволяют оператору установить признак “спам” пользователю, для закрытия всех следующих диалогов пользователя диалогов.

1. Пометить как “СПАМ”

Необходимо добавить сценарий интеграцию на событие “Нажатие кнопки оператором”, добавить кнопку в настройках оператора для выполнения интеграции.

Данный сценарий позволяет оператору установить признак “СПАМ” пользователю

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Code Block
установитьПеременнуюПользователю("userPayload.SPAM", "1")+
комментарий("Пользователь отмечен как СПАМ, можно закрывать диалог. Все последующие обращения будут закрываться автоматически")+
завершить

2. Закрыть “СПАМ”

Необходимо добавить сценарий интеграцию на событие Поступление диалога в систему.

При поступлении диалога будет выполняться проверка наличия признака “СПАМ” у пользователя. При наличии признака диалог будет закрываться.

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Code Block
если("{userPayload.SPAM} == 1").то(
завершитьИЗакрытьДиалог()
)+
завершить

3. Снять признак “СПАМ”

Необходимо добавить сценарий интеграцию на событие “Нажатие кнопки оператором”, добавить кнопку в настройках оператора для выполнения интеграции.

Для снятия признака “СПАМ” оператору необходимо будет инициировать диалог с пользователем, отмеченным как “СПАМ”, и выбрать действие “Снять признак СПАМ”

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Code Block
установитьПеременнуюПользователю("userPayload.SPAM", "")+
комментарий("С пользователя снята пометка СПАМ")+
завершить

Закрытие диалога без оценки

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

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

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Code Block
сообщениеПользователю.сШаблоном("Спасибо за общение, диалог будет закрыт без оценки")+
завершитьИЗакрытьДиалог(отключитьОценку = true)

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

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

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

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Code Block
//
// Сценарий ищет текст в таблице 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}")+
завершить

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

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

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Image AddedImage Added
Code Block
//
// Сценарий ищет строку в таблице 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 Sheets

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Image AddedImage Added
Code Block
//
// Сценарий опроса пользователя и сохранения результатов в таблице Google Sheet 
// 
// Идентификатор таблицы Google Sheet
установитьПеременную("sheetId", "1UyTG1ag6kb-tKCD0Wc5nD21zE38TvYSiMMQ_vzwxWbs")+
// Название листа в таблице Google Sheet
установитьПеременную("sheetName", "Sheet1")+
//
// Опрос пользователя
//
установитьПеременную("sendFio", "{userFullName}")+
если("{sendFio} == ").то(
    задатьПользователюВопрос("Укажите как вас зовут, пожалуйста").сохранитьРезультат("sendFio")
)+
установитьПеременную("sendEmail", "{userEmail}")+
если("{sendEmail} == ").то(
    задатьПользователюВопрос("Укажите адрес вашей электронной почты").сохранитьРезультат("sendEmail")
)+
задатьПользователюВопрос("Пожалуйста назовите ваш отдел").сохранитьРезультат("sendOtdel") +
задатьПользователюВопрос("Пожалуйста назовите вашу должность").сохранитьРезультат("sendDolznost") +
задатьПользователюВопрос("Уточните, пожалуйста, что для Вас является приоритетным?").сВариантамиОтвета("Получение максимальной суммы","Простота получения").сохранитьРезультат("sendPriority")+
задатьПользователюВопрос("Укажите, на какие цели предназначается займ?").сохранитьРезультат("sendGoal")+
//
// Отправка запроса в гугл
// 
// Попытка получения сохраненного ранее токена для запросов в google из переменных сервиса
получитьПеременнуюСервиса("google_token", "g_token")+
// Разбор сохраненного токена
выполнитьJs("""
try {
    var doc = JSON.parse(g_token);
} catch(e) {
    var doc = {'token': '', 'ts': 0};
}

var token = '';
var now_ts =  parseInt(Date.now() / 1000);

if (now_ts > doc.ts) {
    var error = 1;    
} else if (doc.ts == 0) {
    var error = 2;
} else if (doc.token == '') {
    var error = 3;
} else {
    var error = 0;
    var token = doc.token;
}

var exit = {'error':error, 'token':token, 'expired':doc.ts, 'now_ts':now_ts};
exit;
""") +
комментарий("Получил сохраненный токен, статус токена {error}")+ 
// Проверка актуальности сохраненного токена
если("{error} == 0").то(
    // Токен записан и его время жизни не истекло 
    // Сценарий переходит непосредственно к запросу на добавление данных в таблицу
    комментарий("Токен еще валиден, используем его")+
    перейтиНаМетку("google_query")
)+
// Токен или не сохранен или его время жизни истекло
// Сценарий получает новый токен
комментарий("Токен не валиден, получаем новый токен")+
// Сценарий получает из переменных сервиса секретный ключ и адрес аккаунта гугл 
получитьПеременнуюСервиса("google_private_key", "g_private_key") +
получитьПеременнуюСервиса("goggle_account_email", "goggle_account_email") +
// Проверка полученных данных
если("{g_private_key} == ").то(
    комментарий("Нет приватного ключа от аккаунта, не могу получить токен.")+
    завершить
)+
если("{goggle_account_email} == ").то(
    комментарий("Нет адреса аккаунта гугл, не могу получить токен.")+
    завершить
)+
// Сценарий формирует тело запроса на получение токена 
выполнитьJs("""
var header = {
        "alg": "RS256",
        "typ": "JWT"
    };
var data = {
        "iss": goggle_account_email,
        "scope": "https://www.googleapis.com/auth/spreadsheets",
        "aud": "https://oauth2.googleapis.com/token",
        "exp": parseInt(Date.now() / 1000) + 3600,
        "iat": parseInt(Date.now() / 1000)
    };

var sHeader = JSON.stringify(header);
var sPayload = JSON.stringify(data);
var s_g_private_key = g_private_key.replace(/\\n/g,'');
var signature = KJUR.jws.JWS.sign(header.alg, sHeader, sPayload, s_g_private_key);

var exit = {'signature':signature};
exit;
""") +
// Сценарий запрашивает гугла для получения токена
вызвать.внешнийСервис("https://oauth2.googleapis.com/token", "POST").сЗаголовками(("Content-Type", "application/json")).сТеломСообщения("{'grant_type':'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion':'{signature}'}").сохранитьРезультатКакСтроку("result")+
если("{http_code} > 210").то(
    комментарий("Сценарий не смог получить токен для записи в таблицу. Код ошибки {http_code}, ответ {result}")+
    сообщениеПользователю.сШаблоном("Спасибо за участие, но что-то сломалось и я не могу связаться с Google.")+    
    завершить
)+
// Сценарий разбирает ответ от гугл на получения токена
выполнитьJs("""
try {
    var resultParced = JSON.parse(result)
} catch(e) {
    var resultParced = {'access_token':'','expires_in':0};
}
var token = resultParced.access_token;
var expires = resultParced.expires_in;
var g_token = {'token': token, 'ts': parseInt(Date.now() / 1000) + 3599};

var exit = {'token':token, 'expires':expires, 'g_token':JSON.stringify(g_token)};
exit;
""") +
// Сценарий разбирает ответ гугла
если("{token} == ").то(
    комментарий("Сценарий не смог распознать токен в ответе от гугла. Ответ {result}")+
    сообщениеПользователю.сШаблоном("Спасибо за участие, но что-то сломалось и я не могу связаться с Google.")+    
    завершить
)+
// Сценарий сохраняет полученный токен для будущего использования
изменитьПеременнуюСервиса("google_token", "{g_token}")
комментарий("Обновил сохраненный токен для запросов в гугл")+
// 
// Метка для запроса на запись данных в таблицу
установитьМетку("google_query") +
//
// Формирование тела запроса на добавление 1 строки в конец таблицы, количество столбцов не более 25
выполнитьJs("""
    // Дата добавления
    var sendDate = new Date();
    // Список добавляемых значений
    var addValues = [sendDate, sendEmail, sendFio, sendOtdel, sendDolznost, sendPriority, sendGoal];

    var sheetsNames = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','R','S','T','U','V','W','X','Y','Z'];
    var sheetEnd = sheetsNames[addValues.length];
    var requestBody = {"range": sheetName+"!A1:"+sheetEnd+"1", "majorDimension": "ROWS", "values":[addValues,]};

    var exit = {'requestBodyJSON':JSON.stringify(requestBody), 'sheetEnd':sheetEnd};
exit;
""") +
//
// Сценарий отправляет запрос на отправку данных в табличку
вызвать.внешнийСервис("https://sheets.googleapis.com/v4/spreadsheets/{sheetId}/values/{sheetName}!A1:{sheetEnd}1:append","POST").сПараметрами(("valueInputOption","USER_ENTERED")).сЗаголовками(("Authorization", "Bearer {token}"),("Content-Type","application/json")).сТеломСообщения("{requestBodyJSON}").сохранитьРезультатКакСтроку("result")+
// Сценарий разбирает результаты запроса
если("{http_code} > 210").то(
    комментарий("Сценарий не смог отправить запрос на добавление строчки в гугл таблице. Код {http_code}, ответ {result}")+
    сообщениеПользователю.сШаблоном("Спасибо за участие, но что-то сломалось и я не могу связаться с Google.")+    
    завершить
)+
// Запрос на добавление записи завершен успешно
комментарий("Добавил строчку в гугл таблицу.") +
сообщениеПользователю.сШаблоном("Все записал. Большое спасибо за участие!") +
завершить

Интеграционные сценарии

Логировать в комментарии к диалогу запись о выставленном оператором теге в диалоге.

Про добавление тегов в диалог подробный сценарий здесь:

https://deephack.atlassian.net/wiki/spaces/AKB/pages/3207102465#%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D1%82%D0%B5%D0%B3%D0%BE%D0%B2-%D0%BA-%D0%B4%D0%B8%D0%B0%D0%BB%D0%BE%D0%B3%D1%83

https://deephack.atlassian.net/wiki/spaces/AKB/pages/3207102465#%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D1%82%D0%B5%D0%B3%D0%BE%D0%B2-%D0%BA-%D0%B4%D0%B8%D0%B0%D0%BB%D0%BE%D0%B3%D1%83-%D0%B8-%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5-%D0%BF%D1%80%D0%B5%D0%B4%D0%B7%D0%B0%D0%BF%D0%BE%D0%BB%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BF%D0%BE%D0%BB%D0%B5%D0%B9

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

Если оператор не вносил изменение в тег, сценарий запишет email, ФИО оператора и текущий тег диалога.

Сценарий интеграции необходимо добавлять на события “Оператор передал чат другому оператору”, “Оператор вернул диалог в очередь”, “Оператор перевел чат на другую группу“

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Code Block
выполнитьJs("""
var initByOperator = JSON.parse(initByOperator);
var initByOperatorLogin = initByOperator.login;
var exit = {'operatorFIO': initByOperator.fullName, 'operatorEmail': initByOperator.email};
exit;
""")+
комментарий("Оператор с ФИО {operatorFIO} email {operatorEmail} указал тег {tag_name}")+
завершить

где {tag_name} - название переменной тега.

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

Сценарий выводит тему входящего письма почтового канала в комментарий. Аналогично можно выводить “От кого”, “Кому”.

Сценарий интеграции необходимо добавлять на события “Поступление диалога в систему”

Expand
Code Block
//забираем первый комментарий из диалога и забираем из него адрес from
выполнитьJs("""
var msgTypes = ['OperatorComment']; 
var messages = conversation.messages.filter(function (str) {return  msgTypes.indexOf(str.tpe) > -1;});
messages = messages;
var arrayQuestions = [];
for (var i = 0; i < messages.length; i++) {
  var message = messages[i];
  clean_txt = message.txt.replace(/(\r\n|\n|\r)/gm, '');
  arrayQuestions.push(clean_txt);
}
if (arrayQuestions.length == 0) {
  var exit = {'countQuestion':0};
}
else {
  var operatorComment = arrayQuestions[0];
  var operatorComment = operatorComment.replace(/.*?Subject:/,'').replace(/From:.*$/,'');
  var exit = {'operatorComment':operatorComment};
}
exit;
""")+
//выводим в комментарий результат тему письма
комментарий("{operatorComment}")+
завершить

Оповещение операторов в Telegram группе о поступлении диалогов в АФ

Сценарий при поступлении диалога в систему выполняет отправку первой реплики пользователя в заданную в сценарии группу Telegram. Сценарий добавляем в интеграции на событие “Поступление диалога в систему”. Перед добавлением сценария необходимо создать группу в Telegram и получить id группы, например помощью бота https://t.me/myidbot

Expand
Code Block
//указываем токен бота
установитьПеременную("tgToken","123214:34sdfFR8LrSsdf45dsdf")+
//указываем ID чата, можно получить с помощью бота https://t.me/myidbot
установитьПеременную("chatId","-1234567")+
//записываем в переменную первую реплику
установитьПеременную("userAnswer", "{platformInMessageQuery}")+
//выполняем отправку реплики в ТГ группу
вызвать.внешнийСервис("https://api.telegram.org/bot{tgToken}/sendMessage", "POST").сПараметрами(("chat_id","{chatId}"),("text","{userAnswer}")).сЗаголовками(("Content-Type", "application/json")).сохранитьРезультатКакСтроку("answer")+
если("{http_code} > 210").то(
  комментарий("Произошла ошибка при выполнении запроса. Код ошибки: {http_code}. Ответ системы {answer}")+
  завершить
)+
завершить

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

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

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

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Code Block
//количество сообщений для случайного отображения
//
установитьПеременную("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}")
  )+
завершитьИЗакрытьДиалог()

Получить в сценарии дату\время с учетом часового пояса

Expand
titleНажмите здесь, чтобы развернуть пример сценария
Code Block
languagejs
выполнитьJs("""
var currentDate = new Date();
var timezoneOffset = 3;
var timeZoneOffsetInMilliseconds = timezoneOffset * 60 * 60 * 1000;
var adjustedDate = new Date(currentDate.getTime() + timeZoneOffsetInMilliseconds);
var sendtime = adjustedDate.toLocaleTimeString();;
var sendDate = currentDate.toLocaleDateString();
var exit = {'sendDate':sendDate, 'sendtime':sendtime};
exit;
""")+
сообщениеПользователю.сШаблоном("Вот {sendDate} {sendtime}")+
  завершитьИЗакрытьДиалог()

Получить в сценарии информацию об операторе

Expand
Code Block
выполнитьJs("""
var fileError = 0;
try {
  var parsedExtFiles = JSON.parse(initByOperator);
} catch(e) {
  var fileError = 1;
}
var Oname = '';
var Oemail = '';

Oname = parsedExtFiles.fullName;
Oemail = parsedExtFiles.email;

var exit = {'Oname': Oname, 'Oemail': Oemail, 'fileError': fileError};
exit;
""")+
комментарий("Оператор - {Oname} {Oemail}")+
комментарий("Ошибка - {fileError}")+
завершить