Botml is a declarative and powerful markup language for designing modern chatbots (a.k.a. conversational bots).
Anyone (developers and non-developers) can use it to create and teach bots how to behave. Define the behavior of your chatbot using the right tiny bit of formatting and engage the conversation in no time. See for yourself: a calculator bot written in only two lines.
This project uses node and npm. Go check them out if you don't have them locally installed.
$ npm i -g botml
This will install both the botml
node package and the bot
client.
Optionally, if you use Atom as an editor, you may want to install syntax
highlighting with the language-botml
package.
Either run the cli:
$ bot
or use it in your code:
const Botml = require('botml/lib/botml')
const bot = new Botml('alice.bot')
bot.start()
or even load it statically in your browser:
<script src='https://unpkg.com/botml'></script>
<script>
const bot = new Botml()
// either load an URI:
// bot.load('https://raw.githubusercontent.com/codename-co/botml/master/examples/hello.bot')
// or load the script directly:
bot.parse('> *\n< yes?\n')
bot.start()
bot.send('hey')
</script>
Existing features are of two sorts: basic features that cover a basic bot needs, and advanced features that enable richer conversational capabilities.
Basic features:
Advanced features:
- Services integrations (APIs)
- Scripts
- Triggers
- Regular expressions
- Stanford TokensRegex
- Natural Language Processing
The format aims to achieve the most by using the least. With the right and minimal set of conventions, it can be very powerful.
The general syntax follows 3 important conventions:
- The text must be written into blocks of lines separated by at least two line breaks ;
- Each line must start with a one-character symbol that defines its type ;
- A block type is inferred by the symbol of its heading line.
The most basic .bot
file would be:
! BOTML 1
> Hello
< Hello human!
The specification line is needed to tell Botml that it can load the file.
This must be the first line of any .bot
file.
! BOTML 1
The 1
stands for the current version of the format.
Comments can help make your .bot
file clearer.
They can be used as standalone blocks or can be inserted within actionable blocks.
They cannot be used inline.
# COMMENT
Dialogues are the core concept of any bot. It defines how the human and the bot can interact.
A dialogue must start with a >
line, that defines what sentence(s) can
activate the bot to respond.
There must be one or multiple <
lines after that define the bot
response(s).
There can be multiple back and forth by repeating this sequence within the same block.
> MESSAGE
< MESSAGE
Example:
> Hi
< Hello there. Who are you?
> *
< Nice to meet you.
Random replies in dialogues make a bot feel less rigid. When answering to a human, the bot chooses randomly in the reply candidates. Only one of the multiple reply candidates can be chosen by the bot.
> MESSAGE
< REPLY CANDIDATE #1
< REPLY CANDIDATE #2
Example:
> Hello
< Hi there
< Howdy?
Lists are helpful to assemble similar notions, or alternatives.
A list must start with a =
line, that defines the list name.
It must have at least one list item but can have more. Each list item starts
with the -
symbol.
= LIST_NAME
- LIST_ITEM
- LIST_ITEM
A list item can reference yet another list.
= LIST_NAME
- LIST_ITEM
- [ANOTHER_LIST]
It can be referenced in a >
line for referencing multiple variants of
an input pattern.
It can be referenced in a <
line for referencing randomly one of the
list items as a response.
It can be referenced in a ?
line (prompt) for referencing
multiple predefined choices.
Referencing a list is done by wrapping the list name with brackets:
[LIST_NAME]
.
Example:
= citrus
- oranges
- lemons
- grapefruits
= random_fruit
- apples
- apricots
- bananas
- [citrus]
> I like [random_fruit]
< Oh. I prefer [random_fruit].
# which is the equivalent to:
# > I like apples
# > I like apricots
# > I like bananas
# > I like oranges
# > I like lemons
# > I like grapefruits
# < Oh. I prefer apples
# < Oh. I prefer apricots
# < Oh. I prefer bananas
# < Oh. I prefer oranges
# < Oh. I prefer lemons
# < Oh. I prefer grapefruits
#
# > I like [random_fruit]
# < Oh. I prefer [random_fruit].
Lists can also be used in prompts.
Prompts are predefined quick replies in reaction to a specific situation.
They must be placed after a <
line, at the end of a dialogue.
They must reference a list to access all the quick replies.
The number of quick replies should be kept minimal.
= LIST_NAME
- LIST_ITEM
- LIST_ITEM
? [LIST_NAME]
Example:
= pizza_types
- Peperroni
- Margherita
- Hawaiian
> I need a pizza
< What kind of pizza?
? [pizza_types]
Services can leverage external APIs endpoints.
A service must be declared in its own block starting with the @
sign.
It consists of a name and an JSON-formatted API endpoint (over http or https).
It can (and most of the time should) accept a parameter by using the $
sign within its endpoint.
@ SERVICE_NAME ENDPOINT
It can be consumed within a dialogue.
When the ENDPOINT has a $
sign, it must accept a parameter whose value will
replace the $
sign.
The result of the service call can be filtered using an optional OUTPUT.
It's a selector whose format is /(\.\w)+/
.
@ SERVICE_NAME( PARAMETER )[ OUTPUT ]
Example:
@ geo_domain http://freegeoip.net/json/$
> Where is *{domain}
@ geo_domain($domain).city
< It is running from $.
Scripts can be used to evaluate code.
The language of the code is dependent of the language used of the parser used.
The botml
parser is in Javascript, thus Javascript code can be used.
It must be inline within dialogues wrapped in ```.
It can access named variables:
- either from context:
context.variables.get('price')
- or with its named variable shorter form:
$price
Example:
> It will cost you #{price} USD
< `$price / 1000`k USD is a lot!
Variables are the way to detect, format, store and reuse meaningful information.
A variable can be captured within a >
line (dialogue).
It must be either textual ($
), numeric (#
) or alphanumeric (*
).
It can be used in <
lines.
> My name is *{name}
< Nice to meet you, $name
> I am #{age} years old
< Seems that you are $age
The variable format is ${VARIABLE_NAME}
(with its numeric and alphanumeric
equivalents). But for convenient of use, the format $VARIABLE_NAME
can be used
too for textual and numeric variables.
A special $
variable always refers to the last matching value of a dialogue or
the result of the previous line (the result of a service consumption for
instance).
Regular expressions can be used in >
lines to have more control on what to
detect.
A regular expression must be wrapped in /
and cannot be mixed with
basic expressions.
> /^I (?:.+\s)?(\w+) (?:.+\s)?(it|this)/
< Cool bro.
In fact, the XRegExp library is used under the hood, giving you access to leading named captures, inline comments and mode modifiers.
Dialogue workflows are a superset of the classical dialogues.
A workflow can be used to determine a precise flow of conversation.
It must start with a ~
line, that defines the list name.
Only one workflow can start with a <
dialogue. Such a workflow will be
activated and used by default when the user connects to the bot.
# grocery shopping
> I want to buy *{items}
< How many $items?
> #{count} $items
> #{count}
< There you go.
Conditional branches are instructions that direct the bot to another part of the dialogue based on test conditions.
Conditional branches start with ---
and listen for all typed information then test it with all cases. Each case being separated by ---
:
---
> first input case
< first reply
---
> second input case
< second reply
---
> last input case
< last reply
Conditional branches work great with another feature labelled checkpoint.
A checkpoint is a marker ~ CHECKPOINT_NAME
in the workflow, that makes returning to it at a later stage of the current dialog a breeze. It can be referred to with ~ [CHECKPOINT_NAME]
, which redirects the flow to the checkpoint mark:
~ ask_howdy
< Hello stranger.
~ checkpoint_name
< How are you?
> meh
< So you feel bad huh
~ [checkpoint_name]
# Example of workflow working:
# < Hello stranger.
# < How are you?
# > meh
# < So you feel bad huh
# < How are you?
# > ...
Both checkpoints and lists make working with conditional branches something really interesting:
= mood
- good
- meh
- great
- ok
= exceptions
- fantastic
- better than ever
~ ask_howdy
< hello stranger.
~ listen_howdy
< how are you?
? [mood]
---
> meh
< So you feel bad huh
~ [listen_howdy]
---
> good
< Oh, it is not bad ;)
~ [rechecker]
---
> [exceptions]
< Seems you really cool guy!
---
> ok
< Hmm, just ok? Okay then...
---
> great
< Nice! Let's continue then...
~ rechecker
< Maybe it is more than good?
> excellent
< Much better!
It is also possible to use the "not equal" sign !
in conditional branching:
= mood
- [bad_mood]
- [good_mood]
= bad_mood
- meh
- bad
= good_mood
- great
- ok
~ ask_howdy
< hello stranger.
~ listen_howdy
< how are you?
? [mood]
---
> ![mood]
< I asked you how you are ; please let's start over with another answer?
~ [listen_howdy]
---
> [good_mood]
< Oh, it is awesome ;)
---
> [bad_mood]
< Hmm... bye then...
Conditional branching also work well with scripts as long as a value is returned. If a script returns the value true
, the corresponding branch will be activated. If all tests are false
conditional branching will skip all branches and continue with the current workflow:
~ ask_for_email
> start email workflow
~ listen_email
< Your email please?
> *{email}
---
` !/^.+@.+\..+/.test('$email')
< This email $email seems not legit!
~ [listen_email]
---
< Cool. We'll reach you over at $email
Triggers are a way for the bot to be integrated with an other program.
There are two types of events: standard events and custom events.
Standard events are as follow:
'start'
'patterns:set'
with two String parameters: pattern name & value'match'
with two String parameters: label, pattern'current-dialogue-start'
with one String parameter: dialogueLabel'reply'
with one String parameter: message'smart-replies'
with one Array parameter: replies'current-dialogue-end'
with one String parameter: dialogueLabel'variable:set'
with two String parameters: variable name & value'quit'
'*'
catches all the standard and custom events
Custom events can be triggered within dialogues.
A custom event must have a name.
It can have parameters. Parameters can rely on named variables.
@ trigger( 'EVENT_NAME' [, PARAMETER] )
Example:
> hello
< hi
@ trigger('said_hi')
Then handle the 'said_hi' event in your code according to your needs:
bot.on('said_hi', () => console.log('The bot said hi'));
TokensRegex is a framework for defining advanced patterns based of priori Natural Language Processing such as Named Entities and Parts-of-Speech tagging.
> ([ner: PERSON]+) /was|is/ /an?/ []{0,3} /painter|artist/
< An accomplished artist you say.
This feature is enabled through code integration. See an example.
NLP can be enabled through code integration. See an example.
See the examples/
directory.
Start the unit tests with:
npm test
npm run autotest # this is for continuous testing
Use the CLI for trying botml scripts from the console:
node lib/cli.js
node lib/cli.js examples/echo.bot # load script from a local file
debug=true node lib/cli.js # add logging verbosity
In CLI mode, a few special commands can help with debugging too:
- /stats
- /inspect
- /block
- /activators
- /current
Feel free to dive in! Open an issue or submit PRs.
Botml is MIT licensed.