Today, we are going to build a chatbot that can search for restaurants based on user goals and preferences. Let us begin by building Node.js modules to get data from Zomato based on user preferences. Create a file called zomato.js. Add a request module to the Node.js libraries using the following command in the console:
This tutorial has been taken from Hands-On Chatbots and Conversational UI Development.
> npm install request --save
In zomato.js, add the following code to begin with:
var request = require('request');
var baseURL = 'https://developers.zomato.com/api/v2.1/';
var apiKey = 'YOUR_API_KEY';
var catergories = null;
var cuisines = null;
getCategories();
getCuisines(76);
Replace YOUR_API_KEY with your Zomato key. Let’s build functions to get the list of categories and cuisines at startup. These queries need not be run when the user asks for a restaurant search because this information is pretty much static:
function getCuisines(cityId){
var options = {
uri: baseURL + 'cuisines',
headers: {
'user-key': apiKey
},
qs: {'city_id':cityId},
method: 'GET'
}
var callback = function(error, response, body) {
if (error) {
console.log('Error sending messages: ', error)
} else if (response.body.error) {
console.log('Error: ', response.body.error)
} else {
console.log(body);
cuisines = JSON.parse(body).cuisines;
}
}
request(options,callback);
}
The preceding code will fetch a list of cuisines available in a particular city (identified by a Zomato city ID). Let us add the code for identifying the list of categories:
function getCategories(){
var options = {
uri: baseURL + 'categories',
headers: {
'user-key': apiKey
},
qs: {},
method: 'GET'
}
var callback = function(error, response, body) {
if (error) {
console.log('Error sending messages: ', error)
} else if (response.body.error) {
console.log('Error: ', response.body.error)
} else {
categories = JSON.parse(body).categories;
}
}
request(options,callback);
}
Now that we have the basic functions out of our way, let us code in the restaurant search code:
function getRestaurant(cuisine, location, category){
var cuisineId = getCuisineId(cuisine);
var categoryId = getCategoryId(category);
var options = {
uri: baseURL + 'locations',
headers: {
'user-key': apiKey
},
qs: {'query':location},
method: 'GET'
}
var callback = function(error, response, body) {
if (error) {
console.log('Error sending messages: ', error)
} else if (response.body.error) {
console.log('Error: ', response.body.error)
} else {
console.log(body);
locationInfo = JSON.parse(body).location_suggestions;
search(locationInfo[0], cuisineId, categoryId);
}
}
request(options,callback);
}
function search(location, cuisineId, categoryId){
var options = {
uri: baseURL + 'search',
headers: {
'user-key': apiKey
},
qs: {'entity_id': location.entity_id,
'entity_type': location.entity_type,
'cuisines': [cuisineId],
'categories': [categoryId]},
method: 'GET'
}
var callback = function(error, response, body) {
if (error) {
console.log('Error sending messages: ', error)
} else if (response.body.error) {
console.log('Error: ', response.body.error)
} else {
console.log('Found restaurants:')
var results = JSON.parse(body).restaurants;
console.log(results);
}
}
request(options,callback);
}
The preceding code will look for restaurants in a given location, cuisine, and category. For instance, you can search for a list of Indian restaurants in Newington, Edinburgh that do delivery. We now need to integrate this with the chatbot code. Let us create a separate file called index.js. Let us begin with the basics:
var restify = require('restify');
var builder = require('botbuilder');
var request = require('request');
var baseURL = 'https://developers.zomato.com/api/v2.1/';
var apiKey = 'YOUR_API_KEY';
var catergories = null;
var cuisines = null;
Chapter 6
[ 247 ]
getCategories();
//setTimeout(function(){getCategoryId('Delivery')}, 10000);
getCuisines(76);
//setTimeout(function(){getCuisineId('European')}, 10000);
// Setup Restify Server
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});
// Create chat connector for communicating with
// the Bot Framework Service
var connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
});
// Listen for messages from users
server.post('/foodiebot', connector.listen());
Add the bot dialog code to carry out the restaurant search. Let us design the bot to ask for cuisine, category, and location before proceeding to the restaurant search:
var bot = new builder.UniversalBot(connector, [
function (session) {
session.send("Hi there! Hungry? Looking for a restaurant?");
session.send("Say 'search restaurant' to start searching.");
session.endDialog();
}
]);
// Search for a restaurant
bot.dialog('searchRestaurant', [
function (session) {
session.send('Ok. Searching for a restaurant!');
builder.Prompts.text(session, 'Where?');
},
function (session, results) {
session.conversationData.searchLocation = results.response;
builder.Prompts.text(session, 'Cuisine? Indian,
Italian, or anything else?');
},
function (session, results) {
session.conversationData.searchCuisine = results.response;
builder.Prompts.text(session, 'Delivery or Dine-in?');
},
function (session, results) {
session.conversationData.searchCategory = results.response;
session.send('Ok. Looking for restaurants..');
getRestaurant(session.conversationData.searchCuisine,
session.conversationData.searchLocation,
session.conversationData.searchCategory,
session);
}
])
.triggerAction({
matches: /^search restaurant$/i,
confirmPrompt: 'Your restaurant search task will be abandoned.
Are you sure?'
});
Notice that we are calling the getRestaurant() function with four parameters. Three of these are ones that we have already defined: cuisine, location, and category. To these, we have to add another: session. This passes the session pointer that can be used to send messages to the emulator when the data is ready. Notice how this changes the getRestaurant() and search() functions:
function getRestaurant(cuisine, location, category, session){
var cuisineId = getCuisineId(cuisine);
var categoryId = getCategoryId(category);
var options = {
uri: baseURL + 'locations',
headers: {
'user-key': apiKey
},
qs: {'query':location},
method: 'GET'
}
var callback = function(error, response, body) {
if (error) {
console.log('Error sending messages: ', error)
} else if (response.body.error) {
console.log('Error: ', response.body.error)
} else {
console.log(body);
locationInfo = JSON.parse(body).location_suggestions;
search(locationInfo[0], cuisineId,
categoryId, session);
}
}
request(options,callback);
}
function search(location, cuisineId, categoryId, session){
var options = {
uri: baseURL + 'search',
headers: {
'user-key': apiKey
},
qs: {'entity_id': location.entity_id,
'entity_type': location.entity_type,
'cuisines': [cuisineId],
'category': categoryId},
method: 'GET'
}
var callback = function(error, response, body) {
if (error) {
console.log('Error sending messages: ', error)
} else if (response.body.error) {
console.log('Error: ', response.body.error)
} else {
console.log('Found restaurants:')
console.log(body);
//var results = JSON.parse(body).restaurants;
//console.log(results);
var resultsCount = JSON.parse(body).results_found;
console.log('Found:' + resultsCount);
session.send('I have found ' + resultsCount +
' restaurants for you!');
session.endDialog();
}
}
request(options,callback);
}
Once the results are obtained, the bot responds using session.send() and ends the dialog:
Now that we have the results, let’s present them in a more visually appealing way using cards. To do this, we need a function that can take the results of the search and turn them into an array of cards:
function presentInCards(session, results){
var msg = new builder.Message(session);
msg.attachmentLayout(builder.AttachmentLayout.carousel)
var heroCardArray = [];
var l = results.length;
if (results.length > 10){
l = 10;
}
for (var i = 0; i < l; i++){
var r = results[i].restaurant;
var herocard = new builder.HeroCard(session)
.title(r.name)
.subtitle(r.location.address)
.text(r.user_rating.aggregate_rating)
.images([builder.CardImage.create(session, r.thumb)])
.buttons([
builder.CardAction.imBack(session,
"book_table:" + r.id,
"Book a table")
]);
heroCardArray.push(herocard);
}
msg.attachments(heroCardArray);
return msg;
}
And we call this function from the search() function:
function search(location, cuisineId, categoryId, session){
var options = {
uri: baseURL + 'search',
headers: {
'user-key': apiKey
},
qs: {'entity_id': location.entity_id,
'entity_type': location.entity_type,
'cuisines': [cuisineId],
'category': categoryId},
method: 'GET'
}
var callback = function(error, response, body) {
if (error) {
console.log('Error sending messages: ', error)
} else if (response.body.error) {
console.log('Error: ', response.body.error)
} else {
console.log('Found restaurants:')
console.log(body);
var results = JSON.parse(body).restaurants;
var msg = presentInCards(session, results);
session.send(msg);
session.endDialog();
}
}
request(options,callback);
}
Here is how it looks:
We saw how to build a restaurant search bot, that gives you restaurant suggestions as per your preference.
If you found our post useful check out Chatbots and Conversational UI Development.
Top 4 chatbot development frameworks for developers
How to create conversational assistant using Python
My friend, the robot: Artificial Intelligence needs Emotional Intelligence
I remember deciding to pursue my first IT certification, the CompTIA A+. I had signed…
Key takeaways The transformer architecture has proved to be revolutionary in outperforming the classical RNN…
Once we learn how to deploy an Ubuntu server, how to manage users, and how…
Key-takeaways: Clean code isn’t just a nice thing to have or a luxury in software projects; it's a necessity. If we…
While developing a web application, or setting dynamic pages and meta tags we need to deal with…
Software architecture is one of the most discussed topics in the software industry today, and…