Botsquad documentation logo

dialog

The dialog is the main building block of conversations. All statements should be contained in a dialog.

dialog main do
  say "hello"
  ask "what is your name?"
end

Dialog triggers

Dialogs can have triggers which are matched against user input to trigger the dialog. Dialogs are evaluated in order, meaning the first dialog that matches wins.

dialog trigger: "string" do

end

Match sentences which contain the word “string”. 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.

dialog trigger: "my name is #<name>" do

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”

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

end

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

location:

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

Match any location within a radius of 200 meters from position

attachment:

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

Match any message with attachment of type image

event:

dialog event: "name" do

end

Match on incoming events from client, webhook or other bots labaled “name”

special dialog names

dialog __unknown_location__	do

end

invoked when no other location trigger matches

dialog __unknown__ do

end

invoked when no other dialog matches

dialog __returning__ do

end

invoked when returning

dialog __timeout__ do

end

Invoked when session timeout is reached. The standard session timeout is 15 minutes; it can be changed setting the @timeout constant.

dialog __timeout__ do

end

Invoked when ask_timeout is reached. The standard session timeout is infitity (never); it can be changed setting the @ask_timeout constant.

Dialog guard clauses

Dialogs can have “guard clauses”; 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 true guard clause gets executed. Dialogs are processed in order, from top to bottom of the files.

Constants

Variables starting with an @ are treated as constant values. They can only be defined outside dialog.

@constant "key"
@version 12
@message "Hello and welcome!"

dialog main do
  say @message
  say "My version is: " + @version
end

Special constants

The following constants have special meaning, when they are defined.

Example:

@ask_timeout      :infinity
@timeout          "15m"
@delay            1
@typing_indicator 1

Loading constants from a file

When adding a YAML or a JSON file in the Botsqud studio, the contents of that file is exposed in a constant which is named after the JSON/YAML script file name.

Data files are a convenient way to keep data outside the bot script contents, but still embedded as part of the bot.

For instance, when you create a YAML file called people with the following contents:

- name: Arjan
  age: 38
- name: Pete
  age: 22
- name: Julie
  age: 32

The contents of the people file is now accessible in your bot script under the constant @people. It can be used like this:

repeat person in @people do
  say "Name: " + person.name
  say "age: " + person.age
end

Read more about data files and content management

Statements reference

invoke

Invoke is used to switch to a different dialog. It can be compared with the ‘go to’ statement, with the exception that it uses a stack, so after the called dialog is finished, the execution will return on the place after the invoke statement.

invoke mydialog

A “vanilla” invoke adds (pushes) the dialog on top of the current dialog stack, returning there after finishing.

Note that you can not use expressions in the name of the dialog, they always need to point to an existing dialog in one of the bot’s scripts. The studio will show an error message when you have an invoke which points to a non-existing dialog.

:replace

invoke mydialog, :replace

Giving the :replace option to invoke will cause it not to return to the calling dialog, it will replace the current dialog in the stack.

:reset

invoke mydialog, :reset

Giving the :reset option to invoke will cause the bot to stop after finishing the called dialog, it clears the dialog stack entirely.

invoke dialog with a message trigger

dialog main do
  invoke message: "my name is Pete"
end

dialog trigger: "pete" do
  say "Hi Pete!"
end

This will cause the entered message (which can be an expression) to be handled by the global dialog message matching system, as if a user typed it. So the above main dialog will output “Hi Pete!” when executed.

stop

Stops all dialog interaction, clearing the stack. Subsequent actions in the same dialog will not be executed.

say "Hello"
stop
say "This line is never reached"

say

Sends the given “string” to client.

say "string"

URLs in text are automatically linked, and newline characters (\n) can be used to create a line break:

  say "Click this link:\n https://nu.nl"

You can use limited markdown formatting in messages as well, for doing bold, italic, et cetera. The following tags are supported:

say "** bold text **"
say "* italic *"
say "`monospace text`"

class:

Use the class: option to specify a specific presentation for the message. This currently only works in the web client.

For example, this sets the class “large” on the message:

say "string", class: "large"

The predefined classes in Web client are:

as:

To let the bot impersonate another user, it is possible to specify the as: option, with a given user object.

The object needs to have at least an user_id and a first_name attribute. profile_picture can be used to specify the avatar.

person = [user_id: "arjan", first_name: "Arjan", profile_picture: "http:///..."]
say "This is yourself", as: person

ask

ask presents a text message to the user, and then blocks the input, to let the user type a response. When no variable name is given, the response of the user is stored in the variable named answer.

ask "how old are you?"
say answer

When used in an assignment, it is possible to store the response in another variable, like in the next example:

age = ask "how old are you?"
say age

expecting:

The expecting: option will repeat the question until the message is matched against the value(s) in the expecting option.

This can be used for validation purposes, when you need the user to type something.

# Builtin replies "Yes", "yep", "ok", et cetera
ask "Did you laugh?", expecting: [@yes, @no]

# E-mail regular expression
ask "What is your email?", expecting: :email

# File upload
ask "Which file do you want to upload?", expecting: [:file]

# Fixed list of answers, presents them as quick replies
ask "Choose your beverage", expecting: ["Pepsi", "Sprite"]

Builtin types for expecting are:

help_dialog:

The help dialog is invoked when a user enters something that does not match the expectation:

dialog asking do
  ask "Did you laugh?", expecting: [@yes, @no], help_dialog: help
end

dialog help do
  say "Please answer yes or no."
end

quick_replies:

Quick replies give options for the user to choose from. However, these options are mere hints, they are not used for validation.

ask "Did you laugh?", quick_replies: ["Yes", "No"]

ask "What's your email?", quick_replies: ["no thanks"], expecting: [@email, @no]	# The `quick_replies:` are shown but `expecting:` is matched

ask "Please choose your option", quick_replies: [
  [title: "option 1", image_url: "http://via.placeholder.com/40x40"],
  [title: "option 2", image_url: "http://via.placeholder.com/40x40"] ]

It is possible to combine expecting: and quick_replies:; in that case, the given quick_replies are used but the validation is done on the expecting clause.

ask_timeout:

To prevent a script from “hanging”, you can let the ask break out by giving a timeout value. The value wil be set to the given timeout_value, or nil if none was given.

ask "How are you?", timeout: 4, timeout_value: :yes, expecting: [@yes, @no]

if answer == :yes do
  ask "..."
end

It is also possible to trigger a dialog on a timeout. E.g. for asking the user to stop after being idle.

ask "How are you?", timeout: 4, timeout_dialog: ask_for_stop

ask "How are you?", timeout: 4

dialog __ask_timeout__ do
  say "Hey, wake up!"
  ask "Do you want to continue?"
end

Note, that when using ask from within a __ask_timeout__ dialog (without a timeout value), it will not trigger a new ask timeout dialog, but that ask will block infinitely.

To set a gloal timeout on all asks, use the @ask_timeout constant.

await

The await statement works similar to ask, except that no prompt or quick replies are shown while awaiting.

The first argument to await is the argument you would give to expecting: in an ask.

For example, the following waits for a valid email address and stores the value in answer.

await @email

The following blocks until an event with the name “click” has been fired:

await event("click")

emit

Emits an event to the client, or to another chat process. An event consists of a named message (a string), plus an optional payload.

emit "status_update", text: "#{user.first_name} started using the bot"

By default, emit sends it event to the client, where it can be picked up by for instance the Javascript SDK (Botsqd.onEmit("event", ....)), but it can also emit to other processes, for instance the master process or a local group @coordinator.

emit "status_update", [], to: :master

Sends a message to the bot’s master process. In that process it will be handled using the dialog event: "..." trigger.

to:

These are the emit to: targets:

emit is “fire and forget” – No warnings / errors are generated when sending of the message fails. If you need to check whether the message was arrived, use query.

query

Sends an event to the given user, and waits for the reply.

reply = query :master, "ping"
say reply

In the other script, define a task or dialog that catches the ping event, and use the reply construct to send a payload back:

task event: "ping" do
  reply "pong"
end

Make sure that the handling task / dialog in the other process finishes quickly. If there is no reply received within 1 second, the value :timeout is returned in the calling process.

scheduling

Send a status_update event to the client, in 10 seconds from now, and 1 year from now

emit :status_update, [], in: 10
emit :status_update, [], in: "1y"

Specifying an absolute point in time is also possible:

emit :status_update, [], at: "tomorrow at 10:00"

This will use the duckling date parser to parse the time. If date parsing fails, a runtime error is thrown.

Note: It is not possible to schedule events in the past.

named emit

While scheduling you can give the emitted event a name that you can use to cancel it again. You can also use it to reschedule the event, because the name is unique; scheduling a second emit with the same name will cancel out the first (if it was not yet fired).

emit :status_update, [], in: 10, name: "update"
emit :status_update, [], in: 20, name: "update"

This will only send out one emit, in 20 seconds.

cancelling emits

A scheduled emit sets the variable with the name schedule_id in the bot script. You can use this variable to cancel it.

emit :status_update, [], in: 10, name: "update"
result = cancel_emit(schedule_id)

pause

pause 3			# pause the dialog for 3 seconds

type

type 3			# pause for 3 seconds, while showing typing indicator

show

show is a generic way of showing media to the user.

show image "http://via.placeholder.com/150x150"

Note: The statements image, location, video and audio are actually macro’s which use show to display the content; these will be deprecated in time.

image

show image "http://via.placeholder.com/150x150"

class:

show image "http://via.placeholder.com/150x150", class: "rounded"

location

show location [lat: 52.364909, lon: 4.893414]

random

say random 10

returns a random number (int) between 0 and 10

say random [1,2,3,4]

returns a random element from the list

random do
  say "Hi!"
  say "Hello!"
  say "Howdy!"
end

Randomly executes one of the statement in the block.

remember / forget

remember is the way to persist information for a user over the bot’s session. All information stored by remember is saved with the bot’s user and can be viewed in the studio. In a next session, these variables will be set again when new session starts. This way, you only have to ask a user his name once, for example.

    variable = 123
	remember variable
	forget variable

NOTE: variables can be deep ‘object maps’:

car.brand = "Volvo"
car.engine.hp = 200

remember car

This remembers the object car and its nested values.

tag / untag

Tags or untags the current chat session. Can be used later for filtering and matching users.

tag "lead"
untag "lead"

Tags are availabe in the user object: user.tags contains an array of all the users’s collected tags over all sessions.

log

Basic message logging for debugging purposes.

log "This is a log message"

Log messages can contain any valid expression and are logged in the output tab in the Botsquad studio.

if

if exits != "Yes" do
  say "Lets continue"
end

else

if exits != "Yes" do
  say "Lets continue"
else
  say "Ok lets quit"
end

task

A task is a construct which is comparable to a dialog. However, tasks are meant for non-interactive computations, therefore, no statements are allowed that perform user-level side effects, like say, ask, show.

Typically, tasks are executed when an event comes in:

task event: "ping" do
  counter = counter + 1
end

The advantage of using tasks over dialogs here is that tasks execute outside of the current dialog context. Therefore, the current dialog continues undisturbed, without any other effects (like __returning__).

Named tasks

Tasks can also have a name, just like dialogs.

task calculate do
  counter = counter + 1
  if counter > 10 do
    emit "full"
  end
end

perform

Analogous to invoke, a task can be executed by using the perform statement:

perform calculate

Tasks do not have arguments. As the DSL has a global scope, any parameters that are required inside the task should be defined just before the perform statement.

branch

ask "Do you want to continue?", expecting: ["Yes", "No", "Maybe"]
branch do
  answer == "No" ->
    say "Sorry to hear"
  answer == "Yes" ->
    say "Great!"
  answer == "Maybe" ->
    say "Make up your mind!"
end

buttons

buttons "What do you want to do next?" do
  "Open CNN" ->
    url "http://cnn.com"
  "Say hello" ->
    postback "say_hello"
end

NOTE: though buttons are similar to quick_replies they invoke dialogs via a postback event: rather than trigger: and are not displayed as user message in the conversation.

template

  template do
  "The hat shop 1" ->
    image_url "http://via.placeholder.com/150x150"
    subtitle "We've got the right hat for everyone."
    default_action do
      open "http://www.redhat.com"
    end

    buttons do
    "View Website" ->
      open "http://www.redhat.com"
    "Say hello" ->
      postback "postback"
    end

  "The hat shop 2" ->
    image_url "http://via.placeholder.com/150x150"
    subtitle "We've got the better hat for everyone."
    default_action do
      open "http://www.redhat.com"
    end

    buttons do
    "View Website" ->
      open "http://www.redhat.com"
    "Say hello" ->
      postback "postback"
    end

  # ...
  end

= (assignment)

variable = ask "whats your name?"
variable = "string"
variable = 1
variable = [1,2,3]
variable = first [1,2,3]

NOTE: variables are global and mutable.

Operators reference

addition (+)

The + operator has superpowers.

substraction (-)

The - operator is superpowered as well.

In all other cases, it raises an error, for instance when you try to substract a number from a string.

boolean

a && b    # AND
a || b    # OR
a !  b    # NOT
a == b    # IS
a != b    # IS NOT
a <= b    # IS SMALLER OR EQUAL TO
a >= b    # IS LARGER OR EQUAL TO
a <  b    # IS SMALLER
a >  b 		# IS LARGER