Code Lab: Build a (Superior) Bot

Using Watson Conversation, Botmaster, and external APIs.

Amal Amine
AstroLabs
Published in
9 min readMay 31, 2017

--

Meet Mr. Sunshine: a chatbot that can tell you sunset and sunrise times for any location and any date. In this code lab we’ll be building a bot using Watson Conversation, Botmaster, and multiple 3rd party APIs.

Your future bot is available to test our here, and on Facebook Messenger.

You’ll Need:

An account on Bluemix
The Bluemix CLI
GIT
Node.js
Any IDE (we recommend Atom)

Setting Up Your App

We’ll build over a Botmaster integration for Watson Conversation to bring our bot to life. Head-over to this Github repository and clone it to your local machine. Then switch to the guide branch.

Working with Conversation

Head over to Bluemix and create an instance of the Conversation service.

Launch the Watson Conversation tool and import the workspace included in the repository you cloned.

Understanding Language

Let’s take a look at the conversation workspace. You’ll notice three main tabs: intents, entities, and dialog.

Intents

To process language you need to define intents and entities. Intents are the possible meaning of what the user says, and entities are the parameters in the user’s text. For example, when our bot is asked “When does the sun go up today in Dubai” it will process the intent as “sunrise_time” and the entities to be a location: Dubai, and a date: today.

You define an intent by providing a number of examples which have the same meaning (i.e. the same intent).

Our bot has intents to understand greetings, goodbyes, insults, compliments, and of course, when it’s asked about sunset or sunrise times.

Entities

Now of course, you need your bot to detect the specific place the user is asking their “sunrise_time” question about. This is where entities come in.

You define an entity by providing a number of values that correspond with it. Each of these values can also have synonyms. So by defining a “location” entity with a number of places and their synonyms, your bot can pinpoint a location when you mention one.

Watson Conversation has a number of pre-defined system entities to detect commonly used keywords. Our bot has the system entities sys-location and sys-date enabled.

We’ve defined intents and entities to help our bot understand language, but how does it respond?

Engaging in Dialog

This is where the dialog builder comes in. It enables you to create structured conversations which provide responses to particular intents and entities.

So, for example, to provide a response to “When is sunset today?” you can create a node in the dialog which triggers when it detects the “sunset” intent.

To answer any sunset question, our bot needs a location and a day, so we create response conditions to check if we’ve detected any entities.

When your bot receives user input, it will determine the intent and entities, and then look through the dialog for a node that matches. The nodes are checked one by one starting from the top.

If our bot understood the user’s request and has all it needs to fetch the answer, it can make a request to an external API. Otherwise, it needs to prompt the user for the missing information.

As we add intents and nodes to our conversation flow, we need to store important information.

Context Variables

To do so, we set context using the advanced response editor (in the menu at the top-right of each node’s responses.)

Then before answering the user’s question, we can check if we have all we need. Notice the trigger condition in the node.

Scaling it Up

Whenever our bot understands the request and has enough information, it responds with “Let me check that for you.” We’ll use that as a flag to make a call to an external API from within our application.

Let’s take a look at how our app is built. Open the project folder with your IDE.

The application uses our conversation service credentials and workspace ID to send the user’s input and retrieve our bot’s output, comprehended intents, entities and context variables.

You can manipulate the context before and after it’s sent to the conversation service from within context.js

You can find 2 arrays in context.js :
varsToUpdateBeforeWatson
varsToUpdateAfterWatson

Both work the same way, but obviously, one is used before Watson Conversation gets the message (user’s input), and one occurs after it answered (bot’s output).

You can see that when we detect a location, we retrieve geocode information from the Google Maps API to get the latitude, longitude, and timezone values fit. The returned values are stored as new context variables in varsToUpdateAfterWatson.

We need latitude and longitude to be able to retrieve sunset and sunrise times, and timezone to be able to convert times to the location’s local time instead of UTC times.

Let’s update the sunset and sunrise values.

First, we need a source to get our information from. The sunrise-sunset API takes latitude, longitude, and date values and returns sunset and sunrise times. An example request to get sunset timing for Dubai on 01/June/2017 would be:

https://api.sunrise-sunset.org/json?lat=25.2048493&lng=55.2707828&date=2017-06-01

Define the static part your endpoint at the beginning of context.js:

var sunEndpointHeader = "https://api.sunrise-sunset.org/json?lat="

Next, examine sunset in varsToUpdateAfterWatson. Function takes the message and context and returns a value. We’ll set it to return the sunset time.

We need a condition to tell our bot to fetch the data. Since it responds with “Let me check that for you.” whenever it has enough information, we can set it as our flag.

if ((answerText[0].indexOf("Let me check")!=-1))

Next, we need to initiate an http request using the endpoint we defined earlier:

sunset: {
value: false,
forceIfUndefined: true,
function: function(answerText, context, key) {
if ((answerText[0].indexOf("Let me check")!=-1)) {
console.log("API call ahead")
var http = new XMLHttpRequest();
http.open('GET', sunEndpointHeader + lat + "&lng=" + lng + "&date=" + context["date"], false);
http.send(null);
if (http.status === 200) {
var resp = JSON.parse(http.responseText)
sunsetTime = resp.results.sunset
sunriseTime = resp.results.sunrise
sunsetTime = convertTime(sunsetTime);
console.log("Got actual sunset time: " + sunsetTime)
}
} else if (context[key] !== sunsetTime) {
sunsetTime = context[key];
}
return sunsetTime;
}
}

Now that we have sunset time, let’s update the sunrise context variable as well. Since we already retrieved it in the previous step we don’t need to make an API call again.

sunrise: {
value: false, //Set a particular value on every call, set to false to ignore this field.
forceIfUndefined: true, //If the context variable does not exist yet, tells if it should be created
function: function(answerText, context, key) { //Different sets of actions depending on the answerText, can access the whole context, must not update context. @return the new context[key] value.
var returnValue = 0;
if ((answerText[0].indexOf("Let me check")!=-1)) {
sunriseTime = convertTime(sunriseTime)
console.log("Got actual sunrise time: " + sunriseTime)
} else if (context[key] !== sunriseTime) {
sunriseTime = context[key];
}
return sunriseTime;
}
}
}

Everytime our bot responds, the app will check if the response is “Let me check that for you.” and update the latitude, longitude, timezone, sunset, and sunrise context variables accordingly.

Finally, we need to send the answers back to the user. We can do that in setContextAfterWatson, after the context variables are updated.

For sunset:

if ((watsonUpdate.output.text[0].indexOf("Let me check")!=-1) && watsonUpdate.context["param"]=="sunset"){
watsonUpdate.output.text[watsonUpdate.output.text.length+1] = "The sunset time in " + watsonUpdate.context["location"] + " on " + watsonUpdate.context["date"] + " is " + watsonUpdate.context["sunset"] + ", " + watsonUpdate.context["timezone"] + " time."
}

And for sunrise:

if (watsonUpdate.output.text[0].indexOf("Let me check")!=-1 && watsonUpdate.context["param"]=="sunrise"){
watsonUpdate.output.text[watsonUpdate.output.text.length+1] = "The sunrise time in " + watsonUpdate.context["location"] + " on " + watsonUpdate.context["date"] + " is " + watsonUpdate.context["sunrise"] + ", " + watsonUpdate.context["timezone"] + " time."
}

Now our bot should be able to answer any requests for sunset and sunrise timings.

Deploying Your Bot

We’re almost ready to deploy our bot to Bluemix and connect it to Facebook Messenger.

Go back to the Conversation service instance you’ve deployed and get your credentials.

We’ll modify the environment variables to match our credentials. Create a .env file by copying the given .env.example file provided.

Fill in your Conversation credentials and workspace ID. Enter a phrase of your choice for MESSENGER_VERIFY_TOKEN, and /webhook for MESSENGER_WEBHOOK.

Deploy your app to Bluemix using the Bluemix CLI. Login with your username and password and choose your region and space.

Push your app to Bluemix.

Your app is now up and running on Bluemix, visit the app url to test it.

Connecting to Facebook

You’re now ready to connect your bot to Facebook Messenger.

Go to https://www.facebook.com/pages/create/, and create a new page.
Go to https://developers.facebook.com/, login and create a new App for Messenger.

Generate a token for your newly created page, and copy it.

Select “Setup Webhooks” and create a new page subscription.

Your callback URL will be the URL of the app you just deployed to bluemix, with /messenger/webhook.

The verify token is the same one you entered in your .env.

Under Subscription fields, choose messages, message_deliveries, messaging_postbacks, and messaging_optins.

Select the page you just created to subscribe your webhook to.

Go back to your Facebook app’s dashboard and copy your app secret.

Finally, go back to your .env and fill in your app secret and page token, and push your app again to Bluemix.

Your bot is now live on Facebook Messenger and through web!

So, What’s Next?

You’re now part of a platform used by over a million developers making over a million developers making over a billion API calls a day, starting over a hundred thousand apps a month, and welcoming over 2000 new users everyday.

Get Involved

We’re building a community of Bluemix developers around Middle East & Africa. Stay tuned for our events and activities in the region through Twitter.

--

--