Building a sentiment analysis bot with IBM Watson and Raspberry Pi

Tomomi Imura
Jun 12, 2017 · 10 min read

Updated on May 30, 2019:
Hey! I have updated this article after two years. Go to the Tutorials section on Slack API Docs for the latest article. Thank you!


Do you ever wonder how your teammates perceive your Slack chat messages? Maybe you sound heroic! Or unpleasant. Perhaps you project harsh undertones you haven’t even noticed!

Imagine a helpful robot that analyzes your written communications. Now — stop imagining, because I created this (physical!) bot that analyzes message tone using IBM Watson, “reading” the emotion of each posted message!

In this article, I’ll share how I created the bot.

Watson TJbot demo on Vimeo

First I’ll show you how to use the Events API to capture messages, send them to IBM Watson for analysis, and then post the result using chat.postMessage.

Then I will present a totally optional but fun exercise! We will port our bot to a Raspberry Pi and reflect emotions using colors produced by an RGB LED.

🐙🐱 The source code is available on GitHub.

Before Starting This Project

In addition to the software bot, if you want to build a physical Raspberry Pi bot that shows the results with color LEDs, you’ll need some affordable hack-friendly hardware. You can either buy the IBM TJBot Kit that includes the cardboard robot, or prepare separately:

You need to install the latest Raspbian OS, connect to WiFi, and update and upgrade the system. See Raspberrypi.org for additional instruction.

Building a Sentiment Analysis Bot

Our bot’s workflow:

  1. Read each message on a channel
  2. Send the message to IBM Watson for examination
  3. If the likelihood of an emotion is above the given confidence threshold, post the most prominent emotion to the Slack channel

Creating and Configuring Your Slack App

Click Create App when finished.

Next, you need to configure your App.

At Add features and functionality, enable “Bots”, “Event Subscription”, and “Permissions”. We will walk through them soon.

Also, you will need your API credentials you can find at the App Credentials section when you code. They identify your app to the Slack platform.

Setting up a Bot User

Make sure to click the Save Changes every time you make a change in this configuration process.

Setting OAuth & Permissions

You will need the pre-generated OAuth Access Token, beginning with xoxb- during development. (Note: To distribute your bot to the rest of the world, you will need to set up an OAuth button, etc, however, this tutorial does not cover the process.)

Scroll down to Permission Scopes section to add the scopes you need to use for the bot. At the pulldown menu, choose bot:

Setting Event Subscriptions

Scroll down to Subscribe to Bot Events. Click the Add Bot User Event button, and select message.channels:

Setting up your Request URL

Once you install it, run it on terminal:

$ ngrok http 5000

The ngrok tool generates a URL looking like http://618830b2.ngrok.io for your local server at localhost:5000. Copy the ngrok URL and paste it into the configuration setup, but not so fast! The URL must be validated, and in order to do that you’ll need to finally write some code. We will come back here later.

Running an Express Server

First, set up your node application:

$ npm init

And configure your application. Then install Express.JS and body-parser (for POST):

$ npm install express body-parser --save

Once finished installing these modules, create a index.js file, and instantiate the express server, listening on port 5000. Since you’ve set ngrok to localhost:5000, you must use the same port!

'use strict';const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const server = app.listen(5000, () => {
console.log('Express server listening on port %d in %s mode', server.address().port, app.settings.env);});

Now, create HTTP POST route to handle the validation code. Let’s create an /events route. This will be triggered every time an event arrives, like the message.channels event we want from public Slack channels.

When you enter the webhook URL on your configuration page, Slack will send a request with a challenge parameter, so your endpoint must respond with the challenge value to complete the handshake.

app.post('/events', (req, res) => {
if (req.body.type === 'url_verification') {
res.send(req.body.challenge);
}
// will implement the bot here ...
});

Now, let’s run this node script.

Then (1) go back to the Event Subscriptions, and (2) enter your ngrok URL with the route, for instance, http://618830b2.ngrok.io/evens. If everything works fine, the Request URL will verify, and you should see a green checkmark like this:

Handling event subscriptions

Go back to your index.js file and at the /events POST route you defined, we’ll receive and handle the message payload.

Within the request handler, you need to check the payload to see (1) if the token value matches with your verification token (which you can find at the App Credentials section of the Basic Information), and (2) if the type value is the event_callback, extract the message text to be analyzed by Watson later:

app.post('/events', (req, res) => {
let q = req.body;
// 1. To see if the request is coming from Slack
if (q.token !== process.env.SLACK_VERIFICATION_TOKEN) {
res.sendStatus(400);
return;
}
// 2. Events - get the message text else if (q.type === 'event_callback') {
if(!q.event.text) return;
analyzeTone(q.event); // sentiment analysis
}
});

If you just want to run the code locally, you may hard-code your token here. There are multiple ways to set your environment variables, but I usually set a .env file to include the variables. In the source code, you can take a look at the .env-test file to see how it is set.

Now, create a function, analyzeTone, which take the message text to be analyzed and some other info from the payload.

Using the Sentiment Analysis with Watson

Go to your Bluemix console, and choose Tone Analyzer from the catalog under Watson.

At the next screen, click Create.

Once you activate the Tone Analyzer, (1) click the Service Credentials from the left menu, and you should be able to obtain your username and password for this specific service by (2) clicking the little triangle by “View credentials”.

To use the Watson Node.js library, install the watson-developer-cloud module from npm:

$ npm install watson-developer-cloud --save

Then instantiate it with your credentials:

const watson = require('watson-developer-cloud');
let tone_analyzer = watson.tone_analyzer({
username: process.env.WATSON_TONE_ANALYSIS_USERNAME,
password: process.env.WATSON_TONE_ANALYSIS_PASSWORD,
version: 'v3',
version_date: '2016-05-19'
});

Now, you sent the Slack message text to the Tone Analysis:

const confidencethreshold = 0.55; 
tone_analyzer.tone({text: text}, (err, tone) => {
tone.document_tone.tone_categories.forEach((tonecategory) => {
if(tonecategory.category_id === 'emotion_tone'){
console.log(tonecategory.tones);
tonecategory.tones.forEach((emotion) => {
if(emotion.score >= confidencethreshold) {
postEmotion(emotion, ev)
}
})
}
})
});

Watson can analyze a lot more from a given text, but for this project we are only using the emotion analysis. The results will be returned as a JSON that provides a hierarchical representation of the analysis. Each emotion has an index, so define the confidence threshold (let’s set it 0.55 for now. You can adjust it later!) and use only the emotion the exceeded the threshold value! Learn more about the watson-developer-cloud module on the GitHub repo.

Posting a Sentiment back on a Channel

To post a message, use chat.postMessage API. This call requires values for the token, channel, and text. The token should come from an authentication of a user with the OAuth, however, during the development, use the token that begins with xoxb- that you can find at the OAuth & Permissions section.

function postEmotion(emotion, ev) { 
let message = 'feeling ' + emotion.tone_id;
let options = {
method: 'POST',
uri: 'https://slack.com/api/chat.postMessage',
form: {
token: 'xoxb-.....', // Your Slack OAuth token
channel: ev.channel,
text: message,
as_user: false,
username: 'Watson Bot'
}
};
// Use Request module to POST
request(options, (error, response, body) => {
if (error) { console.log(error) }
});
}

Yipee, your bot should be working now! Let’s run your node code. Add the “sentiment_analysis” bot to your Slack channel, and try posting some emotional messages!

The code samples used here are simplified to fit in the tutorial, however, if you wish to include the teammates’ usernames in the result messages as seen in this screenshot, view the entire source code on the GitHub repo!

You can end the project right here, or proceed this tutorial for more fun with hardware!

Making the Bot into a Physical Bot with Raspberry Pi!

Installing Node.js on Pi

Then you can either directly work on the terminal on Pi, or SSH into the Pi from your computer, and install Node.js for Linux ARM:

$ curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -$ sudo apt-get install -y nodejs

Installing ngrok on Pi

$ cd /tmp$ wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-arm.zip$ unzip ngrok*$ sudo mv ngrok /usr/bin/

SSH into the Pi to copy your index.js and all the module dependencies over to the Pi from your computer. Run the index.js, and now you should be able to tunnel requests to your Pi, just like you did before on more expensive computer:

$ ngrok http 5000

This gives you a new ngrok forwarding URL, so don’t forget to update the Request URL at the Event Subscription on your Slack App setting page!

Try posting some message on Slack and see if it works.

Now, let’s work on hardware!

Wiring Up the Pi and an LED

Light Up an Emotion!

First, install the rpi-ws281x-native via npm:

$ npm install rpi-ws281x-native --save

Then write the code to turn the LED on. Let’s define the colors too:

const ws281x = require('rpi-ws281x-native');
const NUM_LEDS = 1;
ws281x.init(NUM_LEDS);
const color = new Uint32Array(NUM_LEDS);
process.on('SIGINT', () => {
ws281x.reset();
process.nextTick(() => { process.exit(0); });
});
const red = 0x00ff00;
const green = 0xff0000;
const blue = 0x0000ff;
const yellow = 0xffff00;
const purple = 0x00ffff;
// Show a specific color for each emotion
function colorEmotion(emotion) {
if (emotion.tone_id === 'anger') {
setLED(red);
} else if(emotion.tone_id === 'joy') {
setLED(yellow);
} else if(emotion.tone_id === 'fear') {
setLED(purple);
} else if(emotion.tone_id === 'disgust') {
setLED(green);
} else if(emotion.tone_id === 'sadness') {
setLED(blue);
}
}
// Set the LED to the given color value
function setLED(colorval){
color[0] = colorval ;
ws281x.render(color);
}

Now, call the colorEmotion function at where you are calling the postEmotion().

Run the Node code, type some message on Slack and see the LED lights up to match your feelings. Hooray!

I hope you enjoyed the tutorial! If you have questions and suggestions, please leave comments. Also, when you build an interesting apps, do let us know!


Learn More

Related Slack API Documentation

Slack Platform Blog

Several bots are typing…

Tomomi Imura

Written by

👩‍💻 Open Web & Tech Advocate. 🍩 Creative Technologist. 🦄 All things JavaScript. 🐱-as-a-Service. 🥑 Developer Advocate at Slack.

Slack Platform Blog

Several bots are typing…

Tomomi Imura

Written by

👩‍💻 Open Web & Tech Advocate. 🍩 Creative Technologist. 🦄 All things JavaScript. 🐱-as-a-Service. 🥑 Developer Advocate at Slack.

Slack Platform Blog

Several bots are typing…

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store