Slack! Hubot! Github!

At SuprNation we are using Slack as our primary communication tool. Slack is great, it has great search capabilities, allows rich messaging and most of all is able to integrate with other software components e.g. integrate Jenkins to post build updates to a slack channel. In this post we will integrate Slack with Hubot. Hubot is a virtual bot which will login to slack and provide some awesome and fun features e.g. automate deployment, language translation, integration with Google Maps, react to comments by posting an image from Imgr and so on. There are various scripts which you can add-on to hubot but the fun really starts when you create your own scripts to automate some of your own processes. Apart from our Slack integration with Hubot we will also create a plugin which integrates with GitHub to illustrate this idea.

Slack

Creating your slack team is simple - just head over to the slack homepage, click on Create New Team and follow the wizard. I have create a slack team called cloudmark with a single user cloudmark which I will use for the rest of this post. Once your team is created you should be presented with the following screen.

Now, let's create a Hubot Slack Token which will enable Hubot to integrate with Slack. From the slack window click on the team name and click Apps and Custom Integrations.

Search for Hubot,

and click Install.

Now, select a name for your bot (in this case I typed down hubot) and click on Add Hubot Integration.

You should now have your HUBOT_SLACK_TOKEN. Copy this somewhere handy, we will need this in the next step. Yes! Yes! my API key is showing! I'll change it afterwards so don't you worry pal!

Hubot

Now let's create our bot using a yeoman generator. Create an empty directory (I will use a directory named hubot) and type in the following command to download the generator.

npm install -g generator-hubot

Next, type in yo hubot. Make sure that you type in slack when the wizard asks for the Bot Adapter.

After completion you should end up with a complete bot scaffold:

To start hubot type in the following command.

HUBOT_SLACK_TOKEN=<your slack token> ./bin/hubot -a slack

Hubot is now integrated with Slack! To verify this go to your Slack window and you should see a new user named hubot logged in :bowtie:

Github Plugin

Now that have Hubot and Slack setup let's create a simple plugin which lists all Github issues on a particular repository labelled with the followup label. To test this plugin I will create a repository called hubot-plugin-test and create a couple of fake issues

To integrate with Github from Hubot we are going to make use of a wrapper library called githubot. To install this Github API wrapper type:

npm install githubot --save

Next, we need to generate a Github API Token using the following request:

curl -i https://api.github.com/authorizations -d '{"note":"githubot","scopes":["repo"]}' -u <username>

You should receive an http response containing the token value. We will pass in this token value to the hubot process.

{
  "id": 27406025,
  "url": "https://api.github.com/authorizations/27406025",
  "app": {
    "name": "githubot",
    "url": "https://developer.github.com/v3/oauth_authorizations/",
    "client_id": "00000000000000000000"
  },
  "token": "b81ba1edaa85ccac51cf53251b792bab19c23455",
  "hashed_token": "cc7bb462d9ce120a4e92c244fa8bf50ce86d5a9a4a62c4a97198e175c474385e",
  "token_last_eight": "19c23455",
  "note": "githubot",
  "note_url": null,
  "created_at": "2016-01-30T12:49:44Z",
  "updated_at": "2016-01-30T12:49:44Z",
  "scopes": [
    "repo"
  ],
  "fingerprint": null
}

Finally, we are ready to code the followup plugin. The simplest way to create a plugin is to create a coffeescript file in the scripts folder <hubot-generated-code>/scripts. The code for the followup plugin is listed below:

# Description:
#   Show open issues from a Github repository
# Commands:
#   hubot followup -- Shows all issues to follow up.
_  = require("underscore")
ASK_REGEX = /followup\s*/i

module.exports = (robot) ->
  github = require("githubot")(robot)
  issues = process.env.HUBOT_FOLLOWUP_LABELS;
  robot.respond ASK_REGEX, (msg) ->
    # Query Parameter
    query_params = state: "open", sort: "created"
    query_params.per_page=100
    query_params.labels = 'followup'

    base_url = process.env.HUBOT_GITHUB_API || 'https://api.github.com'
    github.get "#{base_url}/repos/#{process.env.HUBOT_GITHUB_REPO}/issues", query_params, (issues) ->
      if !_.isEmpty issues
        for issue in issues
          labels = ("`##{label.name}`" for label in issue.labels).join(" ")
          msg.send "> [`#{issue.number}`] *#{issue.title} #{labels}* #{issue.html_url}"
      else
        msg.send "Congratulations! Nothing to followup!"

The code is pretty self explanatory; we retrieve all the issues from the repository and reply back to the source. One important thing to note is that we can use variables passed in from the environment through the process.env object. In this case we retrieve the repository we are interested in by using process.env.HUBOT_GITHUB_REPO.

Now, restart the hubot process with the following parameters:

HUBOT_SLACK_TOKEN=<hubot_slack_token>  HUBOT_GITHUB_TOKEN=<github_api_token> HUBOT_GITHUB_USER=<github_username> HUBOT_GITHUB_REPO=<github_repo> ./bin/hubot -a slack

In my example this would translate to the following command:

HUBOT_SLACK_TOKEN=xoxb-19874712274-matxlL7HDPd1NRR81RD75MlO  HUBOT_GITHUB_TOKEN=b81ba1edaa85ccac51cf53251b792bab19c23455  HUBOT_GITHUB_USER=cloudmark  HUBOT_GITHUB_REPO=cloudmark/hubot-plugin-test ./bin/hubot -a slack

To verify that the plugin is operational type help as a direct message to hubot and verify that the command hubot followup is listed. Note that the list of commands is extracted directly from the comments.

# Commands:
#   hubot followup -- Shows all issues to follow up.

Now let use our newly created command by typing followup.

Pretty neat huh?! Alternatively we can invite hubot to a channel by typing /invite @hubot and type @hubot: followup.

Enjoy using Hubot! Just remember :smirk:

  1. A robot may not injure a human being or, through inaction, allow a human being to come to harm.
  2. A robot must obey orders given it by human beings except where such orders would conflict with the First Law.
  3. A robot must protect its own existence as long as such protection does not conflict with the First or Second Law.
Written on January 30, 2016