Building a Slackbot with Ruby and Sinatra
This article was peer reviewed by Thom Parkin. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!
Slack seems to be everywhere these days. It is enjoying the kind of success and adoption that other services can only dream about. Part of the reason for its success is how easy it is to integrate Slack with other services. There are tons of integrations out-of-the-box, along with web hooks and other integration methods that make using Slack with your favorite service a breeze.
Another way Slack offers extensibility is “Slackbots”. Slackbots are items that can be configured to fire based on a keyword typed into Slack.
Today we’re going to see how to build a custom Slackbot that triggers on a keyword, performs some action, and responds back to Slack. We’re gonna build it using Ruby and the Sinatra web framework. There’s no database involved in this tutorial, but you could extend it and add one if you so desire. This is a basic tutorial that gives you a taste of building a Slack extension. We’re making a brick here with which you can build other things.
Let’s get started.
Our bot, let’s call it giubot
, will provide a set of commands to get details from GitHub. We’ll make the trigger word as giubot:
so an example would be,
Message:-
giubot: issues _ twbs/bootstrap
Response:-
There are currently 433 open issues on twbs/bootstrap.
We’re going to use the official GitHub API. Documentation for the API is available here.
Let’s setup the project files first.
Project Setup
Create a folder for the project and create the below files into like so:
giubot
|
|---- app.rb
|---- Gemfile
That’s it. It’s a simple app where all the functionality goes inside app.rb. The Gemfile is where we’ll specify the dependencies for our project. Let’s also add the gem dependencies before initializing and committing the changes in git. Add the following to the Gemfile:
source 'https://rubygems.org'
gem 'sinatra'
gem 'httparty'
and run
bundle install
A new Gemfile.lock file is created inside the project directory. Regarding our dependencies, all we need is these two gems: Sinatra and httparty. httpparty is a gem that lets handle HTTP requests easily.
Once you’ve created the files, it’s time to commit the changes into git. All the steps in this tutorial regarding git are optional, but encouraged. Git is also required if you want to host the app on Heroku. Let’s initialize our repository and commit all the changes. Type the following commands from inside the project directory:
git init
git add -A
git commit -m "Add project files and gem dependencies"
While we are at it, let’s also push our repository to GitHub. It is free for public repositories, but paid for private ones. Let’s quickly create our repository using this link. Once the repository is created, Github will supply the URL which can be located on the repository page. Copy that URL and add it as a remote:
git add remote origin https://github.com/avinoth/giubot.git
We’re adding the remote URL and aliasing it as origin. Once it’s done, push the changes to GitHub:
git push origin master
After it successfully pushes, you should see your code on GitHub. We’re done with bootstrapping, let’s go ahead and write some code.
Create API endpoint
Here’s how the communication between Slack and our web app will work: Whenever the word giubot:
is mentioned in a message, Slack will send the details of the message, along with a couple of details about the Slack account, to the URL provided when the extension is configured (More on that later). The request method is a POST
with data in the body.
We’ll fetch the full message, getting the repository name and the command requested in the message, then respond with the necessary data. For this app’s purposes, all we need is a single API endpoint which Slack will call if the trigger word is mentioned, generating the response we want to be posted back into Slack. Let’s add the below lines to our app.rb file:
require 'sinatra'
require 'httparty'`
require 'json'
post '/gateway' do
message = params[:text].gsub(params[:trigger_word], '').strip
action, repo = message.split('_').map {|c| c.strip.downcase }
repo_url = "https://api.github.com/repos/#{repo}"
case action
when 'issues'
resp = HTTParty.get(repo_url)
resp = JSON.parse resp.body
respond_message "There are #{resp['open_issues_count']} open issues on #{repo}"
end
end
def respond_message message
content_type :json
{:text => message}.to_json
end
Let’s see what’s happening here. We’re requiring the only three modules we need for the app. There is a POST
request hander called /gateway
, which is going to serve all the requests from Slack. Then get the message, stripping out the trigger word. As you can see, we are making the trigger word independent of the app and making use of the one that comes along with the request, so not only giubot
but any keywords will work.
After that, take the text parameter and split it by _
, which is the separator to split the various components of the message. Having a standard separator will be helpful when you want to have more than one command, which might require more keywords.
There’s a case
statement that performs the necessary action based on the incoming command. Currently it only supports an issues
command, which fetches the current open issues count in a repository. Writing another command is just a case
statement away, so that’s nice. Inside the case
statement, make the API call to GitHub which returns the open issue count and we can build the response to post back to Slack.
Also, if you notice we’re not validating the token from Slack and simply ignoring the parameter altogether. This is not recommended, but since this is account-specific, we’re keeping it open. If you want you can validate the token by having this as the first line of the handler action:
return if params[:token] != ENV['SLACK_TOKEN']
That’s it. Our app’s functionality is done, it’s time to commit and push the changes:
git add -A
git commit -m "Add app logic"
git push origin master
Deploying the app
We will have to deploy the app to configure it in Slack and start using it. Let’s deploy it to Heroku[https://heroku.com], which is a PaaS provider for hosting web apps. Create a file called config.ru inside the project directory and add the below lines to it:
require './app'
run Sinatra::Application
This file is required by Heroku to recognize and start the app. Add the file to git and commit it:
git add config.ru
git commit -m "Add config.ru"
Install the heroku gem in your local system if you haven’t already:
gem install heroku
and type in the following commands from inside the project directory:
heroku create <APP-NAME>
heroku deploy
<APP_NAME>
can be whatever you like, or you can leave it off and Heroku will assign a name to the app. Sign in with your Heroku credentials when it asks for them. Heroku will recognize it’s a Rack application by the config file we created and build the code. We have now deployed our app to Heroku.
Configuring Slack
We’re all set from the application side, let’s configure Slack with the URL of the app we just created. Go to the Outgoing Webhook URL on Slack’s website and click on Add Outgoing Webhooks Integration
. In the Add new page, give all the details however you wish, including the app name and trigger word. In the URL box add the below URL:
http://<APP-NAME>.herokuapp.com/gateway
where <APP-NAME>
is the Heroku name for your deployed application. Save the integration.
We have successfully created our extension and configured it. Test it out by typing messages in the channels with the trigger word we’ve configured at the time of configuring the extension, and see it in action.
Please note, the message is a normal message, so it’s visible to everyone in the channel, as is the response.
Conclusion
Thusly, you now have taste of writing a Slackbot in Ruby. I hope this has your mind swimming in ideas of other Slackbots and extensiosn. The code used in this tutorial is hosted in GitHub. Feel free to fork and poke around. If you have created your own Slack extension, tell me about it in the comments below.