CKSHCommander is a Ruby gem that simplifies the task of processing Slack slash commands. It provides a class-based convention for authoring command handlers, and it supports basic subcommands and arguments. Check out the cksh_command_api repository to see a dead simple Sinatra API that comes bootstrapped with everything you need to process Slack slash commands using this gem.
You can install cksh_commander
via RubyGems. In your Gemfile:
gem 'cksh_commander'
Or from a command line:
gem install cksh_commander
Defining a custom command is easy. First, create a commands/
directory and tell
CKSHCommander
where it can find your commands. Let's say we've created a commands/
directory at the root of our project. Our configuration is as follows:
# app.rb
require 'cksh_commander'
CKSHCommander.configure do |c|
c.commands_path = File.expand_path("../commands", __FILE__)
end
Now, let's assume that we've set up an /example
Slack command. We want to create
a directory-based container for everything that our command might interact
with. (For example, we may wish to log some command-specific data in a text file.)
All that we have to do is to create a directory under our commands/
directory
that matches the name of the slash command. In this case:
root/
app.rb
commands/
example/
command.rb
...
The last step is to create our custom command class. We name this class Command
to
abide by the convention. We define our class as follows. Note that the class must inherit from
CKSHCommander::Command
, and it should reside within a module that shares
its name with the command.
# commands/example/command.rb
require "cksh_commander"
module Example
class Command < CKSHCommander::Command
set token: "gIkuvaNzQIHg97ATvDxqgjtO"
# Subcommand with no arguments
# SLACK: /example test0
desc "test0", "Subcommand 0 description."
def test0
set_response_text("Subcommand: test0")
end
# Subcommand with one argument
# SLACK: /example test1 text
desc "test1 [TEXT]", "Subcommand 1 description."
def test1(text)
set_response_text("Subcommand: test1; Text: #{text}")
end
# No subcommand with one argument
# SLACK: /example text
desc "[TEXT]", "Root command description."
def ___(text)
set_response_text("Root command; Text: #{text}")
end
end
end
We set
our slash command authentication token at the class level, and define
methods for processing subcommands. Attachments (which take the form of a hash)
can be added using add_response_attachment(attachment)
. You can also set the
response to 'in_channel'
at the method level with respond_in_channel!
.
We can access the Slack payload data via the data
reader.
data.token #=> "gIkuvaNzQIHg97ATvDxqgjtO"
data.team_id #=> "T0001"
data.team_domain #=> "example"
data.channel_id #=> "C2147483705"
data.channel_name #=> "test"
data.user_id #=> "U2147483697"
data.user_name #=> "Randy"
data.command #=> "/test"
data.text #=> "subcommand"
data.response_url #=> "https://hooks.slack.com/commands/1234/5678"
Similar to Thor, we are able to document our
command's API with the class-level desc
method—as is shown in the example
above. CKSHCommander provides a help
subcommand out of the box, and this will
echo the documentation back to the Slack user.
/example test0 # Subcommand 0 description.
/example test1 [TEXT] # Subcommand 1 description.
/example [TEXT] # Root command description.
Use authorize
to whitelist the user IDs of Slack users whom you've authorized
to perform a subcommand. An unauthorized user who tries to use this subcommand will
receive the response, "You are unauthorized to use this subcommand!" You can
find the IDs of Slack users on your team using
Slack's REST API. Note that
authorization is not performed unless authorize
is used explicitly.
...
desc "privatecmd", "A private subcommand."
def privatecmd
authorize(%w[U2147483697])
set_response_text("You are authorized!")
end
...
If you need to debug a subcommand and forward a caught exception to your Slack
client, you can use debug!
at the top of your subcommand's method body.
Otherwise, exceptions at the subcommand level will result in the set
error
message text being returned to the client. Using debug!
like this allows you
to isolate testing to a single subcommand without disrupting all usage of the
slash command.
...
set error_message: "Hm. Something went wrong!"
def subcommand
debug!
undefined_method #=> NameError
set_response_text("Never evaluated...")
end
...
To run a command, we use CKSHCommander::Runner
. In the example below, we've
created a simple Sinatra app
to illustrate its usage with the standard Slack slash command payload.
# app.rb
require "sinatra"
require "json"
require "cksh_commander"
CKSHCommander.configure do |c|
c.commands_path = File.expand_path("../commands", __FILE__)
end
post "/" do
content_type :json
command = params["command"][1..-1]
response = CKSHCommander::Runner.run(command, params)
JSON.dump(response.serialize)
end
Check out (and/or use) example commands provided by the
community.
Want to contribute a command? Great! Add your command to the commands/
directory
and open a pull request! The implementations below provide convenient reference
points.
command | description |
---|---|
jira | Display JIRA issue details in a Slack channel or message. |
lunch | State or revoke your intention to attend the weekly lunch gathering. |
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and tags, and push the .gem
file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/openarcllc/cksh_commander.