© Abhishek Singh, Karthik Ramasubramanian, Shrey Shivam  2019
A. Singh et al.Building an Enterprise Chatbothttps://doi.org/10.1007/978-1-4842-5034-1_8

8. Chatbot Integration Mechanism

Abhishek Singh1 , Karthik Ramasubramanian1 and Shrey Shivam2
(1)
New Delhi, Delhi, India
(2)
Donegal, Donegal, Ireland
 

In Chapter 6, we designed a simple chatbot framework in Java which we called the IRIS (Intent Recognition and Information Service) framework. We discussed the core components of IRIS, such as how to define intents and how state machine can be implemented for defining state and transitions for building a conversational chatbot. An example use case focused on the insurance domain. In the example, we outlined specific capabilities that IRIS is supposed to perform such as providing market trend details, stock price information, weather details, and claim status.

In this chapter, we will focus on the integration modules of IRIS, which shows how we can connect with external data sources and third-party APIs for information retrieval.

Integration with Third-Party APIs

In our example, the three functionalities of IRIS require integration with third-party APIs (Figure 8-1):
  • Market trends

  • Stock prices

  • Weather information

../images/478492_1_En_8_Chapter/478492_1_En_8_Fig1_HTML.jpg
Figure 8-1

IRIS integration with third-party APIs

Market Trends

There are plenty of free and paid APIs available online that can provide these details. We explored www.alphavantage.co , which provides free APIs to get real-time and historical stock market data. Alpha Vantage APIs are grouped into four categories:
  • Stock time series data

  • Physical and digital/crypto currencies (e.g., Bitcoin)

  • Technical indicators

  • Sector performances

All APIs are in real time: the latest data points are derived from the current trading day.

Upon providing just three necessary details, which are the type of user, institution/organization name, and an email address, we get an API key, and it is free for a lifetime as per the website. The free API key can be obtained by providing details at www.alphavantage.co/support/#api-key .

Once we have the API key, there are various APIs of Alpha Vantage grouped under multiple API suites, as shown in Figure 8-2.
  • Stock time series

  • Forex

  • Technical indicators

  • Cryptocurrencies

  • Sector performances

../images/478492_1_En_8_Chapter/478492_1_En_8_Fig2_HTML.jpg
Figure 8-2

Alpha Vantage’s multiple API suites

More details on each of these APIs can be found at www.alphavantage.co/documentation/ .

For our example use case, we want to know how to get the current market trend and stock price of a particular stock. For the current market trend, we leverage the Sector Performances API, details of which are available at www.alphavantage.co/documentation/#sector-information .

A sample HTTP GET request to obtain real-time, sector-wise performance details is at www.alphavantage.co/query?function=SECTOR&apikey=demo .

The JSON response received from the API is
{
Meta Data:
{
Information: "US Sector Performance (realtime & historical)",
Last Refreshed: "03:44 PM ET 03/04/2019"
},
Rank A: Real-Time Performance:
{
Real Estate: "0.47%",
Materials: "0.39%",
Utilities: "0.11%",
Energy: "-0.10%",
Communication Services: "-0.10%",
Consumer Staples: "-0.22%",
Consumer Discretionary: "-0.23%",
Industrials: "-0.33%",
Information Technology: "-0.50%",
Financials: "-0.65%",
Health Care: "-1.49%"
},
Rank B: 1 Day Performance:
{
Energy: "1.81%",
Health Care: "1.41%",
Consumer Discretionary: "0.92%",
Communication Services: "0.78%",
Information Technology: "0.71%",
Financials: "0.54%",
Utilities: "0.19%",
Industrials: "0.09%",
Real Estate: "-0.13%",
Materials: "-0.16%",
Consumer Staples: "-0.17%"
},
Rank C: 5 Day Performance:
{},
Rank D: 1 Month Performance:
{},
Rank E: 3 Month Performance:
{},
Rank F: Year-to-Date (YTD) Performance:
{},
Rank G: 1 Year Performance:
{},
Rank H: 3 Year Performance:
{},
Rank I: 5 Year Performance:
{},
Rank J: 10 Year Performance:
{}
}
In this response, some data points are available such as real-time performance, one day, five days, and one-month performance. The Real-Time Performance key in the JSON response describes the different sectors of the market along with their percentage change in real time. We use this information to provide current market trend information in the MarketTrendState :
public String execute(MatchedIntent matchedIntent, Session session) {
// Third-party API that provides us the current market trend
String uri = "https://www.alphavantage.co/query?function=SECTOR&apikey=YOUR_KEY";
// Java client that performs HTTP request and gets a response by performing a GET call to the URL
             RestTemplate restTemplate = new RestTemplate();
/*
* Response is mapped to a string object below. However, in actual development, we should create a Java Bean (POJO) that will be used to map the response into a Java response object by using the getForObject method.
*/
             String result = restTemplate.getForObject(uri, String.class);
             String answer = "";
/*
* ObjectMapper provides functionality for reading and writing JSON, either to and from basic POJO. It is part of Jackson, a standard Java library for parsing JSON.
 */
             ObjectMapper mapper = new ObjectMapper();
             try {
/*
* JsonNode is used to parse response in a JSON tree model representation by Jackson. JsonNode is a base class for all JSON nodes, which form the basis of JSON Tree Model that Jackson implements. One way to think of these nodes is to consider them similar to DOM nodes in XML DOM trees
 */
                    JsonNode actualObj = mapper.readTree(result);
                    JsonNode jsonNode1 = actualObj.get("Rank A: Real-Time Performance");
                    answer = "Energy Sector is " + jsonNode1.get("Energy").textValue() + ". Utilities at "
                                + jsonNode1.get("Utilities").textValue() + ". Real Estate at "
                                + jsonNode1.get("Real Estate").textValue() + ". Consumer Staples at "
                                + jsonNode1.get("Consumer Staples").textValue() + ". Health Care at "
                                + jsonNode1.get("Health Care").textValue() + ". Materials at "
                                + jsonNode1.get("Materials").textValue() + ". Telecommunication Services at "
                                + jsonNode1.get("Telecommunication Services").textValue() + ". Industrials at "
                                + jsonNode1.get("Industrials").textValue() + ". Information Technology at "
                                + jsonNode1.get("Information Technology").textValue() + ". Consumer Discretionary at "
                                + jsonNode1.get("Consumer Discretionary").textValue() + ". Financials at "
                                + jsonNode1.get("Financials").textValue() + " What else do you want to know?";
             } catch (Exception e) {
                    e.printStackTrace();
                    Result = "I am unable to retrieve this information right now. There is some problem at my end. Try asking something else!";
             }
             return answer;
       }

Stock Prices

A user may interact with IRIS and may ask for stock price information. The utterance(s) could be
  • What is the current stock price of microsoft

  • Pru stock price

  • Infy stock today

  • Share price of hdfc

When the utterance is received by IRIS Core, it passes the utterance to the intent classification engine, which knows that the user is looking for 'STOCK_PRICE'. Based on this intent and current state, a transition to StockPriceState happens. The execute method of this state then makes a call to the third-party API.

To retrieve the stock price details, we use a TIME_SERIES_DAILY API from the Stock time series API suite of Alpha Vantage.

A sample HTTP GET request:

www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=MSFT&apikey=demo

A sample API response:
 {
Meta Data:
{
1. Information: "Daily Prices (open, high, low, close) and Volumes",
2. Symbol: "MSFT",
3. Last Refreshed: "2019-03-04 16:00:01",
4. Output Size: "Compact",
5. Time Zone: "US/Eastern"
},
Time Series (Daily):
{
2019-03-04:
{
1. open: "113.0200",
2. high: "113.2000",
3. low: "110.8000",
4. close: "112.2600",
5. volume: "25684300"
},
2019-03-01:
{
1. open: "112.8900",
2. high: "113.0200",
3. low: "111.6650",
4. close: "112.5300",
5. volume: "23501169"
},
2019-02-28:
{
1. open: "112.0400",
2. high: "112.8800",
3. low: "111.7300",
4. close: "112.0300",
5. volume: "29083934"
},
2019-02-27:
{},
2019-02-26:
{}
}
}
A sample execute method of StockPriceState could look like:
       public String execute(MatchedIntent matchedIntent, Session session) {
/*
* In the URL below, we have hard-coded symbol=MSFT. MSFT is the symbol for Microsoft. In an actual implementation, we should retrieve the company name from the user utterance and find its symbol and then pass it in the GET request below. There are many ways to convert from company name to symbol such as by calling publicly available services or by maintaining a mapping.
*/
             String uri = "https://www.alphavantage.co/query?apikey=YOUR_KEY&function=TIME_SERIES_DAILY&outputsize=compact&symbol=MSFT";
             RestTemplate restTemplate = new RestTemplate();
             String result = restTemplate.getForObject(uri, String.class);
// Default answer in case the third-party API does not respond or if there is any network related issue
             String answer = "I am somehow unable to retrieve stock price details right now. But I will be able to help you with your other queries.";
             ObjectMapper mapper = new ObjectMapper();
             try {
/*
 * As we know, the stock market does not run on the weekends and certain holidays. IRIS is expected to provide real-time stock performance data. In a normal working day, we parse out performance detail of current day but for a holiday or if the stock market was closed or if the stock did not trade that day, we get the performance detail of the previous day.
 */
                   Date date = new Date();
                   String today = new SimpleDateFormat("yyyy-MM-dd").format(date);
                   String yday = new SimpleDateFormat("yyyy-MM-dd").format(yesterday(1));
                   String dayBeforeYday = new SimpleDateFormat("yyyy-MM-dd").format(yesterday(2));
                   JsonNode actualObj = mapper.readTree(result);
                   JsonNode jsonNode1 = actualObj.get("Time Series (Daily)");
                   JsonNode jsonNode2 = jsonNode1.get(today);
                   JsonNode jsonNode3 = jsonNode1.get(yday);
                   JsonNode jsonNode4 = jsonNode1.get(dayBeforeYday);
                   if (jsonNode2 != null) {
                          answer = "Today Microsoft stock opened at " + jsonNode2.get("1. open").textValue() + " and closed at "
                                       + jsonNode2.get("4. close").textValue();
                          answer = answer + " It saw an intraday high of " + jsonNode2.get("2. high").textValue()
                                       + " and an intraday low of " + jsonNode2.get("3. low").textValue();
                          answer = answer + ". Total volume traded is " + jsonNode2.get("5. volume").textValue() + " "
                                       + "How else can I help you?";
                   } else if (jsonNode3 != null) {
                          answer = "I don't have the trading info for today as of now, but Yesterday PRU stock opened at "
                                       + jsonNode3.get("1. open").textValue() + " and closed at "
                                       + jsonNode3.get("4. close").textValue();
                          answer = answer + " It saw an intraday high of " + jsonNode3.get("2. high").textValue()
                                       + " and an intraday low of " + jsonNode3.get("3. low").textValue();
                          answer = answer + ". Total volume traded is " + jsonNode3.get("5. volume").textValue() + " "
                                       + "How else can I help you?";
                   } else if (jsonNode4 != null) {
                          answer = "On friday, before weekend, PRU stock opened at " + jsonNode4.get("1. open").textValue()
                                       + " and closed at " + jsonNode4.get("4. close").textValue();
                          answer = answer + " It saw an intraday high of " + jsonNode4.get("2. high").textValue()
                                       + " and an intraday low of " + jsonNode4.get("3. low").textValue();
                          answer = answer + ". Total volume traded is " + jsonNode4.get("5. volume").textValue() + " "
                                       + "How else can I help you?";
                    }
             } catch (Exception e) {
                    e.printStackTrace();
             }
             return answer;
       }
       /*
        *  A method to return 'days' before the current day. If the value of 'days' is 1, yesterday's date is returned.
        *  If the value is 2, the day before yesterday is returned and so on.
        */
       private Date yesterday(int days) {
             final Calendar cal = Calendar.getInstance();
             cal.add(Calendar.DATE, -days);
             return cal.getTime();
       }

Weather Information

There are plenty of digital bots available that provide weather details. People often ask Siri, Google voice assistant, and Alexa to give details on the weather. Let’s see how we can use a third-party API to integrate with IRIS for weather information.

To get the weather report, we leverage http://openweathermap.org , which provides the API to get weather details of the requested city. It offers multiple data points such as current weather data, 5-day forecast, 16-day forecast, and other historical information about the city. It currently includes weather details for over 200,000 cities around the world. The current weather is frequently updated based on global models and data from more than 40,000 weather stations. OpenWeather also provides APIs for relief maps, managing personal weather stations, bulk downloading, weather alerting, UV index, and air pollution.

For our example, we need the current weather in a given city. We need to obtain an API key. OpenWeather provides multiple API plans, the details of which can be found at https://openweathermap.org/price .

There is a free plan that allows a maximum of 60 calls per minute, which is more than enough for the demo. We use the Current Weather data API that can be called in multiple ways to get weather details such as the following:
  • Call the current weather data for one location:
    • By city name

    • By city ID

    • By geographic coordinates

    • By ZIP code

  • Call the current weather data for several cities:
    • Cities within a rectangle zone

    • Cities in cycle

    • Call for several city IDs

A sample HTTP GET request when querying by city name (passed in q): http://api.openweathermap.org/data/2.5/weather?appid=YOUR_APP_ID&q=dublin

The JSON response:
{
coord:
{},
weather:
[
{
id: 501,
main: "Rain",
description: "moderate rain",
icon: "10n"
}
],
base: "stations",
main:
{
temp: 277.07,
pressure: 993,
humidity: 100,
temp_min: 275.93,
temp_max: 278.15
},
visibility: 10000,
wind:
{
speed: 6.2,
deg: 230
},
rain:
{
1h: 1.14
},
clouds:
{
all: 75
},
dt: 1551735821,
sys:
{
type: 1,
id: 1565,
message: 0.0045,
country: "IE",
sunrise: 1551683064,
sunset: 1551722984
},
id: 2964574,
name: "Dublin",
cod: 200
}
A sample execute method of GetWeatherState may contain the following code snippet:
public String execute(MatchedIntent matchedIntent, Session session) {
/*
* Default response in case there is a network issue or if the third-party API takes a lot of time or if there is some other exception
*/
             String answer = "I am unable to get the weather report right now. But I hope it be a nice and charming day for you :) ";
             /*
              * GET API that provides weather details
              */
             String uri = "http://api.openweathermap.org/data/2.5/weather?appid=YOUR_API_KEY&q=";
             String cityName = "dublin";
             try {
                   RestTemplate restTemplate = new RestTemplate();
                   String result = restTemplate.getForObject(uri, String.class);
                   ObjectMapper mapper = new ObjectMapper();
                   JsonNode actualObj = mapper.readTree(result);
                   ArrayNode jsonNode1 = (ArrayNode) actualObj.get("weather");
                   JsonNode jsonNode2 = actualObj.get("main");
                   String description = jsonNode1.get(0).get("description").textValue();
                   String temperature = jsonNode2.get("temp").toString();
                   Double tempInCelsius = Double.parseDouble(temperature) - 273.15;
                   double roundOff = Math.round(tempInCelsius * 100.0) / 100.0;
                   String humidity = jsonNode2.get("humidity").toString();
                   answer = "It seems to be " + description + " at the moment in " + cityName + ". The temperature is "
                                 + roundOff + " degrees. Humidity" + " is close to " + humidity
                                 + ". I wish I were human to feel it. Anyways, what else do you want to know from me? ";
             } catch (Exception e) {
             }
             return answer;
       }

Connecting to an Enterprise Data Store

In our example, we use GetClaimStatus to demonstrate how to connect to a database and query claims information.

Note that although we are demonstrating this capability of querying a database directly, the modern design approach across the industry does not recommend it. Any database should be queried only through a service created on top of the database. There are multiple reasons for this, such as security and access control, database load and connection handling, encapsulation, and portability.
public class GetClaimStatus extends State {
       /*
        *  Java Database Connectivity (JDBC) is an application programming interface (API) for the programming language Java, which defines how a client may access a database. It is a Java-based data access technology used for Java database connectivity. It is part of the Java Standard Edition platform, from Oracle Corporation
        *  DB_URL is the database connection URL.
        *  The URL used is dependent upon the particular database and JDBC driver. It will always begin with the "JDBC:" protocol, but the rest is up to the specific vendor. In our example, we use a MySQL database.
        */
       static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
       // database name is a test
       static final String DB_URL = "jdbc:mysql://localhost/test";
       /*
        *  Database access credentials
        */
       static final String USERNAME = "ClaimsReadOnlyUser";
       static final String PASSWORD = "**********";
       public GetClaimStatus() {
             super("getClaimStatus");
       }
       @Override
       public String execute(MatchedIntent matchedIntent, Session session) {
             Connection conn = null;
             Statement stmt = null;
             String status = null;
/*
* Retrieve claim Id from a session or slot of the matched intent. If this state is executed, it is supposed to mean that we have the claim Id; otherwise Shield would not have validated transition to this state.
 */
             String claimId = SessionStorage.getStringFromSlotOrSession(matchedIntent, session, "claimId", null);
// Default answer
             String answer = "We dont have any info related to " + claimId + " in our system. "
                          + "Please contact our call service representative for further inquiry on the number 1800 333 0333 between Mondays to Fridays, 8:30 am to 5:30 pm. "
                          + "If you're dialling from overseas or via a payphone, please call +65 633 30333. Is there anything else I can help you with?";
             try {
                   //Register JDBC driver
                   Class.forName("com.mysql.jdbc.Driver");
                   // Open a connection (Connecting to database...)
                   conn = DriverManager.getConnection(DB_URL, USERNAME, PASSWORD);
                   // Execute a query
                   stmt = conn.createStatement();
/*
* SQL query to query row from test database and claims table. This query means - return status of the row from claims table where claim id is given claim Id.
*/
                   String sql = "SELECT status FROM claims where claimId='" + claimId + "'";
                   //executing SQL
                   ResultSet rs = stmt.executeQuery(sql);
                   //Extract data from the result set
                   while (rs.next()) {
                          //record fetched
                          status = rs.getString("status");
                   }
                   //Clean up environment and close active connections.
                   rs.close();
                   stmt.close();
                   conn.close();
             } catch (Exception e) {
                   e.printStackTrace();
             } finally {
/*
* In a try-catch, a finally block is always executed even if an exception happens. In case of exceptions in the above code for any reason, the statements and connections will not get closed. Hence we apply an extra check to close it in finally block .
 */
                   try {
                          if (stmt != null)
                                 stmt.close();
                   } catch (SQLException se2) {
                   }
                   try {
                          if (conn != null)
                                 conn.close();
                   } catch (SQLException se) {
                          se.printStackTrace();
                   }
             }
// If we received status from the database for that claims, we override the default answer with actual status details .
             if (status != null) {
                   answer = "The status of your claim for claimId " + claimId + " is  - " + status
                                 + ". Contact our representatives at HELPLINE-NUMBER "
                                 + "between Mondays to Fridays, 8:30am to 5:30pm if you want to inquire more. Anything else that you want to know as of now?";
             }
             // Remove claim Id related attributes from the session
             session.removeAttribute("getclaimidprompt");
             session.removeAttribute("claimid");
             return answer;
       }
}

Integration Module

The integration module is the piece that connects the core of IRIS with different messaging platforms such as Facebook Messenger, Twitter, the Web, mobile apps, Alexa, etc. Each of these platforms has their own integration mechanism, and building a customized integration layer for each of the channels is very difficult and not the core objective in this book. The integration module is a middle-layer service exposed to the outside world, acting as a gateway for IRIS, as shown in Figure 8-3.
../images/478492_1_En_8_Chapter/478492_1_En_8_Fig3_HTML.jpg
Figure 8-3

IRIS channel integration module

Many open source tools provide simple customized integrations with various channels without us having to write a lot of code, and they take care of a lot of complexity required for the integrations. In this illustration, we focus on bootkit, a leading development tool providing customized combinations with multiple messaging platforms:

https://github.com/howdyai/botkit .

In this chapter, we will discuss integration of IRIS with Facebook Messenger. Before we get into the integration code, we need to ensure we have the following in order to be able to put IRIS on a Facebook Messenger page:
  • A Facebook page: A Facebook page is used as the identity of your bot. When people chat with your app, they see the page name and profile picture.

  • A Facebook developer account: Your developer account is required to create new apps, which are the core of any Facebook integration. You can create a new developer account by going to Facebook for Developers and clicking the Get Started button.

  • Facebook App for Web: The Facebook app contains the settings for your Messenger bot, including access tokens.

  • A webhook URL: Actions that take place in conversations with your bot, such as new messages, are sent as events to your webhook. This is the URL of our integration module, which we cover next.

The setup process requires adding the Messenger platform to your Facebook app, configuring the webhook of the app, and subscribing your app to the Facebook page. The details on setting up the Facebook app can be found at https://developers.facebook.com/docs/messenger-platform/getting-started/app-setup/ .

A step-by-step guide to configuring the botkit and Facebook Messenger is available at www.botkit.ai/docs/provisioning/facebook_messenger.html .
  1. 1.
    Create the app and select the Messenger platform. See Figure 8-4.
    ../images/478492_1_En_8_Chapter/478492_1_En_8_Fig4_HTML.jpg
    Figure 8-4

    Creation of new app ID on Facebook

     
  2. 2.

    We created a Facebook page called AskIris and generated a page access token for the app. See Figure 8-5.

     
../images/478492_1_En_8_Chapter/478492_1_En_8_Fig5_HTML.jpg
Figure 8-5

Showing the page access token screen

The callback URL is https://YOURURL/facebook/receive. See Figure 8-6. This URL must be publically available and SSL-secured. We provide a localtunnel callback URL; in our case, it’s the NodeJs server URL tunneled to localhost. We will discuss how to create this endpoint and use of localtunnel in the next section.
../images/478492_1_En_8_Chapter/478492_1_En_8_Fig6_HTML.jpg
Figure 8-6

Showing page subscription details along with our callback URL

The AskIris page should be connected to the newly created app, and the settings should show the app details. See Figure 8-7.
../images/478492_1_En_8_Chapter/478492_1_En_8_Fig7_HTML.jpg
Figure 8-7

The AskIris page settings

As described above, setting up the webhook requires an HTTPS callback URL. This URL is the API endpoint of our integration module, which will receive messages from Messenger. We create a NodeJS application for this purpose because it is the requirement for setting up the webhook.

More details on setting up are described in Facebook developer page at

https://developers.facebook.com/docs/messenger-platform/getting-started/webhook-setup .

Here are the steps to create a simple NodeJS application:
  1. 1.
    Create an HTTP Server (server.js):
    // Import modules required in server.js
    var express = require('express');
    var bodyParser = require('body-parser');
    var https = require('https');
    var http = require('http');
    var fs = require('fs');
    var localtunnel = require('localtunnel');
    // Custom JavaScripts
    var conf = require(__dirname + '/conf.js');
    function server(ops) {
       // Create App
       /* Express is a popular Node web framework and provides a mechanism to write handlers.
      */
       var app = express();
      /* body-parser is Node.js middleware that parses incoming request bodies in a middleware before handlers.
      */
    // parse JSON
       app.use(bodyParser.json());
    //Returns middleware that only parses urlencoded bodies
       app.use(bodyParser.urlencoded({
    //This object will contain key-value pairs, where the value can be a string or array (when extended is false), or any type (when extended is true).
           extended: true
       }));
    // Path to static files
       app.use(express.static(__dirname + conf.static_dir));
    /* Declare option and create a HTTPS server by reading SSL key and SSL cert path from config file .
    */
       var options = {
           port : conf.securePort,
           key : fs.readFileSync(conf.sslKeyPath),
           cert : fs.readFileSync(conf.sslCertPath),
           requestCert : false,
           rejectUnauthorized : false
       };
       https.createServer( options, app)
       .listen(conf.securePort,  conf.hostname, function() {
           console.log('** Starting secure webserver on port ' + conf.securePort);
       });
       http.createServer(app)
       .listen(conf.port, conf.hostname, function() {
           console.log('** Starting webserver on port ' + conf.port);
       });
    /*
    localtunnel exposes localhost to the world for easy testing and sharing. It will connect to the tunnel server, set up the tunnel, and tell you what URL to use for your testing. We used localtunnel to get an https endpoint on http://localhost:9080/respond to test with Facebook Messenger .
    */
       if(ops.lt) {
           var tunnel = localtunnel(conf.port, {subdomain: 'askiris'}, function(err, tunnel) {
               if (err) {
                   console.log(err);
                   process.exit();
               }
               console.log("Your bot is available on the web at the following URL: " + tunnel.url + '/facebook/receive');
           });
           tunnel.on('close', function() {
               console.log("Your bot is no longer available on the web at the local tunnel.me URL.");
               process.exit();
           });
       }
       return app;
    }
    /* module.exports is an object that the current module returns when it is "required" in another program or module
    */
    module.exports = server;
     
  2. 2.
    Add the Facebook webhook endpoints (Webhooks.js):
    const fetch = require("node-fetch");
    // This is the IRIS API URL endpoint
    const url = "http://localhost:9080/respond";
    function webhooks(controller){
    /* This is the initial message a user sees before interacting with Iris for THE first time .
    */
       controller.api.messenger_profile.greeting('Hi, my name is IRIS. I am continuously training to become your Digital Virtual Assistant's.');
    // All messages will be sent to the API.
       controller.hears(['.*'], 'message_received,facebook_postback', function(bot, message) {
    // Facebook request message contains text, senderID, seq, and timestamp.
           var params = {
               message: message.text,
               sender: message.sender.id,
               seq: message.seq,
               timestamp: message. timestamp
           };
           var esc = encodeURIComponent;
           var query = Object.keys(params)
           .map(k => esc(k) + '=' + esc(params[k]))
           .join('&');
    /* fetch makes a HTTP GET call and receives a response and passes the 'message' back to Facebook .
    */
           fetch(url +query)
           .then(response => {
               response.json().then(json => {
                  bot.reply(message, json.message);
               });
           })
           .catch(error => {
               bot.reply(message, "");
           });
       });
    }
    module.exports = webhooks;
     
  3. 3.

    Add webhook verification. More details on specific of botkit-facebook integration are explained in detail at www.botkit.ai/docs/readme-facebook.html .

     
var Botkit = require('botkit');
var commandLineArgs = require('command-line-args');
var localtunnel = require('localtunnel');
// Reading static files
var server = require(__dirname + '/server.js');
var conf = require(__dirname + '/conf.js');
var webhook = require(__dirname + '/webhooks.js');
// Command line arguments to run in local mode vs server; we need to use localtunnel to connect to Facebook Messenger webhook in local mode as it requires an HTTPS endpoint .
const ops = commandLineArgs([
     {name: 'lt', alias: 'l', args: 1, description: 'Use localtunnel.me to make your bot available on the web.',
     type: Boolean, defaultValue: false},
     {name: 'ltsubdomain', alias: 's', args: 1,
     description: 'Custom subdomain for the localtunnel.me URL. This option can only be used together with --lt.',
     type: String, defaultValue: null},
  ]);
// Create the Botkit controller, which controls all instances of the bot.
var controller = Botkit.facebookbot({
   debug: true,
   log: true,
   access_token: conf.access_token,
   verify_token: conf.verify_token,
   app_secret: conf.app_secret,
   validate_requests: true
});
// Create server
var app = server(ops);
// Receive post data from FB; this will be the messages you receive.
app.post('/facebook/receive', function(req, res) {
    if (req.query && req.query['hub.mode'] == 'subscribe') {
       if (req.query['hub.verify_token'] == controller.config.verify_token) {
           res.send(req.query['hub.challenge']);
       } else {
           res.send('OK');
       }
   }
   // Respond to Facebook that the webhook has been received.
   res.status(200);
   res.send('ok');
   var bot = controller.spawn({});
   // Now, pass the webhook to be processed.
   controller.handleWebhookPayload(req, res, bot);
});
// Perform the FB webhook verification handshake with your verify token. The verification token is stored in the conf.js file.
app.get('/facebook/receive', function(req, res) {
   if (req.query['hub.mode'] == 'subscribe') {
       if (req.query['hub.verify_token'] == controller.config.verify_token) {
           res.send(req.query['hub.challenge']);
       } else {
           res.send('OK');
       }
   }else{
       res.send('NOT-OK');
   }
});
// Ping URL
app.get('/ping', function(req, res) {
           res.send('{"status":"ok"}');
});
webhook(controller);

Once we are all done with the above and have tested the endpoints, we are all set to start interacting with IRIS on Facebook Messenger.

Demonstration of AskIris Chatbot in Facebook Messenger

Let’s go through some example interactions. We can begin interacting with IRIS with a simple salutation to which IRIS replies with details about herself. See Figure 8-8.
../images/478492_1_En_8_Chapter/478492_1_En_8_Fig8_HTML.jpg
Figure 8-8

Interaction with IRIS

Account Balance

When the user asks IRIS for an account balance, it responds with a message asking for the confidential IPIN in order to proceed forward. As stated previously, demo implementation should not be adopted as a practice for setting a PIN. More complex and standard security authentication mechanism are available and should be followed. Upon a successful IPIN (which is a hard-coded value in the example use case), IRIS will retrieve the account balance for the account type the user asked for. See Figure 8-9.
../images/478492_1_En_8_Chapter/478492_1_En_8_Fig9_HTML.jpg
Figure 8-9

Asking for account balance details

Claim Status

The claim example demonstrates
  • Identification of intent as well as slot required for intent fulfilment in one utterance. In example 1 below, intent (CLAIM_STATUS) and claim ID (gi123) are obtained at the same time.

  • This demonstrates the potential of natural language processing to analyze natural user utterances, including spelling mistakes.

  • Also, it shows the handling of variations in which the user asks for the same information. In example 1, we have natural language-based inference of intent and slot. In example 2, since the slot is not obtained, IRIS prompts for this information just like any other typical conversation. See Figure 8-10.

../images/478492_1_En_8_Chapter/478492_1_En_8_Fig10_HTML.jpg
Figure 8-10

Requesting claim status

Weather Today

The weather example is a demonstration of how we can build a chatbot to integrate with third-party services in real time. User utterances such as the following return the live temperature information as available from the API (see Figure 8-11):
  • What’s the weather in Letterkenny today

  • Dublin weather today

  • Weather in Ranchi now

../images/478492_1_En_8_Chapter/478492_1_En_8_Fig11_HTML.jpg
Figure 8-11

Real time weather info from IRIS

Frequently Asked Questions

First, you can see the correct response to an FAQ. Then, when a user utterance (401k) neither matches any explicit intent nor any document in the knowledge repository, IRIS performs a search. See Figure 8-12.
../images/478492_1_En_8_Chapter/478492_1_En_8_Fig12_HTML.jpg
Figure 8-12

FAQ example

Context Switch Example

In Figure 8-13, the user starts by asking for a life insurance quote. IRIS prompts for age, smoker info, height, and weight. The user provides all other information as expected except for weight. Instead of providing weight, user asks for a stock price. Instead of replying to the user that they have not entered the weight correctly or that their weight was not recognized, IRIS understands the switch in context and hence the switch in intent and seamlessly provides the requested detail. Later, when the user asks for life insurance again, notice that the already answered questions are not asked again. This is done because of short term memory in which these details were stored.

When the user provides the answer to the question that they didn’t answer earlier, the intent is fulfilled and the response is obtained.
../images/478492_1_En_8_Chapter/478492_1_En_8_Fig13_HTML.jpg
Figure 8-13

Life insurance example illustrating context switch

Summary

In this chapter, we showed how to extend the functionalities of IRIS to support integration with various third-party services. Connecting with an enterprise database to fetch user data was also explained. We discussed the integration module through which IRIS is exposed to the outside world. We implemented integration of IRIS with Facebook Messenger and followed the step-by-step process required for a successful integration.

Finally, we illustrated what interaction with Facebook Messenger and IRIS looks like. We showed a few use cases via examples and explained behind the scenes what was discussed in previous chapters.

In the next chapter, we will discuss deploying this in-house framework to the cloud. We will explore various ways in which we can deploy our services to AWS. We will also discuss how IRIS can be integrated with Alexa in less than 5 minutes by going through a step-by-step process. We will conclude by discussing how this framework can be improved and the scope for further enhancements such as the implementation of a feedback loop.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset