Skip to content

Dialogs

The dialog is the main building block of conversations. All the code that forms the interaction between the bot and the user takes place inside a dialog. Every statement inside a dialog is run in sequence.

dialog greeting do
  say "Hello!"
  ask "What is your name?"
end

Inside a dialog you write statements, like say, ask, et cetera, to create the conversation. See the statement reference for more detail on those. The rest of document documents in which ways dialogs can be triggered.

Named dialogs

Dialogs can have names, the name you give a dialog is an "internal" name, it is only used in the script. By using invoke, you can let the named dialog be executed, from another place.

dialog greeting do
  say "Hello!"
  invoke ask_name
end

dialog ask_name do
  ask "What is your name?"
end

The platform checks the dialog names that you invoke. If we try to invoke a dialog that does not exist, it is considered as a syntax error, and the bot will refuse to run.

Dialog trigger

Basic matching

Dialogs can have triggers which are matched against user input to trigger the dialog.

dialog trigger: "hi" do
  say "hi there!"
end

Above we match sentences which contain the word "hi". The original message that caused the trigger is accessible in the variable named message. The part of the string that was matched is stored in the variable entity.

Dialogs are evaluated in order, meaning the first dialog that matches wins.

dialog trigger: "you look nice" do
  say "thats nice of you"
end

dialog trigger: "nice " do # This dialog will never be executed
  say "thank you"
end

Advanced matching

Besides exact matches you can also match text patterns.

dialog trigger: "mus(ic|eum)" do
  say "you typed either music or museum"
end

The full message that the user typed is availabe in the variable message. The exact part that matched the trigger is available under the variable entity.value.

dialog trigger: "mus(ic|eum)" do
  say "you typed '#{message}'' matching '#{entity.value}'"
end

Note that matches are case insensitive: "museum", "Museum", "MUSEUM" will all match the dialog trigger: "museum". The above example uses string interpolation, which is described in the section on Strings.

Capturing parts of the match

You can also capture parts of the user's input in a dialog by using a notation with pointy brackets, specifying the variable name between the brackets as follows:

dialog trigger: "my name is #<name>" do
  say "Hello #{entity.name}"
end

Match sentences with "my name is" capturing everything after into variable entity.name.

dialog trigger: "stop|quit|end" do

end

Match sentences with the word "stop" or "quit" or "end"

You can also use regular expressions in the capturing group as follows:

dialog trigger: "num:#<n:[0-9]+>" do

end

dialog trigger: "My age is #<age:\d+>" do
  say "so you are #{entity.age} years old!"
end

Match "num:" followed by a number and capture the number into variable n

Intent matching

Next to hard text matching you can also use a more fuzzy matching based on machine learning by defining intents:

@hello intent(learn: ["hi", "hello there", "howdy", "how are you doing"])

dialog trigger: @hello do
  say "Hello to you too!"
  say intent
end

Note: these intents are matched after all hard matches fail. So if you also have a dialog with trigger: "hi" and type "hi" this will match first even though the trained intent also include the example "hi".

Entity extraction

The entity() function allows you to define entities which can be used in ask and in dialog triggers. Some defaults are defined in every BotSquad bot. These entities are defined in the defaults file and match multiple user inputs returning atoms :yes or :no

@yes entity(match: "yes|yep|sure", label: "Yes", return: :yes)
@no entity(match: "no|nope|nah", label: "No", return: :no)

Besides @yes and @no the following entities are also already defined in the defaults file. @postcode and @email use a 'regular expressions' to pattern match the user's response. If it matches the user's message will be accepted if it does not it will loop and if defined the help_dialog will be invoked.

@postcode entity(match: "^\d{5}(?:[-\s]\d{4})?$")

dialog main do
  laugh = ask "What is your postcode?", expecting: @postcode, help_dialog: invalid_postcode
end

dialog invalid_postcode do
  say "Sorry, that doesn't look like a postcode. It should be something like 1000 AA."
end

To learn more about regular expression matching you can use this online tool: regexr

Event trigger

dialog event: "status_update" do
  say "We received an event."
end

Match on an incoming event with the name "status_update". The event can come from the web client, from a webhook, from another bot or conversation, or even from yourself, when it had been scheduled to be sent in the future.

Other dialog triggers

The following dialog triggers are less frequently used.

A location trigger will match any location within a given radius:

dialog location: [lon: 4.893414, lat: 52.364909], radius: 3000 do
  say "Welcome in Amsterdam!"
end

Note that the location has to be sent to the client explicitly, no background location tracking is going on.

Furthermore, an attachment trigger can fire as soon as a user sends a media file to the conversation:

dialog attachment: :image do
  show image attachment.url
  show image attachment.url
end

Receiving other media types

To respond to any other media type the user uploads you can use a special system dialog:

dialog __unknown_attachment__ do
  say "Thanks for the #{attachment.type} file"
end

Asking a location

To ask a user to provide his location you can set :location as expectation.

  location = ask "Please share your location", expecting: :location

The location latitude and longitude will be captured into the variable location. This is a Map with two keys (lat, lon) than can be accessed as follows:

  say "your latitude is #{location.lat}, and longitude is #{location.lon}"

Guard clauses

A powerful way to create variations of dialogs is to define the same dialog multiple times, but add a "guard clause" on it.

Inspired by Elixir's guards, Guard clauses are small expressions which can be added to the dialog definition. These guards decide whether the given dialog is matched or not:

dialog main when user.frontend == "slack" do
  say "Hello slack user!"
end

dialog main do
  say "Hello, other user"
end

There can be multiple dialogs with the same name or trigger, but with different guard clauses. When looking up a dialog, the guard clauses get evaluated and the first dialog with a matching guard clause gets executed.

It is important to remember that dialog resolution is always processed in order, from the top to bottom of the files.

Platform-specific dialogs

The following dialogs are automatically invoked by the platform, based on certain conditions that happen in the lifecycle of the conversation:

__main__

__main__ is the entrypoint for any conversation. For historical purposes, the platform defines a __main__ dialog, which invokes main.

dialog __main__ do
  say "Hello, I am a chatbot."
end

__root__

dialog __root__ do
  say "What would you like to do?", expecting: [@get_quote, @reset_password]
end

The root dialog is invoked the moment the bot's stack is empty. Implement a __root__ dialog to create a "menu"-like interaction model where the bot keeps returning to a menu of options when it is done processing its current dialog.

__unknown__

The unknown dialog is invoked when the user sends a message, but there are no dialogs that get triggered because of that message. This is a typical place to respond with something like "Sorry, I dont Understand, I'm just a chatbot".

dialog __unknown__ do
  say "I don't get what you mean..."
end

__unknown_event__

When an event was sent into the conversation, but there was no event trigger defined for it.

__unknown_location__

When a user sends a location pin to the chat, but there was no location trigger that caught it.

dialog __unknown_location__ do
  hilversum = [5.177459, 52.224025]
  d = round(distance(location, hilversum) / 1000)
  say "You are #{d} km from Hilversum"
end

__unknown_attachment__

When a user sends a image/video/audio file to the chat, but there was no attachment trigger that caught it.

__returning__

This dialog is invoked when the stack is popped.

dialog main do
  ask "Choose a color", expecting: ["Red"]
end

dialog __unknown__ do
  say "That is not right."
end

dialog __returning__ do
  say "Back to the subject."
end

In this case, the user is asked to type "Red"; when he types something else, first __unknown__ is triggered, and then, just before the question is repeated, __returning__ is invoked as well.

__timeout__

Invoked when session timeout is reached. This can be used to clean up things, write information to a database, et cetera.

The standard timeout for a chat session is 15 minutes; it can be changed setting the @timeout constant.

__resume__

Invoked when the conversation is resumed. After the conversation times out, the conversation state is saved. Later, when the user returns to the conversation, the conversation state is looked up again, and just before the conversation resumes where it left off, __resume__ will be invoked.

__ask_timeout__

Invoked when an ask statement has a timeout: value, but when there was no timeout_dialog specified.

To set a global timeout on all ask statements, define the @ask_timeout constant:

# Skips any question after 1 minute
@ask_timeout "1m"

Contextual variables

In the runtime, there are several variables exposed that contain information about the dialog stacking system.

  • dialog.stack - the current stack of dialog names, topmost dialog first. The name of the current dialog is dialog.stack[0].
  • dialog.history - the list of dialog names that the user has passed through, ordered by most recently visited dialog.
  • dialog.last_user_utterances - the list of utterances (last messages) sent by the user; maximum 5; most recent first.
  • dialog.last_bot_utterances - the list of utterances (last messages) sent by the bot; maximum 5; most recent first.