Building an extensible Chat Bot using JavaScript & YAML

Andrea Falzetti

January 03rd, 2017

In this post, I will share with you my experience in designing and coding an extensible chat bot using YAML and JavaScript. I will show you that it's not always required to use AI, ML, or NPL to make a great bot. This won't be a step-by-step guide. My goal is to give you an overview of a project like this and inspire you to build your own.

Getting Started

The concept I will offer you consists of creating a collection of YAML scripts that will represent all the possible conversations that the bot can handle. When a new conversation starts, the YAML code gets converted to a JavaScript object that we can easily manage in Node.js.

I have used WebSockets implemented with socket.io to transmit the messages back and forth.

First, we need to agree on a set of commands that can be used to create the YAML scripts, let’s start with some essentials:

  • messages: To send an array of messages from the bot to the user
  • input: To ask the user for some data
  • next: The action we want to perform next within the script
  • script: The next script that will follow in the conversation with the user
  • when, empty, not-empty: conditional statements

YAML script example

A sample script will look like the following:

 

Link to the gist

Then we need to implement those commands in our bot engine.

The Bot engine

Using the WebSockets I send to the bot the name of the script that I want to play, the bot engine loads it and converts it to a JavaScript object. If the client doesn't know which script to play first, it can call a default script called “hello” that will introduce the bot. In each script, the first action to run will be the one with index 0.

As per the example above, the first thing the bot will do is sending two messages to the user:

 

With the command next we jump to the next block, index = 1. Again we send another message and immediately after an input request.

 

At this point, the client receives the input request and allows the user to type the answer.

Submitted the value, we send the data back to the bot that will append the information to a key-value store, where all data is received from the user live and is accessible via a key (for example,user_name).

Using the when statement, we define conditional branches of the conversation. When dealing with data validation this is often this case. In the example, we want to make sure to get a valid name from the user, so if the value received is empty, we jump back in the script and ask for it again, continuing with the following steponly when the name is valid.

Finally, the bot will send a message, this time containing a variable previously received and stored in the key-value store.

 

In the next script, we will see how to handle multiple choice and buttons, to allow the user to make a decision.

 

Link to the gist

The conversation will start with a message from the bot,followed by an input request, with input type set as buttons_single_select which in the in client it translates to “display multiple buttons” using an array of options received with the input request:

 

When the user clicks on one of the options, the UI sends back the choice of the user to the bot which will eventually match it with one of the existing branches. Found the correct branch the bot engine will look for the next command to run, in this case is another another input request, this time expecting to receive an image from the client.

 

Once the file has been sent, the conversation ends just after the bot sends a last message confirming the file got uploaded successfully.

Using YAML gives you the flexibility to build many different conversations and also allows you to easily implement A/B testing of your conversation.

Implementing the bot engine with JavaScript / Node.js

To build something able to play the YAML scripts above, you need to iterate the script commands until you find an explicit end command. It’s very important to keep in memory the index of current command in progress, so that you can move on as soon as the current task completes. When you meet a new command, you should pass it to a resolver that knows what each command does and is able to run the specific portion of code or function.

Additionally, you will need a function that listens to the input received from the clients, validating and saving it into a key-value store.

Extending the command set

This approach allows you to create a large set of commands, that do different things including queries, Ajax requests, API calls to external services, etc. You can combine your command with a when statement so that a callback or promise can evolve in its specific branch depending on the result you got.

Conclusion

If you are wondering where the demo images come from, they are a screenshot of a React view built with the help of devices.css, a CSS package that provides the flat shape of iPhone, Android, and Windows phones in different colors only using pure CSS. I have built this view to test the bot, using socket.io-client for the WebSockets and React for the UI.

This is not just a proof of concept; I am working on a project where we have currently implemented this logic. I invite you to review, think about it and leave a feedback. Thanks!

About the author

Andrea Falzetti is an enthusiastic full-stack developer based in London. He has been designing and developing web applications for over 5 years. He is currently focused on node.js, react, microservices architecture, serverless, conversational UI, chat bots and machine learning. He is currently working at Activate Media, where his role is to estimate, design and lead the development of web and mobile platforms.