Integration of SUSI AI in Twitter

We will be making a Susi messenger bot on Twitter. The messenger bot will tweet back to your tweets and reply instantly when you chat with it. Feel free to tweet to the already made SUSI AI account (mentioning @SusiAI1 in it). Follow it, to have a personal chat.

Make a new account, which you want to use as the bot account. You can make one from sign up option from https://www.twitter.com.

Prerequisites

To create your account on -:
1. Twitter
2. Github
3. Heroku
4. Node js

Setup your own Messenger Bot

1. Make a new app here, to know the access token and other properties for our application. These properties will help us communicate with Twitter.

Click “modify the app permissions” link, as shown here:

Select the Read, Write and Access direct messages option:

Don’t forget to click the update settings button at the bottom.

Click the Generate My Access Token and Token Secret button.

3. Create a new heroku app here.

This app will accept the requests from Twitter and Susi api.

4. Create a config variable by switching to settings page of your app.
  
  The name of your first config variable should be HEROKU_URL and its value is the url address of the heroku app created by you.
 

The other config variables that need to be created will be these:
 

The corresponding names of these variables in the same order are:
  i) Access token
  ii) Access token secret
  iii) Consumer key
  iv) Consumer secret
  
We need to visit our app from here, the keys and access tokens tab will help us with the values of these variables.

  1. Let’s start with the code part of the integration of SUSI AI to Twitter. We will be using Node js to achieve this integration.

First we need to require some packages:

Now using the Twit module, we need to authenticate our requests, by using our environment variables as set up in step 4:

Now let’s make a user stream:

var stream = T.stream('user');

We will be using the capabilities of this stream, to catch events of getting tweeted or receiving a direct message by using:

stream.on('tweet', functionToBeCalledWhenTweeted);
stream.on('follow', functionToBeCalledWhenFollowed);
stream.on('direct_message', functionToBeCalledWhenDirectMessaged);

So, when a person tweets to our account like this:

We can catch it with ‘tweet’ event and execute a set of instructions:

stream.on('tweet', tweetEvent);

    function tweetEvent(eventMsg) {
        var replyto = eventMsg.in_reply_to_screen_name;     

       // to store the message tweeted excluding '@SusiAI1' substring
        var text = eventMsg.text.substring(9);

        // to store the name of the tweeter
        var from = eventMsg.user.screen_name;
        
        if (replyto === 'SusiAI1') {
            var queryUrl = 'http://api.asksusi.com/susi/chat.json?q=' + encodeURI(text);
            var message = '';
            request({
                url: queryUrl,
                json: true
            }, function (err, response, data) {
                if (!err && response.statusCode === 200) {
                    // fetching the answer from the data object returned
                                        message = data.answers[0].actions[0].expression + data;


                }
                else {
                    message = 'Oops, Looks like Susi is taking a break';    
                    console.log(err);
                }
                console.log(message);
                // If the message length is more than tweet limit
                if(message.length > 140){
                    tweetIt('@' + from + ' Sorry due to tweet word limit, I have sent you a personal message. Check inbox'+date);
                    sendMessage(from, message);
                }
                else{
                    tweetIt('@' + from + ' ' + message + date);
                }
            });
        }
    }
  • When we a person follows this SUSI AI account, we can thank him/her by making use of the “follow” event. Also, we need to follow him/her back, to enable personal chat between Susi and that person (according to the rules of twitter):
stream.on('follow',followed);

function followed(eventMsg) {
        console.log('Follow event !');
        var name = eventMsg.source.name;
        var screenName = eventMsg.source.screen_name;
        var user_id1 = eventMsg.source.id_str;

        // To follow back the person.
        T.post('friendships/create', {user_id : user_id1},  function(err, tweets, response){
            if (err) {
                console.log("Couldn't follow back!");
            } 
            else {    
tweetIt('@' + screenName + ' Thank you for following me! I followed you back, you can also direct message me now! ');
                console.log("Followed back!");
            } 
        }); 
    }

When we personally message this SUSI AI account

This can be handled through the ‘direct_message’ event:

stream.on('direct_message', reply);
    function reply(directMsg) {
        console.log('You receive a message!');
        // If its our own bot messaging, ignore it, as this can lead to an infinite loop when we answer a user.
        if (directMsg.direct_message.sender_screen_name === 'SusiAI1') {
            return;
        }

        // code to fetch the reply from SUSI API
        
        // reply the user with the SUSI API's message
        sendMessage(directMsg.direct_message.sender_screen_name, message);
        });
    }
  • The tweetIt and sendMessage function code can be seen from the repo code.

6. Connect the heroku app to the forked repository.

Connect the app to Github by selecting the name of this forked repository.

7. Deploy on development branch. If you intend to contribute, it is recommended to Enable Automatic Deploys.

Branch Deployment.

Successful Deployment.

  1. Visit your own personal account and tweet to this new bot account with your query and enjoy a tweet back from the bot account! Also, you can enjoy personal chatting with Susi.

    Feel free to play around with the already made SUSI AI account on twitter here. Follow it, to have a personal chat with it.

    Resources
Continue ReadingIntegration of SUSI AI in Twitter

Integration of Susi AI to Gitter

This blog post discusses the development of Susi Messenger bot on Gitter. It replies instantly to the messages sent to it, using the Susi API. The Streaming API notifies us when a user messages to the SUSI chat room. The REST API helps to message back with a reply from SUSI API, to the SUSI chat room.

Feel free to message to the already made SUSI AI account on Gitter and have a chat with it.

Prerequisites

  1. Basic knowledge about calling API’s and fetching data or posting data to the API.
  2. Node.js language.
  3. Github
  4. Heroku

Figure – Architecture for running SUSI AI on different messaging services.

This blog post will walk you through each of the steps required to integrate SUSI AI to Gitter:

Setup SUSI AI Bot on Gitter

  1. Create a Github or twitter account with a username having ‘Susi’ as its substring because this is the name that will be shown with the reply string we will get from Susi AI.
  2. Now you need to sign in to Gitter with a twitter or Github account from here.
  3. Create your community by visiting this page. After writing your community name press next, invite the people you want to be in this room and press next. You will be redirected to your communities lobby. This lobby is the chat room to which we will deploy our SUSI AI.
  4. Now visit the Gitter developer page, press sign in on the top right. You will be redirected to your apps page. Copy the personal access token written there as shown in this image: (The area colored black will have your access token).

5. On a new tab, in your browser visit   https://api.gitter.im/v1/rooms?access_token=YOUR_ACCESS_TOKEN, with YOUR_ACCESS_TOKEN replaced by the token we just copied.

A JSON object will be shown on our browser screen. You will see the value of ‘name’ key as YOUR_COMMUNITY_NAME/Lobby. Copy the id of this chat room, as we will need it later. You can refer to the image below, you will have your chat room id in the area colored black.

  1. Create a new heroku app here.

This app will accept the requests from Gitter and Susi api.

  1. Set the config variables for this heroku app in the setting tab of your account. Set ROOM_ID to the id of the chat room and TOKEN to the personal access token, we copied in steps 4 and 5.

These were the formalities to be done to have our chat bot account on Gitter.

  1. Let’s jump to the code part of how this integration will be done:

To use the two config variables set in Heroku, we need these two lines in our Node js code:

var roomId = process.env.ROOM_ID;
var token = process.env.TOKEN;

We need to set up an options variable with our access token and room id in it:

// Setting the options variable to use it in the https request block
var options = {
    hostname: 'stream.gitter.im',
    port:     443,
    path:     '/v1/rooms/' + roomId + '/chatMessages',
    method:   'GET',
    headers:  {'Authorization': 'Bearer ' + token}
};

We will send this options variable when making a request so that Gitter lets our request through and notifies us when a client messages to our SUSI chat room.

The res.on(‘data’) accepts a function which is called when a client messages to our SUSI chat room:

// making a request to gitter stream API
var req = https.request(options, function(res) {
 res.on('data', function(chunk) {
    // do stuff
 }
}

req.on('error', function(e) {
 console.log('Hey something went wrong: ' + e.message);
});

req.end();

According to the docs of REST API in Gitter, the JSON data that we receive when a client sends a message to a chat room is like this:

To get this response set in a variable, we can use this code snippet:

res.on('data', function(chunk) {
   var msg = chunk.toString();
   if(msg != " \n"){              // If message is not an empty message
     var jsonMsg = JSON.parse(msg);

Now we have this json response as shown in the above figure in the jsonMsg variable. To extract the client’s message from this json object:

var clientMsg = jsonMsg.text;

As we now have the user query in clientMsg variable. Let’s call Susi API and fetch an answer from it for a query.

As an example, let’s first take the query as ‘hi’ and visit http://api.asksusi.com/susi/chat.json?q=hi from the browser. We will get a JSON object as follows:

{
        "query": "hi",
        "count": 1,
        "client_id": "aG9zdF8xMDMuMjkuMjIyLjE4MA==",
        "query_date": "2017-07-17T02:29:44.171Z",
        "answers": [{
            "data": [{
                "0": "hi",
                "token_original": "hi",
                "token_canonical": "hi",
                "token_categorized": "hi",
                "timezoneOffset": "-330",
                "language": "en"
            }],
            "metadata": {"count": 1},
            "actions": [{
                    "type": "answer",
                    "language": "de",
                    "expression": "Hallo!"
            }],
   "skills": ["/susi_skill_data/models/general/smalltalk/de/German-Standalone-aiml2susi.txt"]
        }],
        "answer_date": "2017-07-17T02:29:44.179Z",
        "answer_time": 8,
        "language": "en",
        "session": {"identity": {
            "type": "host",
            "name": "103.29.222.180",
            "anonymous": true
        }}
    }

The answer can be found as the value of the key named expression. In this case, it is “Hallo!”.

To fetch the answer through coding for our client message, we can use this code snippet in Node js:

// including request module
var request = require(‘request’);

// setting options to make a successful call to Susi API.
var susiOptions = {
            method: 'GET',
            url: 'http://api.asksusi.com/susi/chat.json',
            qs:
            {
                timezoneOffset: '-330',
                q: clientMsg  //the client message sent to SUSI chat room.
            }
        };

// A request to the Susi bot
request(susiOptions, function (error, response, body) {
    if (error)
        throw new Error(error);
    // answer fetched from susi
    ans = (JSON.parse(body)).answers[0].actions[0].expression;
}

The properties required for the call are set up through a JSON object (i.e. susiOptions). Pass the susiOptions object to our request function as its 1st parameter. The response from the API will be stored in ‘body’ variable. We need to parse this body, to be able to access the properties of that body object. Hence, fetching the answer from Susi API.

As we now have the answer, let’s call the API of Gitter to show our answer back to the user. Let’s code the request for that:

// To send a reply by Susi AI to client's message back to Gitter
         var gitterOptions = {
                               method: 'POST',
                               url: 'https://api.gitter.im/v1/rooms/'+roomId+'/chatMessages',
                               headers:
                               {
                                 'authorization': 'Bearer '+ token ,
                                 'content-type': 'application/json',
                                 'accept': 'application/json'
                               },
                               body:
                               {
                                 text: ans
                               },
                               json: true
                             };

         // making the request to Gitter API
         request(gitterOptions, function (error, response, body) {
           if(error)
             throw new Error(error);
           console.log(body);
         });

Hence, we have made the basic chat work!

The streaming API of Gitter notifies us for every message sent to our chat room. We will also be notified about the reply message sent by ourselves. To not fall into an infinite loop of answers and questions by SUSI itself, we must include this line in our code:

res.on('data', function(chunk) {
   var msg = chunk.toString();
   if(msg != ” \n”){              // If message is not an empty message
     var jsonMsg = JSON.parse(msg);
     if(jsonMsg.fromUser.displayName != 'SusiAI'){ // If it’s not our own answer
        // do stuff 
     }
   }
}

req.on('error', function(e) {
 console.log('Hey something went wrong: ' + e.message);
});

req.end();

The display name in my case is ‘SusiAI’, but it may be different in your case according to the Github or Twitter id made by you.

  1. Upload this code to Github.
  2. Connect the Heroku app to the Github repository, which has your code.

  1. Deploy on the development branch. If you intend to contribute, it is recommended to Enable Automatic Deploys.

Branch Deployment.

Successful Deployment.

  1. Go to your Gitter room created and enjoy chatting with Susi.Resources
Continue ReadingIntegration of Susi AI to Gitter

Using Variables in a SUSI skill

One of the best feature provided in making a skill is the ease of using variables. From storing the favourite book of the user to the most recent movie he searched for to the mood he is in, variables play an indispensable part. If any problem is faced with the code part, the skill referred in this blog is coded in this file in susi_skill_data repository

This link refers to the official docs of SUSI, which walk you through some basic examples of how to use variables in a SUSI skill. Great skills can be achieved using them like the skill below:

It’s easy to make such skills by using variables. Let’s check it out how this skill can be achieved.

To store value in a variable we use this syntax during the skill development

^value^>_variableName

First, let’s save the favourite dish of the user and then we will try to surprise him/her with a witty answer.

I love * dish
^$1$^>_userFavouriteDish

So, if the user types “I love biryani dish”, $1$ will be equal to biryani. Let’s save it to _userFavouriteDish variable.

Now if user asks “What should i eat” to SUSI, I bet SUSI will answer a well calculated answer!

What should i eat?
I am sure you will love $_userFavouriteDish$!

Another example that can answer back the user efficiently:

How to cook biryani?

#Gives recipies and links to cook a dish
* cook *
!console:To cook  $title$ , check out $href$ and make sure you have $ingredients$! ^$2$^>_recentSearch
{
"url":"http://www.recipepuppy.com/api/?q=$2$",
"path":"$.results"
}
eol

In the above code, we saved the dish searched for at the end of the output.

If somehow user ends up asking “what is the most recent dish i searched for”. It’s skill will be:

what is the most recent dish I searched for?
It was $_recentSearch$

Even if before asking this question, user asks “how to cook sushi”. The _recentSearch variable will be overridden with value “sushi” instead of “biryani”. Hence, SUSI won’t mistake answering “most recent dish” as “sushi”!

Now I think we are bit comfortable with use of variables in a skill. Let’s get back to our target skill i.e. remembering skill. We store the thing asked to remember in a variable having the same name as of that thing and the statement related to it as the value of that variable. Examples:

Remember that my keys are on the table. So the variable will be named “keys” and it’s value will be “on the table”.

Remember that my birthday is on 20th of December. So the variable will be named “birthday” and it’s value will be “on 20th of December”.

Remember that my meetings are at 8 pm with mentors and at 9:30 pm with Shruti. So the variable will be named “meetings” and it’s value will be “at 8 pm with mentors and at 9:30 pm with Shruti”.

Hence the skill:

Remember that my * is * | Remember that my * is *
Okay, remembered!^$2$^>_$1$

When the user will ask for any of its thing, we will just show the value of the variable having the same name as of the thing asked. Examples:

#$_keys$ will be our answer
Where are my keys?
On the table                   

#$_meetings$ will be our answer
When are my meetings?
at 8 pm with mentors and at 9:30 pm with Shruti

Hence the skill which answers the question is:

when are my * | where is my * | where are my *
$_$1$$

So the skill as a whole will be:

Remember that my * is * | Remember that my * is *
Okay, remembered!^$2$^>_$1$

when are my * | where is my * | where are my *
$_$1$$

Resources

Continue ReadingUsing Variables in a SUSI skill

Integration of SUSI AI to Alexa

An Alexa skill which can be used to ask susi for answers like: “Alexa, ask susi chat who are you” or “Alexa, ask susi chat what is the temperature in berlin”.

If at any point of time, you are unclear about the code in the blog post, you can check the code of the already made SUSI Alexa skill from the susi_alexa_skill repository.

Getting Started : Alexa Susi AI Skill

Follow the instructions below:

Visit the Amazon developer site and Login.

Click Alexa, on the top bar.

Click Alexa skills kit.

Click on add a new skill button on the top right of the page.

We will be at the skill information tab.

 

Write the name of the skill Write the invocation name of the skill i.e. the name that will be used to trigger your skill. Like in our case, if we need to ask anything (as we have ‘susi chat’ as the invocation name), we will ask with “Alexa, ask susi chat” as a prefix to our question sentence.

By clicking next, we will be redirected to the second tab i.e. Interaction model. We need to fill two fields here i.e. intent schema and sample utterances. For intent schema, we need to write all the available intents and the parameters for each of them. Like in our case:

{
 "intents": 
 [
   {
     "slots": [
       {
         "name": "query",
         "type": "AMAZON.LITERAL"
       }
     ],
     "intent": "callSusiApi"
   }
 ]
}

We have a single intent that is “callSusiApi” and the parameter it accepts is “query” of type “AMAZON.LITERAL” (in simple words, a string type). Parameters are termed as slots here. The different types of slots available, can be seen from here.

For sample utterances, we need to tell what utterances by the client will lead to what intent. In our case:

We have just one intent and the whole string uttered by the client should be fed to this intent as a “query” slot (parameter).

Let’s click next now.

We will be shifted to the configuration tab.

We will be making a lambda function, which will hold the code for our Susi skill, further we need to link that code to this skill. To do the linking we need to get the Amazon resource name i.e. ARN and fill it in the field named endpoint:


To get the amazon resource name, in a new tab, visit here. Visit “Lambda” followed by get started button. Click on “Create a lambda function”:

We need to select a blueprint for our lambda function. Select the “blank function” option for that.


Click next.

For configure triggers option, click this box and select “Alexa skills kit” option.


Click next.

In configure function tab, just write the name of the function and its description. Let’s code our lambda function:

// basic syntax that should be available in the lambda function
var https = require('http');
exports.handler = (event, context) => {
  try {
    if (event.session.new) {
      // New Session
      console.log("NEW SESSION")
    }
    switch (event.request.type) {
      case "LaunchRequest":
        // Launch Request
        console.log(`LAUNCH REQUEST`)
        context.succeed(
          generateResponse(
            buildSpeechletResponse("Welcome to a Susi Skill, this is an A.I. chatbot developed by Fossasia open source community. Ask anything to me like temperature at a place or rating of a movie or any other thing which you would like to ask?", false),
            {}
          )
        )
        break;
      case "IntentRequest":
        // Intent Request
        console.log(`INTENT REQUEST`)

        switch(event.request.intent.name) {
          case "callSusiApi":
            console.log(event.request.intent.slots.query.value)
            var endpoint = "http://api.susi.ai/susi/chat.json?q="+event.request.intent.slots.query.value; // ENDPOINT GOES HERE
            var body = ""
            https.get(endpoint, (response) => {
              response.on('data', (chunk) => { body += chunk })
              response.on('end', () => {
                var data = JSON.parse(body)
                // fetching answer from susi
                var viewCount = data.answers[0].actions[0].expression;
                if(viewCount.indexOf('I found this on the web') != -1)
                    viewCount = 'I have no idea about it, sorry.';
                context.succeed(
                  generateResponse(
                    buildSpeechletResponse(`${viewCount}`, false),
                    {}
                  )
                )
              })
            })
            break;

          default:
            throw "Invalid intent"
        }

        break;
      case "SessionEndedRequest":
        // Session Ended Request
        console.log(`SESSION ENDED REQUEST`)
        break;
      default:
        context.fail(`INVALID REQUEST TYPE: ${event.request.type}`)
    }
  } catch(error) { context.fail(`Exception: ${error}`) }
}

// Helpers
buildSpeechletResponse = (outputText, shouldEndSession) => {
  return {
    outputSpeech: {
      type: "PlainText",
      text: outputText
    },
    shouldEndSession: shouldEndSession
  }
}

generateResponse = (speechletResponse, sessionAttributes) => {
  return {
    version: "1.0",
    sessionAttributes: sessionAttributes,
    response: speechletResponse
  }
}

Paste this code into the space given below “lambda function code”. In lambda function handler and role, Click the field named role and select “create a custom role” from the dropdown shown.

You will be redirected to a new page. Select the IAM role as lambda_basic_execution:

Click allow button in the bottom right. We will be redirected back to our previous page. We don’t need to worry about other settings on this page.

Click next.

Again cross-check the details shown and click next.

Now we will have our ARN (Amazon resource name) on the top right of the page.


Copy that and paste it into the field “endpoint” on our previously open browser tab:


Click next.

Great that our SUSI AI skill is ready!

Now we can test it with a sample query, when we get redirected to the test tab:


Also we can test it using our voice through reverb app available on play store or echosim by logging into your amazon account.

Till now, the skill can just be invoked or tested from your own amazon id. To make this skill public , you need to fill the other 2 tabs left that are “publishing information” and “privacy and compliance”.

Some sample strings that we can speak to test it: “Alexa, ask susi chat where are you” “Alexa, ask susi chat tell me a joke” “Alexa, ask susi chat what is a table” (where ‘susi chat’ is the invocation name).

This was the video that helped a lot in making this skill for Alexa. It can be referred too.

Continue ReadingIntegration of SUSI AI to Alexa

Deploy SUSI.AI to a Messenger

Integration of SUSI AI to messenger platform has become a vital step as to enhance the popularity of this chatbot and to target a large base of users. For example – Viber claims that it has a user base of 800 million. So just integrating SUSI AI to Viber can increase its user base exponentially. This integration also proves to be a big boon, if the chat bot learns with the number and variations in the questions being asked. Like in the case of the web chat client (Susi AI).

This blog post will walk you through on how to deploy SUSI.AI to a messenger platform (Viber and Line messengers are used as an example in this post). We will be using Node.js and REST API technology in our example integrations. The repository of deployment of Susi AI to Viber can be found at susi_viberbot, and to Line messenger at susi_linebot. The SUSI AI Viberbot can be followed from here and Linebot by scanning this QR code.

The diagram below will give you an overview on what flow is followed to deploy SUSI AI chatbot to various messenger platforms.

Fig: Integration of Susi AI to chat messengers.

Let’s walk through each of the steps mentioned in the above diagram.

  1. To get familiar with SUSI.AI chatbot.

We have an API from where we fetch answers. To get a reply for the query ‘hi’, we can visit the API link with the query ‘hi’ appended to it (http://api.susi.ai/susi/chat.json?q=hi). You can chat with SUSI AI here.

  1. To set up a private SUSI AI chatbot account.

A account must be set up in the messenger platform, so that the user can message in that account to get a reply by the chatbot. Steps to set up the chatbot account is dependent on the messenger platform.

  1. To set up a webhook url.

The message sent to the chatbot account, must somehow connect to the chatbot. This message can be fed as a query to the chatbot, so that accordingly chatbot can think of a reply. To achieve this we need a url referred to as the webhook url.

The messages sent by the user, to the SUSI AI chatbot account on the messenger, can then be redirected to this url.

(Heroku platform allows 5 apps to be hosted on its platform for free, so you can check this documentation on how to host a node js app there.)

Now we need to think on how to handle these messages.

  1. To host code on our webhook url

As said earlier, we will be using Node js technology.

Generally, the messages from our SUSI AI chatbot account on the messenger will travel as requests to our webhook url. These come as POST requests to our url. To handle that we can use this piece of code:

app.post('/', function(request, response) {
    response.writeHead(200);

    // first step here, getting the message string from the request body

    // second step, calling the chatbot to get the reply to this message

    // third step, to send this reply back to our messenger's API
    
    response.end();
}

Let’s go through the three steps:

  • Getting the message string from the request body:

Request body is in JSON in case of REST API. To be extra sure, we use:

var reqBody = JSON.parse(request.body);

What property of this reqBody has our message string is dependent on the messenger platform. Suppose we have our message in the actions property of the reqBody, we can access that by:

var message = reqBody.actions;

For example in Viber, we need to use this piece of code:

app.post('/', function(req, response) {
    response.writeHead(200);

    // If user sends a message in 1-on-1 chat to the susi public account
    if(req.body.event === 'message'){
        // call chatbot or it’s API with event.message.text as the message string.
    }
}

In Line messenger, we accept the request at ‘/webhook’:

// register a webhook handler with middleware
app.post('/webhook', line.middleware(config), (req, res) => {
  // here events property has our message string somewhere nested in it.
  Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

// event handler
function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
      // ignore non-text-message event
      return Promise.resolve(null);
  }
  // call chatbot API with event.message.text as the query string.
}

So the code is dependent on the messenger platform.

  • Calling the SUSI AI chatbot API to get the reply to this message(‘hi susi’ in this case).

This part of our code will remain constant, for any messenger platform.

Let’s first see SUSI API’s answer to query “hi susi” and get familiar with it. Visit http://api.asksusi.com/susi/chat.json?q=hi susi from the browser and get familiar with the JSON object returned.

We get a JSON object as follows:

The answer can be found as the value of the key named expression. In this case it is “Hi, I’m Susi”.

To fetch the answer through coding, we can use this code snippet in Node js:

// including request module
var request = require(‘request’);

// setting options to make a successful call to Susi API.
var options = {
    method: 'GET',          
    url:'http://api.asksusi.com/susi/chat.json',
    qs:
    {
        timezoneOffset: '-330',
        q:'hi susi'
    }
};

// A request to the Susi bot
request(options, function (error, response, body) {
    if (error)
        throw new Error(error);
    // answer fetched from susi
    ans = (JSON.parse(body)).answers[0].actions[0].expression;
}

The properties required for the call are set up through a json object (i.e. options). Pass the options object to our request function as its first parameter. The response by the API will be stored in ‘body’ variable. We need to parse this body, to be able to access the properties of that body object. Hence, fetching the answer from Susi API.

  • To send the answer fetched from our SUSI API, back to our messenger.

This feature too is dependent on the messenger platform. Initially as the messenger platform requests us with a request which has the user message string. Now, it’s our time to send a request back to our messenger platform with a reply(i.e. the answer we will fetch from our chat bot’s API).

Generally, it is sent as a POST request.

The basic code to send a message to the messenger API:

// Assuming ans variable has the reply by our chatbot.
// setting options to request the chat api of viber.
var options1 = {
    method: 'POST',
    url: MESSENGER_API_URL,
    headers: headerBody,
    body:
    {
        // other properties dependent to the messenger
        text: ans
        // the property name can be different from 'text'
    },
    json: true
};

// request to the api of messenger.
request(options1, function (error1, res, body1) {
    if (error1) 
        throw new Error(error1);
    console.log(body1);
});

In case of Viber, we set up an options variable and request Viber’s chat API:

// setting options to request the chat api of viber.
var options1 = {
    method: 'POST',
    url: 'https://chatapi.viber.com/pa/send_message',
    headers: headerBody,
    body:
    {
        receiver: req.body.sender.id,
        min_api_version: 1,
        sender:
        {
            name: 'Susi',
            avatar: ''
        },
        tracking_data: 'tracking data',
        type: 'text',
        text: ans
    },
    json: true
};

// request to the chat api of viber.
request(options1, function (error1, res, body1) {
    if (error1) throw new Error(error1);
        console.log(body1);
});

In case of Line messenger, we use it’s reply API:

const answer = {
    type: 'text',
    text: ans
};

// use reply API
return client.replyMessage(event.replyToken, answer);
Continue ReadingDeploy SUSI.AI to a Messenger

Deploy SUSI AI to Viber messenger

Prerequisites

  1. Basic knowledge about calling API’s and fetching data or posting data to the API.
  2. Node.js language.
  3. Github
  4. Heroku

FigArchitecture for running all different messaging services.

To integrate Susi AI chat to Viber, a public account is needed, messaging to which users can chat with Susi.

We need to have a webhook url. Webhook url is a url which serves our Node.js code i.e. the code we will write to serve requests from Viber and to respond back to it.

Whenever a user messages to the SUSI AI public account, these messages come as post requests to our webhook url. The url then requests Susi API to give an answer for the (question based) message received from Viber. The answer fetched from Susi API is sent to the messenger platform’s API by the webhook url, to show it to the user on Viber.

As said we need a public account for our chat bot. The steps to be followed can be seen from here (Steps 2 and 3).

The REST API helps to make applications follow a RESTful way. In this way, the requests and response are in the form of JSON objects. Any language can be used to make an application follow a RESTful way.

In this blog, I will be using Node.js language.

The Rest API Viber, is the document to be followed for integration of a chatbot to Viber. Let’s go through each of the steps:

  1. To call Susi API and fetch an answer from it for a query (‘hi’ in this case). Let’s first visit http://api.asksusi.com/susi/chat.json?q=hi from the browser. We will get a JSON object as follows:

The answer can be found as the value of the key named expression. In this case it is “Hallo!”.

To fetch the answer through coding, we can use this code snippet in Node js:

// including request module
var request = require(‘request’);

// setting options to make a successful call to Susi API.
var options = {
    method: 'GET',
    url: 'http://api.asksusi.com/susi/chat.json',
    qs: 
    {
        timezoneOffset: '-330',
        q:'hi'
    }
};

// A request to the Susi bot
request(options, function (error, response, body) {
    if(error)
        throw new Error(error);
    //answer fetched from susi
    ans = (JSON.parse(body)).answers[0].actions[0].expression;
}

The properties required for the call are set up through a json object (i.e. options). Pass the options object to our request function as its 1st parameter. The response by the API will be stored in ‘body’ variable. We need to parse this body, to be able to access the properties of that body object. Hence, fetching the answer from Susi API.

  1. Let’s set the webhook url for our Susi public account. The folder containing our Node.js code must be pushed to a repo in github. We need to do some changes to the default package.json file in our project.

This file has a code portion:

The “test” key and its value must be replaced with “start”: “node index.js” i.e. node followed by the name of the main file which has to accept the requests from Viber and responds to it. In my case it is index.js . So the resultant code of package.json file should look like this:

Now, even on the local system you can run the node application by running “npm start” command from our project folder.

  • Push this project to github.
  • Create a new heroku app for Node js, following the steps here.
  • Then link this app to the repository where you pushed your code. For reference, follow the steps 7 and 8 here.

To set a webhook for our account, we can use this code snippet.

var headerBody = { 
                     'cache-control': 'no-cache',
                     'content-type': 'application/json',
                     'x-viber-auth-token': 'YOUR_X_VIBER_AUTH_TOKEN'
                 };

var options = {
                  method: 'POST',
                  url: 'https://chatapi.viber.com/pa/set_webhook',
                  headers: headerBody,
                  body:
                       { 
                           url: 'YOUR_WEBHOOK_URL',
                           event_types: ['delivered', 'seen', 'failed', 'subscribed', 'unsubscribed','conversation_started']
                       },
                  json: true 
              };

// request to the chat api of viber.
request(options, function(error, res, body) {
    if (error)
        throw new Error(error);
    response.write("The status message - " + body.status_message);
    response.end(); 
});

To set the webhook url, we need to post info to the viber chat api i.e. https://chatapi.viber.com/pa/set_webhook. The headers key present in our options object must have the Viber authentication key in the object passed to it. So we have passed a headerBody object to it, which contains our x-viber-auth-token. This property helps Viber to set webhook url for the account that corresponds to this passed Viber authentication token. The format of the body of options object is according to this(as stated in the doc):

{  
    "url": "https://my.host.com",  
    "event_types": ["delivered", "seen", "failed", "subscribed", "unsubscribed", "conversation_started"]  
}

We can wrap up this code in a app.get() block. So that whenever we visit our webhook url from a browser, we initiate a get request. This request as seen below, initiates a request to the chat api of Viber to set this url as a webhook url for our Susi AI public account.

app.get('/',function(req, response){
    response.writeHead(200,{'content-type': 'text/plain'});
    response.write("To chat with Susi through Viber, visit this link - chats.viber.com/chatauto and click on the 'Have a look' button\n\n");

    // setting options to request the chat api of viber.
    var options = {
                      method: 'POST',
                      url:'https://chatapi.viber.com/pa/set_webhook',
                      headers: headerBody,
                      body: 
                      {
                          url:'https://intense-crag-83953.herokuapp.com',
                          event_types: ['delivered', 'seen', 'failed', 'subscribed', 'unsubscribed', 'conversation_started']
                      },
                      json: true 
    };

    // request to the chat api of viber.
    request(options, function(error, res, body) {
        if (error) 
            throw new Error(error);
        response.write("The status message received for set Webhook request is - " + body.status_message);
        response.end();
    });
});

Now after setting up a webhook url, the messages sent to our account will come as post requests to this webhook url.

Let’s work with some of the events, for which Viber callbacks our webhook url.

First, let’s talk about the message event request.

According to Viber, this will be the body of the request:

{
    "event": "message",
    "timestamp": 1457764197627,
    "message_token": 4912661846655238145,
    "sender": {
        "id": "01234567890A=",
        "name": "John McClane",
        "avatar": "http://avatar.example.com",
        "country": "UK",
        "language": "en",
        "api_version": 1
    },
    "message": {
       "type": "text",
       "text": "a message to the service",
       "media": "http://example.com",
       "location": {
          "lat": 50.76891,
          "lon": 6.11499
       },
       "tracking_data": "tracking data"
    }
}

We check if the value of ‘event’ key is equal to ‘message’:

app.post('/', function(req, response) {
    response.writeHead(200);

    // If user sends a message in 1-on-1 chat to the susi public account
    if(req.body.event === 'message'){

If it is, then the body of if block can be populated with the code to handle messages by the user.

Assuming we have a reply by Susi API in the ans variable. We can include this code further:

// setting options to request the chat api of viber.
    var options1 = {
                       method: 'POST',
                       url: 'https://chatapi.viber.com/pa/send_message',
                       headers: headerBody,
                       body: 
                            {
                                receiver: req.body.sender.id,
                                min_api_version: 1,
                                sender: 
                                       {
                                           name: 'Susi',
                                           avatar: '' 
                                       },
                                tracking_data: 'tracking data',
                                type: 'text',
                                text: ans 
                   },
                   json: true 
   };

// request to the chat api of viber.
request(options1, function (error1, res, body1) {
    if (error1)
        throw new Error(error1);
    console.log(body1);
});

This above code can help us send a response to the user. To send a message to the user, our body object in options1 must be similar to:

{
    "receiver": "01234567890A=",
    "min_api_version": 1,
    "sender": {
       "name": "John McClane",
       "avatar": "http://avatar.example.com"
    },
    "tracking_data": "tracking data",
    "type": "text",
    "text": "a message from pa"
}

receiver key should have a value of the id of the user to which message needs to sent, which will be shown on our account interface. The user id can be easily fetched from request by ‘req.body.sender.id’ as the reply is to be sent to the same user from where we received the message.

The sender key’s value is an object which indicates the sender’s name, which in our case is Susi. Also we can pass an avatar url along with the name.

The text key must have our answer as the value i.e. the ‘ans’ variable (in this case).

The whole code to accept the request, and reply accordingly:

// If user sends a message in 1-on-1 chat to the susi public account
if(req.body.event === 'message'){
    // Susi answer to a user message
    var ans;
    // setting options to request susi bot.
    var options1 = { 
                       method: 'GET',
                       url:'http://api.asksusi.com/susi/chat.json',
                       qs: { 
                            timezoneOffset: '-330',
                            q:req.body.message.text 
                           }
    };

    // A request to the Susi bot
    request(options1, function (error1, response1, body1) {
        if (error1)
            throw new Error(error1);
        // answer fetched from susi
        ans = (JSON.parse(body1)).answers[0].actions[0].expression;
        var options = {
                          method: 'POST',
                          url:'https://chatapi.viber.com/pa/send_message',
                          headers: headerBody,
                          body: 
                               {
                               receiver: req.body.sender.id,
                               min_api_version: 1,
                               sender: 
                                      {
                                           name: 'Susi',
                                           avatar: '' 
                                      },
                               tracking_data:'tracking data',
                               type: 'text',
                               text: ans 
                      },
        json: true 
    };

    // request to the chat api of viber.
    request(options, function (error, res, body) {
        if (error)
            throw new Error(error);
        console.log(body);
    });
}

The same way we can handle “conversation started” type of event:

if(req.body.event === 'conversation_started'){
    // Welcome Message
    var request = require("request");
    var options = {
                      method: 'POST',
                      url:'https://chatapi.viber.com/pa/send_message',
                      headers: headerBody,
                      body: 
                          {
                              receiver: req.body.user.id,
                              min_api_version: 1,
                              sender: 
                                    {
                                        name: 'Susi',
                                        avatar: '' 
                                    },
                              tracking_data: 'tracking data',
                              type: 'text',
                              text:'Hi from your favourite, Susi!' 
                          },
                      json: true 
    };

    request(options, function (error, res, body) {
        if (error)
            throw new Error(error);
        console.log(body);
    });
}

This way our Susi AI chatbot can reply to messages from Viber.

The repository which contains whole project of Susi AI’s integration to Viber can be found here.

Continue ReadingDeploy SUSI AI to Viber messenger

How to teach SUSI.AI skills using external API’s

A powerful feature of SUSI is, that it can use external API’s to learn new skills. The generic syntax used here is:

Question string
!console:constant answer string + answer variable
{
 "url" : "API to be called",
 "path" : "path from where answer will be fetched"
}
eol

I will try to explain this syntax with the help of some useful examples. Let’s start with a very basic example:

I want SUSI to be able to answer questions like “What is the date today?”.

Let’s try to tackle this step by step. As we can infer from the above-written syntax, to teach SUSI a skill involving external API call, we need to be clear about five things namely:

  1. Question string i.e. “What is the date today?” (in this case).
  2. Constant answer string i.e. “The date today is ”
  3. The API to be called i.e. “http://calapi.inadiutorium.cz/api/v0/en/calendars/default/today
  4. The path which contains our answer.

When we visit this API url, we get the result as follows:

{
  "date":"2017-05-16",
  "season":"easter",
  "season_week":5,
  "celebrations":[
    {
      "title":"",
      "colour":"white",
      "rank":"ferial",
      "rank_num":3.13
    }
  ],
  "weekday":"tuesday"
}

The whole JSON object is represented with the ‘$’ sign. As date is a property of this object, so date can be accessed with “$.date” – this string is referred to as the path.

  1.  The last one is the answer variable.

We can see that the result of API url contains many “key:value” pairs. Answer variable is the value of the last key variable(i.e. date) referred in path string. This value is stored in a variable named $object$.

So our answer variable turns out to be $object$.   

Now, as we have all the five things ready with us, we can make our SUSI skill:

What is the date today?
!console:$object$
{
  “url”:“http://calapi.inadiutorium.cz/api/v0/en/calendars/default/today”,
  “path” : “$.date”
}
eol

Kudos! But where to feed this skill and check if SUSI chat bot is able to answer “What is the date today?” appropriately.

To test the working of a skill:

  1. Open dream.asksusi.com, write whatever name you like for the pad and then click OK.

  2. Replace the data written on your pad with the skill code you created. You don’t need to save it, it is saved automatically. Now your page should look something like this:
  3. To check if this skill is working properly:
  • Visit SUSI chat bot.
  • In the textbox below, write dream followed by the name of your pad and then press Enter key. SUSI will reply with “dreaming enabled for YOUR-PAD-NAME”.
  • Now write the question string i.e. What is the date today? and you should be shown today’s date!

For more clarity, refer to this image:


Great, that you made it! You can now contribute skills by making a PR to
this repository and see those skills live on SUSI without enabling any dream! Just ask your question and get your own skilled answers.

Let’s learn more about skills by introducing some changes to this question. Let’s go through some variations of this question:

  • We want SUSI to answer the same when we ask “What is the date today?” or “today’s date”. To achieve this we can use ‘|’ symbol when writing our question.

The new syntax of our skill will be:

What is the date today? | today’s date?
!console:$object$
{
“url” : “http://calapi.inadiutorium.cz/api/v0/en/calendars/default/today”,
“path” : “$.date”
}
eol
  • We want SUSI to answer according to the question. To make it answer all the questions like today’s date?, tomorrow’s date? or yesterday’s date?

The new syntax of our skill will be:

*’s date?
!console:$object$
{
“url” : “http://calapi.inadiutorium.cz/api/v0/en/calendars/default/$1$”,
“path” : “$.date”
}
eol

Here * acts as a wildcard character. That means * will be “today” in “today’s date” and “tomorrow” in “tomorrow’s date”. $1$ is the variable which stores the value in *.

Let’s dive into more examples:

  1. Sometimes we may need 2 wildcard characters in our question:
* plot of * | * summary of *
!console:$object$
{
  "url":"http://api.tvmaze.com/singlesearch/shows?q=$2$",
  "path":"$.summary"
}
eol   

The api used above is to tell the plot of a tv show. We need to query this API with the name of the show.

For questions like “Tell me the plot of Game of Thrones” or “What is the plot of Game of Thrones”, we want to ignore the string before “plot of” and store the string after it. This string stored can be used to query the API later.

The naming of the variables for storing the values in * is done number wise. The value of the first * in the question is stored in $1$, for the second * it is in $2$ and so on…

Now the above-written skill should make sense to everyone. Let’s see the skill in action:

 

  1. What if we want two answers from the same API. Consider this question:
    We have a public API to check the details of a space agency. We need the abbreviation of the space agency and append that to the API.

For example, when we visit https://launchlibrary.net/1.2/agency/ISRO, we get the following as output:

We want SUSI to answer the full form of a space agency along with its country code.

The skill used for it:

what is the full form of * and its country code?
!console:Full form - $name$, Country code - $countryCode$
{
  "url":"https://launchlibrary.net/1.2/agency/$1$",
  "path":"$.agencies[0]"
}
eol

How this skill works?

Let’s breakdown the path variable and check what does it leads to. The ‘$’ will fetch the whole object.

Further, “$.agencies[0]” will fetch this -:

{
  "Id":31,
  "name":"Indian Space  Research Organization",
  "countryCode":"IND",
  "abbrev":"ISRO",
  "Type":1,
  "infoURL":"http:\/\/www.isro.org\/",
 "wikiURL":"http:\/\/en.wikipedia.org\/wiki\/Indian_Space_Research_Organiation",
  "infoURLs":["http:\/\/www.isro.org\/"]
}

To fetch values of any of the keys, we can use the key name enclosed in ‘$KEY_NAME$’. The  value of that key will be automatically stored in this variable i.e. $KEY_NAME$.

Hence we use $name$ and $countryCode$ in our skill, to get the required answer.

The skill in action:

The same way we can use other API’s and contribute new skills to SUSI. To help you get started, see the public API’s repository available here. As said before, you can contribute skills by making a PR to this repository and see those skills live in SUSI!

Continue ReadingHow to teach SUSI.AI skills using external API’s