In my job we have a meeting every wednesday where two people on the team give a lightning talk about absolutely anything in a random coffee shop nearby. But the problem we were facing was that every time we took ages to decide where to go. So one day I suggest that I could create an app to help us to decide the place giving options nearby. With this in mind I started coding a simple web app, but when I commented how it was going with a friend he said:

- But you know what would be cool ? If our chat bot could do it.

Damn. He was right. So there I was, looking at the web app that was cool but not so cool as the ... coffee shop guide bot. Then I thought:

- Well, let's do it.

Now I will give you a step by step guide of how you can create a simplified script to search coffee shops nearby and in the end I will show the complete plugin. Hold my beer. Actually, hold my coffee !

Hubot

Hubot is a very friendly and easy extensible bot written in Coffeescript by the Github guys and before you start to yelling at me:

- But I hate Coffeescript !

Calm down. We can use normal JS here, I didn't saw any es6 and transpiler setup around but we are ok. (I have nothing against CS, in fact I used it a lot)

Running a bot locally

The first thing I encourage you to do is to download the Hubot and run it in your machine, just to see it working. You will need to install Yeoman first, to do it you run:

npm install -g yo

Now that you got Yeoman in your machine, let's execute this steps to install the Hubot:

npm install -g yo generator-hubot
mkdir mybot
cd mybot
yo hubot

The generator will ask you a feel questions like the bot's name. You should see something like this in your screen:

Hubot configuration

Cool, after some ASCII art let's see if our bot is working. Go ahead and type in you bot directory:

bin/hubot

You will see your bot starting, like this:

Hubot starting

Poking around

Now I hope you are prepared to this:

Mind = blown. I know. Even it looks like the something dumb let's look how we can replicate it. Why ? Because one of the best ways to learn something is pick the simplest case possible :)

If you take a look at the /scripts/example.coffee you will see how it's done. Basically we can replicate it with something this: (I will use JS here, you can rename your file extension to .js to use it too)

module.exports = function (robot) {
  robot.respond(/ping/i, function (res) {
    res.send('PONG')
  })
}

Easy right ? Now go and change the "ping" to make the bot respond something different to you, like a coffee joke or something. If you want the bot responds only direct you use .respond, but if you want to your bot to hear to whatever anybody says in chat room you can use .hear instead.

To capture something that someone said could be like this:

  robot.respond(/open the (.*) doors/i, function (res) {
    door = res.match[1]
    res.send('Well, that\'s a nice type of door, don\'t know if I can open it.')
  })

It's just regex right ? Pretty simple. You can see more examples in the /scripts/example.coffee file that I mentioned earlier. But now that you have an idea of how to code in the Hubot, let's get fancy.

The neohipster coffee bot

Foursquare

Since we want that our bot to suggest for us places near to drink a coffee we need an api that have this type of information, let's use the Foursquare app here. Take a look at https://developer.foursquare.com/ to register and create an app to get a CLIENT_ID and a CLIENT_SECRET, we will need it.

Now that you have your dev credentials with our fellow Foursquare let's add to our project the node-foursquare-venues npm package:

npm install node-foursquare-venues --save

* In the github page you can see more about the package usage, I will just cover here what we need to know to our bot.

Plus the dotenv and lodash too:

npm install dotenv --save-dev
npm install lodash --save

Create a .env file in the project root with a content like this:

FOURSQUARE_CLIENT_ID = <INSERT YOU CLIENT ID HERE>
FOURSQUARE_CLIENT_SECRET = <INSERT YOU CLIENT SECRET HERE>

Showing coffee shops nearby

Now let's create a feature on our bot show coffee shops near a place that the user wants editing the scripts/example.js with this:

if (process.env.NODE_ENV !== 'production') { require('dotenv').load() }

var _ = require('lodash')
var foursquare = require('node-foursquare-venues')(process.env.FOURSQUARE_CLIENT_ID,
                                                   process.env.FOURSQUARE_CLIENT_SECRET)

module.exports = function (bot) {
  bot.respond(/coffee near (.*)/i, function (res) {
    var params = {
      near: res.match[1],
      categoryId: '4bf58dd8d48988d1e0931735',
      radius: 1000
    }

    return foursquare.venues.search(params, function (error, payload) {
      if (error) return res.send(error)

      var message = '\nHello ! I\'m the neohipster coffee bot. You have this coffee shops nearby:\n\n'

      var coffeeShops = _.sample(payload.response.venues, 3)
      var url = 'https://www.foursquare.com/v/'

      for (var i = 0; i < coffeeShops.length; i++) {
        var cs = coffeeShops[i]
        message += i + ': ' + cs.name + ' (' + url + cs.id + ')\n'
        message += cs.location.address + '\n\n'
      }

      return res.send(message)
    })
  })
}

Breaking down the code, we are first configuring the params of our request to foursquare, sending the place that we wanna search, the category of the Venue (in our case we want coffee shops) and a radius to consider:

...
var params = {
  near: res.match[1],
  categoryId: '4bf58dd8d48988d1e0931735',
  radius: 1000
}
...

Now, we will send a request to Foursquare, after that we will grab the response and concat with a friendly message of our bot to show the user the coffee shops that he got:

...
return foursquare.venues.search(params, function (error, payload) {
  if (error) return res.send(error)

  var message = '\nHello ! I\'m the neohipster coffee bot. You have this coffee shops nearby:\n\n'

  var coffeeShops = _.sample(payload.response.venues, 3)
  var url = 'https://www.foursquare.com/v/'

  for (var i = 0; i < coffeeShops.length; i++) {
    var cs = coffeeShops[i]
    message += i + ': ' + cs.name + ' (' + url + cs.id + ')\n'
    message += cs.location.address + '\n\n'
  }

  return res.send(message)
})
...

Now let's see it running !

The Plugin

Well, I did not stop here. Actually I wrote a Hubot plugin called hubot-coffeepoll that not only suggest you places to drink coffee but create a configurable poll where people in the chat can vote and the bot announces the winner. I know, this is madness ! Let me show you the plugin working in a Slack chat:

Configuring a poll

Starting a poll

Voting

Partial results

Announcing the winner

You can see the repo with the plugin structure, tests and complete code at //github.com/wallacyyy/hubot-coffeepoll. I hope you liked this bot journey, was very fun for me ! Thanks for reading and see you soon :)