May 15th, 2015 by Josh

Building a Slack slash command with Sinatra, Finch and Heroku

Slack is awesome. We use it on a daily basis to keep in touch with pretty much everyone. With all the positive press it's been having, we thought we'd see if we could create a simple bot which would respond to slash commands.

Overview

For the purpose of this post, we're going to write a bot which responds to questions about our Hey! events. Ideally, it will respond to the following sub-commands:

  • /hey when - When is the next event
  • /hey what - Who is speaking at the next event, and where can I find out more information on each talk
  • /hey unsupportedcommand - Respond to missing commands with a friendly error message

Tools

To achieve this we're going to use a few free tools:

  • Sinatra - A Ruby DSL for creating web applications
  • Finch - A great tool for exposing local sites to the Internet
  • Heroku - Cloud hosting platform, deployed to via Git

You can find the Sinatra code for this post on GitHub.

Steps

Firstly we're going to write a simple Sinatra application to respond to Slack requests. At its most basic, it will receive a question from Slack which it needs to then return a plain text response.

We'll create a Gemfile to tell Ruby about our dependencies:

source 'https://rubygems.org'

gem 'rack'
gem 'sinatra'
gem 'foreman'

We've also added Foreman here which we'll want to use when we deploy the application to Heroku.

Next up we'll write a basic version of our application, we'll put the contents into a file called config.ru (a Rack config file).

require 'sinatra'

post '/' do
  text = params.fetch('text').strip

  case text
  when 'when'
    'TODO'
  when 'what'
    'TODO'
  end
end

run Sinatra::Application

Right now, this takes the incoming text parameter (the command typed by the user that Slack sends), and responds with some placeholder text depending on the sub-command.

Now let's see if we can get Slack talking to this application. Run the application with the following command from the project directory:

bundle exec rackup config.ru

Assuming everything has worked correctly, you should see something like:

[2015-05-14 17:41:14] INFO  WEBrick 1.3.1
[2015-05-14 17:41:14] INFO  ruby 2.1.1 (2014-02-24) [x86_64-darwin14.0]
[2015-05-14 17:41:14] INFO  WEBrick::HTTPServer#start: pid=57001 port=9292

This is where Finch comes in. We need to expose this application to the Internet so Slack can talk to it.

Once Finch is installed, we'll tell it to forward your local application to an external URL which we can give to Slack:

> finch forward localhost:9292

→ Requesting connection... ✔
→ Establishing secure connection... ✔

The following sites are now being forwarded. Press CTRL+C at any
time to end your session:

--------------------------------------------------------------
| Public URL                         | Private URL           |
--------------------------------------------------------------
| https://cream-uneven.usefinch.io   | http://localhost:9292 |
--------------------------------------------------------------

Great, we're all set to configure Slack. Under your own Slack account, visit /services/new/slash-commands to add a new command, and configure it as below.

Setting up a Slack integration

When that's all saved, you should be able to test out your slash command integration! Try typing the following into any slack channel:

/hey when

And you should get back TODO. It works! Let's improve our application a little further.

require 'sinatra'

InvalidTokenError = Class.new(Exception)

post '/' do
  raise(InvalidTokenError) unless params[:token] == ENV['SLACK_TOKEN']

  user = params.fetch('user_name')
  text = params.fetch('text').strip

  case text
  when 'when'

    <<-TEXT
The next Hey! event will be held on the 20th May from 7:30pm at The Belgrave in central Leeds.

Hopefully see you then #{user}!

https://heyst.ac/
TEXT

  when 'what'

    <<-TEXT
The next Hey! event has two lectures planned. The first one is with Rich Fiddaman discussing everything hospitality. The second is with Matt Dix discussing Leeds Indie Food Festival.

https://heyst.ac/lectures/a-pint-with-the-pub-landlord

https://heyst.ac/lectures/kickstarting-a-city-wide-food-festival
TEXT

  else

    'Unknown command :cry:'

  end
end

run Sinatra::Application

We've added a few things here:

  • Some real copy for the when and what sub-commands
  • We're raising a custom error if the request isn't coming from Slack. We can verify this by checking the request contains a private token that only you and Slack know about
  • We're also dealing with missing commands by falling back to a friendlier error message

Now when you type /hey when you should see something like this:

Working slash command

And if you mistype a command:

Missing command

Deploying to Heroku

Now we've got our application working and tested, we need to push it to Heroku. First create a Procfile so Heroku knows how to run the application:

echo 'web: rackup config.ru' > Procfile

Then add everything to Git and push it:

git init
git add -A
git commit -m "Initial commit"
heroku create
git push heroku

The heroku create command should return the URL your application is available at. Now your application is hosted! All that's left to do is change the URL in the Slack slash command settings and you're good to go.

Future Improvements

A few potential improvements to this application could be:

  • Remove the hard-coded event information. This would ideally pull from a Hey! API
  • Embed more information about the events in the what command
  • Add another sub-command to subscribe to the Hey! mailing list directly from Slack

We hope you've found this brief tutorial useful, let us know how you're using slash commands in the comments.