A Vapor template for making Slack bots
SwiftMN is a Swift user group in Minneapolis, Minnesota.
You can meet us in person and join our meetup at swift.mn
You can talk to us on Slack and see this template in action at slack.swift.mn
You can email us directly at [email protected]
You can also contribute to this template! Pull requests, issues, feature requests, and feedback of any kind is welcome.
To create a new Slack App with Vapor, simply specify this template when creating your vapor app.
vapor new MySlackApp --template=SwiftMN/slack-template
If you set up an incoming webhook for your Slack App, simply copy the url into Config/slack.json
under the webhooks
section. It should look something like this:
"webhooks": {
"scheduledCommand": "https://hooks.slack.com/services/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"
}
Then to call the webhook, you simply grab the url out of config, and make the request:
guard let webhook = config["slack", "webhooks", "scheduledCommand"]?.string else {
throw Abort(.internalServerError, reason: "Could not find scheduledCommand webhook")
}
let message = SlackMessage(text: "Good news, everyone!")
let _ = try client.sendMessage(message, to: webhook)
Responding to slash commands is super easy. There are 3 steps you need to take for every slash command that you want to add. For this example we’ll add a command /say
that sends a message with whatever text the user entered.
Add a Slash Command in your Slack App configuration, and set the “Request URL” to <your_domain>/slashCommand/say
.
Add a route for say
to Sources/App/Routes/Routes.swift
route.post("say", handler: slashCommand.say)
Add a function in SlashCommandController
to handle the command
func say(_ request: Request) throws -> ResponseRepresentable {
return "ok"
}
That’s it! That is a fully functional slash command. However, it doesn’t actually do what we want it to. Let’s update our say
function to send a message back to the slack group.
func say(_ request: Request) throws -> ResponseRepresentable {
guard let text = try? request.formValue(key: .text) else {
return """
Hi there!
If you add some text to the command, I'll say it to everyone. Like this:
`/say Good news, everyone!`
"""
}
background {
// send the message to everyone in the channel on a background thread
let message = SlackMessage(text: text)
let _ = try? request.respond(with: message)
}
// This will be shown to the user that entered the slash command
return "Coming right up!"
}
Most of the time you should send messages on a background thread so the slash command doesn’t timeout and show the user an error even thought it actually worked. Slack has a short timeout for slash commands.
Vapor allows you to build commands that can be run in the terminal. These commands can then be scheduled using cron expressions. This is super handy if you want to have your bot automatically do something every day, every hour, or every whatever you can do with cron.
Let’s say we want our bot to say “Good Morning” every day at 8am CST. First, you’ll need to follow the steps in the Incoming Webhooks
section above so we can easily send a message into Slack. Then we need to do a few steps to build and configure our command.
Create a Command
class. This template already has a ScheduledCommand
for you to copy, but you should read the documentation, too.
Call addConfigurable
in Sources/App/Setup/Config+Setup.swift
addConfigurable(command: ScheduledCommand.init, name: "scheduledCommand")
Add your Command id to Config/droplet.json
"//": "Choose which commands this application can run",
"//": "prepare: Supplied by the Fluent provider. Prepares the database (configure in fluent.json)",
"commands": [
"prepare",
"scheduledCommand"
]
Set up a cron schedule in cloud.yml
(Xcode can’t see this file, BTW)
type: "vapor"
swift_version: "4.0.0"
cronjobs:
production:
scheduledCommand:
time: "0 14 * * *"
command: "scheduledCommand"