Структура сценария
Теперь разберем ся со структурой сценария. Создадим интенты для осуществления переходов по стейтам, а также применим функции, которые мы написали ранее.
Start
В стейте Start запускается сценарий. Под тегом script обнуляем все сессионные переменные. Затем посылаем приветственное сообщение и предлагаем выбрать, кто начнет игру. Содержит три вложенных стейта: Computer, User и LocalCatchAll.
state: Start || modal = true
q!: $regex</start>
intent!: /LetsPlay
script:
$session = {}
$client = {}
$temp = {}
$response = {}
a: Привет! Предлагаю сыграть в игру "Города". Кто загадывает город: компьютер или пользователь?
state: User
intent: /user
a: Назовите город
script:
$session.keys = Object.keys($Cities);
$session.prevBotCity = 0;
go!: /LetsPlayCitiesGame
state: Computer
intent: /computer
script:
$session.keys = Object.keys($Cities);
var city = $Cities[chooseRandCityKey($session.keys)].value.name
$reactions.answer(city)
$session.prevBotCity = city
go!: /LetsPlayCitiesGame
state: LocalCatchAll
event: noMatch
a: Это не похоже на ответ. Попробуйте еще раз.
Предусмотрим переход в стейт Правила из любого другого стейта по фразе давай поиграем. Для этого создадим интент /LetsPlay.
Перейдите на вкладку NLU > Интенты, расположенную в боковом меню. Создайте интент /LetsPlay и добавьте в поле Тренировочные фразы фразы: хочу играть, играть, давай поиграем, давай играть.

Установим стейту Start флаг modal = true. Он используется, когда от клиента нужно получить важную информацию, без которой диалог не может продолжаться. В нашем случае игра не может быть продолжена, пока пользователь не выберет, кто начнет игру первым.
User
Переход в стейт User осуществляется по интенту /user. Затем выводится сообщение Назовите город.
state: User
intent: /user
a: Назовите город
script:
$session.keys = Object.keys($Cities);
$session.prevBotCity = 0;
go!: /LetsPlayCitiesGame
Создайте интент /user и добавьте в поле Тренировочные фразы фразы человек, пользователь, я, не ты.

В теге script присваиваем переменной $session.keys массив из id городов, находящихся в списке city/cities-ru.csv.
Обнуляем переменную $session.prevBotCity, в которой будем в дальнейшем хранить город, названный ботом. Затем с помощью тега go! осуществляем переход в следующий стейт LetsPlayCitiesGame.
Computer
Переход в стейт Computer осуществляется по интенту /computer.
state: Computer
intent: /computer
script:
$session.keys = Object.keys($Cities);
var city = $Cities[chooseRandCityKey($session.keys)].value.name
$reactions.answer(city)
$session.prevBotCity = city
go!: /LetsPlayCitiesGame
Создайте интент /computer и добавьте в поле Тренировочные фразы фразы компьютер, комп, бездушный кусок железа, ты, не я.

В теге script присваиваем переменной $session.keys массив из id городов, находящихся в списке city/cities-ru.csv.
В переменную city записываем случайно выбранный город с помощью функции chooseRandCityKey(). Воспользуемся встроенной функцией $reactions.answer(), чтобы вывести город из city в качестве ответа бота.
Записываем в переменную $session.prevBotCity названный ботом город. Затем переходим в стейт LetsPlayCitiesGame.
LocalCatchAll
Если в стейте установлен флаг modal = true, то система обработает запрос в контексте этого стейта. Следовательно, запрос может попасть только в один из его вложенных стейтов.
Если пользователь введет сообщение, не предусмотренное интентами /computer и /user, то система выдаст ошибку. Лог ошибки сообщит, что в сценарии не найден стейт для перехода. Поэтому создадим локальный стейт LocalCatchAll, который будет срабатывать на сообщения, не предусмотренные установленными стейтами.
state: LocalCatchAll
event: noMatch
a: Это не похоже на ответ. Попробуйте еще раз.
LetsPlayCitiesGame
Стейт LetsPlayCitiesGame содержит вложенные стейты CityPattern и NoMatch.
state: LetsPlayCitiesGame
state: CityPattern
q: * $City *
script:
if (isAFullNameOfCity()) {
if (checkLetter($parseTree._City.name, $session.prevBotCity) == true
|| $session.prevBotCity == 0) {
var removeCity = findByName($parseTree._City.name, $session.keys, $Cities)
if (checkCity($parseTree, $session.keys, $Cities) == true) {
$session.keys.splice(removeCity, 1)
var key = responseCity($parseTree, $session.keys, $Cities)
if (key == 0) {
$reactions.answer("Я сдаюсь")
} else {
$reactions.answer($Cities[key].value.name)
$session.prevBotCity = $Cities[key].value.name
removeCity = findByName($Cities[key].value.name, $session.keys, $Cities)
$session.keys.splice(removeCity, 1)
}
} else $reactions.answer("Этот город уже был назван")
}
} else $reactions.answer("Используйте только полные названия городов")
state: NoMatch
event: noMatch
a: Я не знаю такого города. Попробуйте ввести другой город
CityPattern
Переход в стейт CityPattern выполняется по паттерну * $City *, указанному в теге q.
В теге script осуществляется логика игры:
- В первом условии проверяем, что пользователь ввел полное название города. Если условие не было выполнено, то выводим сообщение:
Используйте только полные названия городов
- Если первое условие было выполнено, то проверяем совпадает ли первая буква введенного пользователем города с последней буквой введенного ботом слова или же переменная
$session.prevBotCityравна0. - Если условие выполняется, то сохраняем введенный город в переменную
removeCityи переходим к следующей проверке. - Проверяем не был ли назван город ранее. Если уже был, то выводим сообщение:
Этот город уже был назван
Иначе, удаляем из общего списка введенный город. В переменную key записываем результат вызова функции responseCity().
- Если переменная
keyравна0, значит города на последнюю и предпоследнюю букву закончились. Бот выведет сообщениеЯ сдаюсь. - Иначе выводим загаданный ботом город и удаляем его из списка всех городов.
NoMatch
Если пользователь допустит ошибку при вводе города или назовет несуществующий город, то сработает событие noMatch. Бот выведет сообщение:
Я не знаю такого города. Попробуйте ввести другой город
EndGame
Стейт EndGame обрабатывает конец игры, когда пользователь не хочет больше продолжать игру.
state: EndGame
intent!: /endThisGame
a: Очень жаль! Если передумаешь — скажи "давай поиграем"
Создайте глобальный интент /endThisGame, по которому будет осуществлен переход в стейт EndGame из любого другого стейта. Добавьте в поле Тренировочные фразы фразы стоп, надоело, сдаюсь, я устал, хватит.

Теперь перейдем к тестированию разработанного сценария.