yargs / pirate-joe

yargs' chat-bot
ISC License
35 stars 4 forks source link

Pirate Joe

Build Status Coverage Status js-standard-style standard-version

Yargs' slack-bot Pirate Joe, the eternal optimist.

Joe was one optimistic pirate.

Building Slack a Slack-Bot with yargs

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.

Parsing Incoming Messages

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:

Responding to Slack

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)
    })
  }
}

Parsing the message with yargs

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.

Deploying Your Application

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.

License

ISC