Hubot + ES6 + Promises

Kees C. Bakker
wehkamp-techblog
Published in
3 min readJan 19, 2018

Lately we’ve been playing around with ChatOps at Wehkamp. We added a Hubot to our Slack channels to automate some operational jobs. It makes work more fun and way easier. As it is hosted in our own infrastructure, it can interact with our micro-services.

Consuming an API-endpoint is easy with Hubot. In this article I’ll explore how by responding to the word “Norris” with a quote from the Internet Chuck Norris Database.

I will demonstrate how to:

  • use JavaScript instead of CoffeeScript
  • consume a simple JSON web-service endpoint
  • leverage the ES6-style Promise to build a nice chain.

Building stuff for Hubot is really easy!!

I like to drink my coffee, instead of scripting it

I’m sure CoffeeScript has it’s uses, but it’s not for me. I like to do my programming in standard ES6 JavaScript. Fortunately Hubot understands .js scripts just as well as .coffee scripts.

Hubot provides it’s own HTTP client to talk web-services. Let’s wrap a GET request with a an ES6 Pomise using function arrow expressions and convert the result to a JSON object:

const norrisUrl = 'https://api.icndb.com/jokes/random';// wrap with promise
new Promise((resolve, reject) =>
robot.http(norrisUrl).get()((err, response, body) =>
err ? reject(err) : resolve(body)
)
)
// parse to js object
.then(body => JSON.parse(body))

Promises can be used to make asynchronous programming easier. They can be chained together, creating a neat pipeline. Another advantage is error handling. We can add a .catch(err => { }) at the end of the chain to catch any errors the might occur during the processing of the endpoint data.

Let Hubot listen in

Hubot uses a regular expression to “hear” commands. In this case we want to respond to phrases that contain the word Norris. The following regular expression will do so: /Norris/i. This expression will respond to “norris”, “Norris” and even “noRRis” due to the i-option.

The response (res) object can be used to echo a reply back to the channel. Here is the code:

const
norrisUrl = 'https://api.icndb.com/jokes/random',
decode = require('decode-html');
//the export is used to init the bot
module.exports = (robot) => {
//listen to phrases that contain "Norris"
robot.hear(/Norris/i, (res) => {
//wrap the HTTP get call as a Promise
new Promise((resolve, reject) =>
robot.http(norrisUrl).get()((err, response, body) =>
err ? reject(err) : resolve(body)
)
)

//parse to js object
.then(body => JSON.parse(body))
//get joke - jokes may have stuff like "
.then(json => decode(json.value.joke))
//reply joke
.then(joke => res.reply(joke))
//problems? Annoy the user with the problem
.catch(err => res.reply('Not even Chuck Norris can deal with this one: ' + err));
});
};

The promise allows us to build a nice chain of plugable operations. It feels a bit like overkill for this example as the JSON parsing, decoding and replying can be done in 1 then. Yet it has a nice “ring” of separation of concern to it.

And action!

When I run the bot, it start responding to my phrases:

Super fun. Super simple.

PS. Be careful with hearing too much
When you have 2 bots using hear on the same topic and respond with a hear keyword, you might get into trouble. I had to kick 2 bots discussing Chuck Norris facts:

Originally published at keestalkstech.com on January 19, 2018.

--

--

Kees C. Bakker
wehkamp-techblog

I work as a Lead Developer for one of the biggest web-shops in the Netherlands: wehkamp. I ❤️ C# and I like to solve nifty problems.