Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Build and Test a Messenger Bot With Node.js and Optimizely

DZone's Guide to

Build and Test a Messenger Bot With Node.js and Optimizely

Interested in learning how to build your own chatbot? Read on to learn how to use Node, Optimizely, and some JavaScript code to do just that.

· Web Dev Zone
Free Resource

Get deep insight into Node.js applications with real-time metrics, CPU profiling, and heap snapshots with N|Solid from NodeSource. Learn more.

Let’s talk about chatbots. My first interaction with a chatbot, like millions of others, was SmarterChild on AOL Instant Messenger, in the early 2000s. SmarterChild offered an early glimpse into the potential that an automated bot could offer to real-world interactions. However, personally, it felt like a novelty in the midst of an era of existential crisis for internet technologies.

Fast forward to today, and we can see that the proliferation of smartphones, messaging platforms with open APIs (Twilio, Messenger, Slack, WhatsApp, etc.), and advancements in artificial intelligence have graduated chatbots from internet novelty to business necessity.

With the potential to reach more customers in more places, chatbots offer the potential to greatly improve how customers interact with businesses. Research from 2016 found that users spent 85% of their time in as little as 5 mobile apps each month. Recently Facebook announced that its Messenger platform has surpassed 1.3 billion monthly users, displaying steady user growth and showing its huge reach. Despite the opportunity, there are many hurdles to creating a successful chatbot, most notably insight into the optimal chatbot experience.

As companies such as KLM, eBay, and TD Ameritrade bring their digital experiences to platforms like Messenger, it's crucial to understand what works, what doesn't, and how users will engage with your bot early on in your development cycle.

Building Our Messenger Bot

For our bot, we emulated a recommendation feature, similar to eBay’s Messenger bot. For our experiment, we wanted to know whether it’s better to recommend one product or multiple products at a time.

We built the bot in Node.js and Express, in part because Node's concurrency works well for applications handling large amounts of requests, and it's easy to setup. Alternatively, we could've easily accomplished the same functionality using another language/framework such as Python + Flask, or Ruby + Sinatra.

First, we created an Optimizely Full Stack project and implemented the Optimizely Node SDK into our Node Express app.

Next, we configured the Optimizely SDK in our app to use a helper function to grab the Optimizely datafile and initialize the Optimizely client:

const OPTLY = require('optimizely-server-sdk'),
    defaultErrorHandler = require('optimizely-server-sdk/lib/plugins/error_handler'),
    defaultLogger = require('optimizely-server-sdk/lib/plugins/logger'),
    rp = require('request-promise');

module.exports.initializeClient = (url) => {
  let options = {uri: url, json: true};
  // Grabs datafile from CDN
  let initializePromise = new Promise((resolve, reject)=>{
    rp(options).then((datafile) => { 
        console.log('Initializing Optimizely Client with Datafile: ', datafile);
        let optlyClient = OPTLY.createInstance({
          datafile: datafile,
          errorHandler: defaultErrorHandler,
          logger: defaultLogger.createLogger()
        });
        resolve(optlyClient);
    });
  });
  return initializePromise;
};

For a production use case, you could also grab the datafile via Optimizely's REST API and leverage Optimizely Webhooks to keep the SDK in sync with changes you make in the Optimizely interface.

Instantiating Optimizely in Our Chatbot App

With the helper function to initialize Optimizely setup, we could then easily instantiate the Optimizely Full Stack client when our server starts so that we can begin using the SDK to run experiments.

let OPTLY_URL = `https://cdn.optimizely.com/json/${process.env.PROJECT_ID}.json`,
    optimizelyClientOb;

optimizely.initializeClient(OPTLY_URL).then((client)=>{
  console.log('Intialize promise resolve');
  optimizelyClient = client;
});

Once that was completed, we built out the endpoint to accept incoming events from Facebook - for example when a user sends our bot a message or takes an action from within Messenger.

// Post endpoint to receive requests from Facebook
app.post('/webhook/', (req, res) => {
  messaging_events = req.body.entry[0].messaging
  for (i = 0; i < messaging_events.length; i++) {
    event = req.body.entry[0].messaging[i];
    // Check to see if the event was a message from 
    if (event.message && event.message.text) {
      text = event.message.text
      console.log(‘Heres\’s what the user said ’, text);
      continue
    }
    // Check to see if the event was a postback for a Facebook event - https://developers.facebook.com/docs/messenger-platform/reference/webhook-events/messaging_postbacks/
console.log(‘Hey this was a postback!’)
      continue
    }
  }
  res.sendStatus(204);
});

Facebook Helper Functions

We also created some helper functions to handle sending a message back to Facebook and the user.

// Sending a Messenger template message
function sendGenericMessage(sender, data) {
    messageData = data;
    request({
        url: 'https://graph.facebook.com/v2.6/me/messages',
        qs: {access_token:token},
        method: 'POST',
        json: {
            recipient: {id:sender},
            message: messageData,
        }
    }, function(error, response, body) {
        if (error) {
            console.log('Error sending messages: ', error)
        } else if (response.body.error) {
            console.log('Error: ', response.body.error)
        }
    })
}

Creating the Experiment in Optimizely

Now that we've finished setting up our bot, we can build our experiment. For this simple use case, we only need two variations for testing the single product vs. multiple product recommendations. We'll also want to define our metrics for this experiment. In this case, we simply tracked if a user clicked to purchase an item, and the associated revenue generated. However, since users can also click through, the bot links to our website.

If we want to, we can also track events that occur outside of Messenger such as new account creations, products viewed, or purchases on your website or in your mobile app. We can easily do this using the Full Stack JavaScript SDK to track these events. The key thing that we will want to do is share the same between our Node.js server code and JavaScript client code so that we can be in sync.

Implementing the Experiment in Node

Finally, we built out the new functionality that we wanted to test. Here we added a condition to check if the user was asking for a recommendation as well as the logic for how to recommend products using hard-coded product data.

 if (event.message && event.message.text) {
      text = event.message.text
      if (text.toLowerCase().includes('recommend')) { 
        // Generate random user ID
        var userId = uuid();
        // Creates an array of the products from sample products/responses
        var categories = Object.keys(recs.products);
        var randomNumber = Math.floor(Math.random() * categories.length);
        // Select a random category to respond with
        var category = categories[randomNumber];
        // Uses global activate function defined on line 9
        var optimizelyVariation = optimizelyClient.activate('product_recs', userId);
        // Gets response from sample response json given category + variation
        var responseData = recs.products[category][optimizelyVariation];
        var products = responseData.attachment.payload.elements;
        // Adds generated user ID to products for tracking if user clicks to buy
        optimizelyVariation === "single_project" ? helpers.addUserId(userId, products) : helpers.addUserIdSingleProd(userId, products);
        // Sends chatbot response
        sendGenericMessage(sender, responseData);
        continue
    }
    if (event.postback) {
      // Fired when user clicks "Buy Now"  
      var payloadResponse = JSON.parse(event.postback.payload);
      // Grabs userID from payload
      var userId = payloadResponse.user;
      // Builds receipt
      var receipt = helpers.buildReceipt(payloadResponse);
      // Track event to Optimizely
      optlyClient.track('purchased', userId, {}, {revenue:receipt.attachment.payload.summary.total_cost * 100});
      // Send receipt
      sendGenericMessage(sender, receipt);
      continue
    }

We set up a rule to parse incoming requests from Facebook to detect the word "recommend." If anyone sent our bot a message looking for recommendations, we'd then activate them into our experiment, and either return one product or three different products.

Messenger Bot in Action!

Just like that, we created two different variations for how our chatbot should respond with recommendations and were able to track the effectiveness of each variation in terms of revenue and conversions.

Want to see our demo in action? Go to https://m.me/atticandbutton/ and ask for a recommendation via Messenger!

Additional Links:

Node.js application metrics sent directly to any statsd-compliant system. Get N|Solid

Topics:
web dev ,chatbots ,node.js ,web application development

Published at DZone with permission of Andreas Bloomquist, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}