Bot integrations of Susi on Online Social Media: Slack

In my past few posts, I have explained the use of Susi in detail. We have come to see Susi as an intelligent chat bot cum search engine, which answers Natural Language queries, and has a large dataset to support it thanks to the various sites we scrape from, the APIs we integrate, and also, the additional services that we make (like the TwitterAnalysisService I talked about). All of these make Susi an excellent chat service.

So now the question comes up: how do we increase its reach?

This is where bot integration comes up. Services like Messenger (Facebook), Google Hangouts, Slack, Gitter etc have a large number of user chatting on their platform, but in addition, they also added an additional service of bot users. These users, when messaged about related queries, answer those queries to the user. We have recently seen a very interesting example of this, when the White House used FB Messenger Bots for people to reach out to President Obama (link). This makes users get quick and instant replies to specific queries, and also, bot integrations on these big platforms make more and more people connect with the bot and its maintainers too.

That is why we believed it would be amazing if Susi were integrated onto these platforms as a bot, so that people realise all the things it is able to do. Now, we need to implement these.

As Sudheesh must have spoken about, we are following a system of maintaining all the bots on one index.js file, all of the bots post to different routes, and we deploy this file and the npm requirements in the package.json so that all run concurrently. Keeping this in mind, I developed the Slack Bot for Susi.

I actually developed the bot both in Python and node, but we will only be using node because of the easiness of deployment. For those who wish to check out the Python code, head over here. The Slack API usage remains the same though.

The main part of our bot will be the Slack RTM (Real Time Messaging) API. It basically reads all the conversation going on, and reports every message in a specified format, like:


{
    "id": 1,
    "type": "message",
    "channel": "C024BE91L",
    "text": "Hello world"
}

There are other parameters also included, like username. More info on all the parameters can be found here.

So this is the API schema. We will be using an npm package called slackbots for implementing our bot. Slackbots gives an easy way of interfacing with the RTM API, so that we can focus on implementing the Susi API in the bot without having to worry much about the RTM. You could read Slackbots’ documentation here.

For making the bot, first go here, register your bot, and get the access token. We will need this token to make authorised requests to the API. For keeping it in a secure place, store it as an environment variable:

export SLACK_TOKEN=<access token>

Now comes the main code. Create a new node project using npm init. Once the package.json is created, execute the following commands:


npm install --save requests
npm install --save slackbots

This install the slackbots and the requests packages in our project. We will need requests to make a connection with the Susi API on http://loklak.org/api/susi.json.

Now we are all set to use slackbots and write our code. Make a new file index.js (add this to the package.json as well). Here’s the code for our slackbot.


'use strict';
/* global require, process, console */
var request = require('request');
var SlackBot = require('slackbots');
var slack_token = process.env.SLACK_TOKEN; //accessing the slack token from environment
var slack_bot = new SlackBot({
	token: slack_token, 
	name: 'susi'
});

slack_bot.on('message', function(data){
	var slackdata = data;
	var msg, channel, output, user;
	if(Object.keys(slackdata).length > 0){
		if('text' in slackdata && slackdata['username'] != 'susi'){
			msg = data['text'];
			channel = data['channel']
		}
		else {
			msg = null;
			channel = null;
		}
	}
	if(msg != null && channel !=null){
		var botid = ':' 
		if (msg.split(" ")[0] != botid){
			//do nothing
		} else{
			var apiurl = 'http://loklak.org/api/susi.json?q=' + msg;
			request(apiurl, function (error, response, body) {
				if (!error && response.statusCode === 200) {
					var data = JSON.parse(body);
					if(data.answers[0].actions.length == 1){
						var susiresponse = data.answers[0].actions[0].expression;
						slack_bot.postMessage(channel, susiresponse);
					} else if(data.answers[0].actions.length == 2 && data.answers[0].actions[1].type == "table"){
						slack_bot.postMessage(channel, data.answers[0].actions[0].expression + " (" + data.answers[0].data.length + " results)");
						for(var i = 0; i < data.answers[0].data.length; ++i){
							var response = data.answers[0].data[i];
							var ansstring = "";
							for(var resp in response){
								ansstring += (resp + ": " + response[resp] + ", ");
							}
							slack_bot.postMessage(channel, ansstring);
						}
					}
				}
			});
		}
	}
});

Let’s go over this code bit by bit. We instantiate SlackBots using our token first. Then, the line slack_bot.on('message', function(data) triggers the RTM API. We first get the message in the conversation, check if its JSON is empty or not. Also, our bot should only reply when the user asks, it should not reply to the queries of itself (because RTM continuously reads input, so even the bot’s replies come under it, so we don’t want the bot to react to its own replies lest we get an infinite loop). This check is done through:


if(Object.keys(slackdata).length > 0){
		if('text' in slackdata && slackdata['username'] != 'susi'){
			msg = data['text'];
			channel = data['channel']
		}
		else {
			msg = null;
			channel = null;
		}
	}

We also get the text message and the channel to post the message into.

Next, we check for an empty message. If there is a message, we check if the message starts with @susi: (my bot was named susi, and the bot id came from the RTM API itself, I hardcoded it). We should only query the Susi API in such a case where the message starts with @susi. And once that check is done, we query the Susi API, and the response is data.answers[0].actions[0].expression (except when it’s a table, then we use data.answers[0].data). Once we get what we need to send, we use SlackBot’s postMessage method, and post the message onto the channel using the RTM API. That’s what the rest of the code does.


if(msg != null && channel !=null){
		var botid = ':' 
		if (msg.split(" ")[0] != botid){
			//do nothing
		} else{
			var apiurl = 'http://loklak.org/api/susi.json?q=' + msg;
			request(apiurl, function (error, response, body) {
				if (!error && response.statusCode === 200) {
					var data = JSON.parse(body);
					if(data.answers[0].actions.length == 1){
						var susiresponse = data.answers[0].actions[0].expression;
						slack_bot.postMessage(channel, susiresponse);
					} else if(data.answers[0].actions.length == 2 && data.answers[0].actions[1].type == "table"){
						slack_bot.postMessage(channel, data.answers[0].actions[0].expression + " (" + data.answers[0].data.length + " results)");
						for(var i = 0; i < data.answers[0].data.length; ++i){
							var response = data.answers[0].data[i];
							var ansstring = "";
							for(var resp in response){
								ansstring += (resp + ": " + response[resp] + ", ");
							}
							slack_bot.postMessage(channel, ansstring);
						}
					}
				}
			});
		}
	}
});

This completes the bot. When you shoot it up from your terminal using node index.js, or deploy it, it will work perfectly.

This can now be used by a wide range of people, and everyone can see all that Susi can do. ๐Ÿ™‚

We are still in the process of making bots. FB, Telegram and Slack bots have been made till now, and we will be making more. Feedback, as usual, is welcome. ๐Ÿ™‚

Bot integrations of Susi on Online Social Media: Slack