Yargs' slack-bot Pirate Joe, the eternal optimist.
yargs, with its new headless parse()
feature and its powerful command
parsing functionality, is a great tool for building a chat-bot.
The Pirate Joe sample project and accompanying tutorial provide the foundation you need to get a chat-bot up in running in Slack using yargs.
We've implemented Pirate Joe as a Slack slash-command. When
a user types /joe
in any of yargs' slack channels, a webhook is posted to whatever URL you configure.
To process this webhook we created a minimal express application, with the the following post
handler:
const bodyParser = require('body-parser')
const express = require('express')
// Slack's slash commands are passed as an urlencoded
// HTTP post: https://api.slack.com/slash-commands
app.use(bodyParser.urlencoded({ extended: false }))
// slack webhook endpoint.
app.post('/', function (req, res) {
let context = Object.assign({}, req.body)
// slack secret token must be provided.
if (!req.body || req.body.token !== process.env.SLACK_TOKEN) {
return res.sendStatus(401)
}
// provides a respond function that any yargs
// command can use to respond to Slack.
context.respond = buildResponder(req.body.response_url)
// run the yargs parser on the inbound slack command.
parser.parse(req.body.text || '', context, (err, argv, output) => {
if (err) logger.error(err.message)
if (output) argv.respond(output)
})
res.send('')
})
bodyParser
processes the inbound webhook from Slack and populates three
variables that are important to us:
- req.body.token: the shared secret between our application and and Slack. This allows us to verify that the post originated from Slack. Note: make sure that you keep this token a secret! don't put it under version control.
- req.body.text: the text that the user typed into the Slack channel.
- req.body.response_url: the URL that your bot's response will be posted to.
Slack provides a response_url
in their webhook for a bot to post their response to.
Before passing req.body.text
to yargs for processing, we populate a helper function that
allows yargs commands to easily send messages back to Slack:
// returns a helper function for dispatching messages
// back to Slack.
function buildResponder (responseUrl) {
return function (msg) {
request.post({
url: responseUrl,
json: true,
body: {
response_type: 'in_channel',
text: msg
}
}, function (err, res, body) {
if (err) return logger.error(err)
if (res && res.statusCode >= 400) logger.error('invalid response =', res.statusCode)
else logger.info(body)
})
}
}
Creating a yargs instance for processing chat messages is almost identical to how you would configure it for a command line application.
We begin by configuring the parser in the abstract:
const parser = require('yargs')
.usage('/joe [command]')
.commandDir('commands')
.demand(1)
.strict()
.help()
.epilog("yargs' slack-bot Pirate Joe")
yargs.commandDir('commands')
indicates that
we should load all the chat commands located in /commands
.
We create a command JavaScript file for each of our chat commands.
Here's the module for translating English strings to Pirate strings:
pirate.js
const pirateSpeak = require('pirate-speak')
exports.command = 'pirate <strings...>'
exports.describe = 'US English to US Pirate translator'
exports.builder = {}
exports.handler = function (argv) {
argv.respond(pirateSpeak.translate(argv.strings.join(' ')))
}
With the yargs instance configured, we now simply run parse()
on each
of the inbound messages from Slack:
context.respond = buildResponder(req.body.response_url)
parser.parse(req.body.text || '', context, (err, argv, output) => {
if (err) logger.error(err.message)
if (output) argv.respond(output)
})
Again, by providing the context.respond
method, we give yargs' commands a
mechanism for sending messages back to Slack.
If any default output has been logged by yargs (perhaps help
was executed), the
parse()
function itself dispatches the output
back to Slack.
The easiest way to get your slack-bot up and running is to create a Heroku application. A wonderful interactive tutorial is available on this topic.
Once your application is in the wild, visit:
https://[your-slack].slack.com/apps/manage/custom-integrations
and configure a new Slash Command for your application.
ISC