Script structure
At the previous step, we’ve created a chatbot.yaml
configuration file and a main.sc
script for the bot.
Now, let’s work with the structure of the script. We will create intents for navigating states, and will also use the functions that we’ve created previously.
Start
The script starts from the Start
state. The bot sends a welcome message and asks the user to choose who will start the game first.
state: Start || modal = true
q!: $regex</start>
intent!: /LetsPlay
script:
$session = {}
$client = {}
$temp = {}
$response = {}
a: Let's play "Cities" Game. Who will start first: bot or user?
state: User
intent: /user
a: Name the city
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: I don't understand you. Please try again.
Under the script
tag, initialize all session variables to zero. We use them to save user’s and bot’s replies.
Let’s create a Letsplay
intent, so we can get to the Rules
state from any other state when the user sends a message Let's play
.
To view the intents settings, go to the NLU > Intents tab on the control panel. Create an intent and specify phrases in the Training phrases field: let's play
, play
, I want to play
.
Let’s set modal
flag to true
in the Start
state. It is used when you need the user to provide some information for the game to go on. In our case, the game cannot go on until the user chooses who starts the game first.
The state includes three nested states: Computer
, User
and LocalCatchAll
.
User
The transition to the User
state is triggered by the /user
intent. Then the message Name the city
is displayed.
state: User
intent: /user
a: Name the city
script:
$session.keys = Object.keys($Cities);
$session.prevBotCity = 0;
go!: /LetsPlayCitiesGame
Create a /user
intent and specify phrases in the Training phrases field: user
, me
, not you
, I
.
In the script
tag, assign an array of id
from the city/cities-en.csv
list to the $session.keys
variable.
$session.keys = Object.keys($Cities);
Clear the $session.prevBotCity
variable in which we store the city named by the bot. Then move to LetsPlayCitiesGame
state using go!
tag.
Computer
The bot moves to the Computer
state when the /computer
intent is triggered.
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
Create a /computer
intent and specify phrases in the Training phrases field: computer
, you
, not me
.
In the script
tag, assign an array of id
from the city/cities-en.csv
list to the $session.keys
variable.
Write a randomly selected city into the city
variable using the chooseRandCityKey()
function. Let’s use the built-in function $reactions.answer()
to display the city from city
as the bot’s response.
Save the city named by the bot to the $session.prevBotCity
variable. Then move to LetsPlayCitiesGame
state.
LocalCatchAll
If modal=true
, the system will process the request in the context of that state, i.e. the request can only be switched to one of its nested states.
If the nested states have no matching answer and the local LocalCatchAll
state is missing, the system returns an error. The error log tells you that no state to switch to was found in the script.
So, let’s create a LocalCatchAll
state. The bot switches to this state if it receives a message that differs from all the considered options in the states.
state: LocalCatchAll
event: noMatch
a: I don't understand you. Please try again.
LetsPlayCitiesGame
The LetsPlayCitiesGame
state consists of two nested states: CityPattern
and 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("I give up")
} 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("You can't name the same city twice. Try again.")
}
} else $reactions.answer("Please use only full cities name")
state: noMatch
event: noMatch
a: I don't know this city. Try again.
CityPattern
The transition to the CityPattern
state is based on the * $City *
pattern specified in the q
tag.
The script
tag implements the game logic:
- In the first condition, we check that the user has entered the full name of the city. If
false
, the following message is displayed:
Please use only full cities name
- If
true
, we check whether the first letter of the city entered by the user matches the last letter of the city entered by the bot, or the$session.prevBotCity
variable equals to0
. - If
true
, save the entered city in theremoveCity
. - Then we check whether the city has been named earlier. If so, the following message is displayed:
You can't name the same city twice. Try again.
- Otherwise, remove the entered city from the list. Assign the result of
responseCity()
function to thekey
variable. - If the
key
variable equals to0
, then the bot has run out of city names starting with the last or the second to last letter. The bot displays the messageI give up
. - Otherwise, we display the city generated by the bot and remove it from the city list.
NoMatch
If the user makes a mistake when entering a city or names a non-existent city, the noMatch
event is triggered. The bot displays a message:
I don't know this city. Try again.
EndGame
The EndGame
state is used to process the end of the game in case the user chooses to finish the game.
state: EndGame
intent!: /endThisGame
a: That's a pity! If you change your mind, just text me "Let's play"
Create an /endThisGame
intent that will process the transition to the EndGame
state from any other state. There can be two cases when the users want to finish the game: they don’t want to play anymore or they give up to continue the city names chain. So, specify phrases in the Training phrases field: stop
, give up
, tired
, finish
.
Next, move on to testing the script.