A Rails-based Slack bot
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
app
bin
config
db
doc
lib
log
public
spec
tmp
vendor/assets
.babelrc
.browserlistrc
.gitignore
.postcssrc.yml
.rspec
.ruby-gemset
.ruby-version
Gemfile
Gemfile.lock
Procfile
README.md
Rakefile
config.ru
package.json
yarn.lock

README.md

Giffy

Web-based Slack application powering the following Slack commands:

  • /giffy [description]: Searches for a GIF matching [description] on Google Image Search. Dispatched to {GiffyController#search}.
  • /latex [code]: Renders [code] as a LaTeX image. Dispatched to {LaTeXController#display}.
  • /comicsans [text]: Renders [text] in Comic Sans. Dispatched to {ComicSansController#display}.

Getting Started

This application consists of a Web front-end with an "Add to Slack" button, and API endpoints for the above-listed Slash commands.

To use this application, you must have a client ID and secret from Slack, which is provided after creating an app. This information should be placed in config/environments/*/slack.yml, along with the verification token.

To use /latex you will need LaTeX installed (with pdflatex), and to use /latex or /comicsans you will need ImageMagick and an AWS S3 account. For development you will need credentials in a file at ~/.aws/credentials that looks like this:

[default]
aws_access_key_id = YOUR_AWS_ACCESS_KEY_ID
aws_secret_access_key = YOUR_AWS_SECRET_ACCESS_KEY

For production you will need to set the AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_REGION environment variables. You can set your region and bucket in the config/environemnts/*/aws.yml file.

You will also need to modify the config/secrets.yml with randomly-generated key bases, and modify the config/database.yml with your production database credentials.

This application requires Ruby 2.3 and PostgreSQL 8.0+. To get started, first run bundle install to install all required gems, then create PostgreSQL databases named giffy_development and giffy_test, both owned by a role called giffy:

createuser giffy
createdb -O giffy giffy_development
createdb -O giffy giffy_test

You should be able to run the application using rails server, and see the "Add to Slack" button. Note that due to the nature of OAuth, you will need an SSH tunnel in order to test the authorization flow against your locally-running instance.

You can also run specs with rspec spec.

Architecture

Authorization flow

The homage page ({AuthorizationRequestsController#new}) displays an "Add to Slack" button that redirects to Slack with the Slack-provided client ID for this app. Once the user logs in to Slack and authorizes the application, Slack will redirect back to {AuthorizationRequestsController#create} with an authorization code. An {AuthorizationRequest} is created with that code and an {AuthorizeJob} is spawned to retrieve an OAuth access token for that code. Meanwhile, a Vue.js-powered front-end widget displays the status of the authorization request until it is successful (or fails).

Once Slack provides this app with an access code, it is stored in an {Authorization} record, one for each Slack team that installs this app. This record is then used to interface with the Slack API on behalf of that team.

For more details, see the {AuthorizationRequestsController} class documentation.

Slack command requests

Slack commands are dispatched as HTTP requests to one of the {SlackCommandController} subclasses. The authenticity of the command is confirmed using the verification token (provided by Slack when the app is created).

A {Slack::Command} instance is then created to represent the incoming slash command. This instance loads the {Authorization} associated with the request, which is then used to interface with the Slack API as necessary to respond to the command. Slack command requests typically return instantly, to avoid being timed out by the Slack client, and the response is processed in a separate {SlackCommandJob} subclass.

Documentation

HTML documentation can be generated by running rake yard. Documentation is stored in the doc/app directory.

Slack Commands

/giffy

Uses the {Google} helper class to perform a Google image search. See {GiffyController}.

/latex

Uses the pdflatex binary to render a LaTeX math snippet to a PDF file, and then ImageMagick to convert it to an image, and then AWS S3 to upload the image. See {LaTeXController}.

/comicsans

Uses Prawn and ImageMagick to render a string in Comic Sans to an image, and then AWS S3 to upload the image. See {ComicSansController}.

API Integration

Slack

Slack API integration is done via the {Slack} singleton. A {Slack::Command} instance stores all contextual information about a slash-command invocation.

Other APIS

Google image search API integration is handled by the {Google} singleton.

API Credentials

API credentials are managed using Configoro. Most all credentials are stored in the config/environments/common directory, and you should store environment-specific versions of these credentials in the appropriate environment directory.

Adding Your Own Commands

To add your own command, first create a new slash command on the configuration page for your Slack app. Provide an endpoint such as https://your.host/COMMAND_NAME, replacing your.host and COMMAND_NAME as appropriate.

Add an entry to config/routes.rb with your COMMAND_NAME path, dispatching it to a new or existing controller/action combo as you see fit. Make sure your controller inherits from {SlackCommandController}.

Add the action to your controller. In the controller, you have access to the {SlackCommandController#command #command} method, from which you can data such as who typed the command, what channel they typed it in, etc.

To avoid timeouts (especially when interfacing with third-party APIs), create a new job (by subclassing {SlackCommandJob}) to actually process your command. Your controller action should simply spawn the job and return 200 OK immediately:

def my_command
  MyCommandJob.perform_later command.to_h
end

In your job, you can use the {Slack::Command#reply #reply} method as needed to communicate:

def perform_command(command)
  command.reply text: "Hello, world!", response_type: 'in_channel'
end

Testing

Testing slash-commands is done using RSpec. See the spec/controllers directory for examples of how to test slash-commands using specs. Run specs with rspec spec.