BotkitでSlackのSlash Commandを作る


あけましておめでとうございます．
BotkitでSlash Commandを作れることに気付いたので試してみました．
(Slash Commandは `/topic` のようなメッセージのことです．)

  - [Slash Commands | Slack](https://api.slack.com/slash-commands)
  - [Using slash commands - Slack Help Center](https://get.slack.help/hc/en-us/articles/201259356-Using-slash-commands)
  - [Outgoing Webhooks and Slash commands | Botkit](https://github.com/howdyai/botkit/blob/master/readme.md#outgoing-webhooks-and-slash-commands)

Slash Commandを作るには，ドメインか固定IPを持っている必要があります．
今回はお試しなので，HerokuのFree Planを使うことにしました．

## Herokuでアプリを作る

事前にHerokuの登録と，Heroku Toolbeltのインストールが必要です．

  - [Getting Started with Node.js on Heroku | Heroku Dev Center](https://devcenter.heroku.com/articles/getting-started-with-nodejs)

今回は「与えられた選択肢からランダムに選ぶ」という `/omikuji` を作ってみます．
アプリ名は「omikuji-command」とします :crystal_ball: 

```
$ mkdir botkit-omikuji-command
$ cd botkit-omikuji-command
$ heroku create --stack cedar omikuji-command
Creating omikuji-command... done, stack is cedar-14
https://omikuji-command.herokuapp.com/ | https://git.heroku.com/omikuji-command.git
Git remote heroku added
```

`https://omikuji-command.herokuapp.com/` がアプリのURLです．
この後のSlack Appの登録で必要なので，メモしておいて下さい．

## Slack Appを作る

これまでSlash CommandはIntegrationとして追加していました．
しかし，Botkitでは新しく登場したSlack Appを作成する必要があるようです．
Slack Appでは他にも，BotやCustom Integrationを定義することができます．

  - [Getting started with Slack apps | Slack](https://api.slack.com/slack-apps)
  - [SlackのスラッシュコマンドでLyftのタクシーを呼ぶなど他サービスが利用できるようになる | TechCrunch Japan](http://jp.techcrunch.com/2015/11/04/20151103slack-improves-slash-commands-so-you-can-call-a-lyft-and-more-from-inside-slack/)

[New Application](https://api.slack.com/applications/new)から下記のようにSlack Appを作成します(まだSlash Commandの定義はしません)．
リンク関係はGitHubのリポジトリを指定しました(READMEでも良いかも)．
Redirect URIは，OAuthの認証に成功するとアクセスされます．
認証機能はBotkitが提供してくれるので(後述)，先程のHerokuのURLに `/oauth` を追加します． 

![new-application.png](https://qiita-image-store.s3.amazonaws.com/0/58742/37ce82da-38cb-863f-262f-c61e61696cf8.png "new-application.png")

作成できたら「OAuth Information」の **Client ID** と **Client Secret** をメモして下さい．
Botkitで必要になるので，後程Herokuの環境変数として設定します．

## BotkitでSlash Commandを作る

ここからが本題です．
実際にBotkitを使ってSlash Commandを作ってみます．

まず，最初に作成した `botkit-omikuji-command` ディレクトリに移って，必要なファイルを用意します．
`package.json` は必要なnpmを， `Procfile` はHerokuで実行するコマンドを指定しています．

```json:package.json
{
  "dependencies": {
    "botkit": ">= 0.0.*"
  }
}
```

```:Procfile
web: node bot.js
```

ここからがBotkitのコードですが，以外と少ないですね．

```javascript:bot.js
var botkit = require('botkit');

var controller = botkit.slackbot({
  debug: false,
  json_file_store: './simple_storage/'
}).configureSlackApp({
  clientId: process.env.BOTKIT_SLACK_CLIENT_ID,
  clientSecret: process.env.BOTKIT_SLACK_CLIENT_SECRET,
  scopes: ['commands']
});

controller.setupWebserver(process.env.PORT, function(err, webserver) {
  controller.createWebhookEndpoints(controller.webserver);
  controller.createOauthEndpoints(controller.webserver, function(err, req, res) {
    if (err) {
      res.status(500).send('Error: ' + JSON.stringify(err));
    } else {
      res.send('Success');
    }
  });
});

controller.on('slash_command', function(bot, message) {
  switch (message.command) {
  case '/omikuji':
    var choices = message.text.split(',');
    var choice = choices[Math.random() * choices.length | 0];
    bot.replyPrivate(message, '<@' + message.user + '> *' + choice + '*');
    break;
  }
});
```

幾つか重要なポイントがあるので，簡単に解説します．

### json_file_store

Botkitでは，データを保存するためのストレージを使うことができます．
Slash Command(Slack App)では，OAuthのトークンを保存するために必要です
ただ，Herokuではファイルシステムに書き込むことができないので，無意味ですが…．
([lib/storage/redis_storage.js](https://github.com/howdyai/botkit/blob/master/lib/storage/redis_storage.js)があるけど，どうやって使うんだろう…．)

### controller.configureSlackApp

`clientId` と `clientSecret` を指定して，BotkitをSlack Appとして動作させます．
`scopes` ではSlack Appが必要な権限を指定します．
[OAuth Scopes](https://api.slack.com/docs/oauth-scopes)の通り，Slackでは大量のスコープが定義され，細かく設定することができます．
Slash Commandの場合は，[Slack Button](https://api.slack.com/docs/slack-button)の `commands` を指定します．

### controller.createWebhookEndpoints

SlackからのWebHookを受ける `/slack/receive` を追加します．
WebHookの種類によって `slash_command` または `outgoing_webhook` イベントを発火します．

### controller.createOauthEndpoints

OAuthに必要な `/login` と `/oauth` を追加します．
`/login` は，Slack Appをチームに追加する(OAuthを認証する)場合にアクセスします．
その後 `/oauth` にリダイレクトされ，Botkitがトークンの受取と確認を行います．

### bot.replyPrivate

Slash Commandを実行したユーザーだけに見えるように返信します．
全員に見られるようにするには `bot.replyPublic` を使います．
[Slash Commands](https://api.slack.com/slash-commands)によると，レスポンスは3000ms以内に返す必要があるようです．
時間がかかる場合は `bot.replyPublicDelayed` か `bot.replyPrivateDelayed` を使うようです．

また， `message` オブジェクトにはWebHookで与えられた下記のデータが格納されているようです．

```
token=gIkuvaNzQIHg97ATvDxqgjtO
team_id=T0001
team_domain=example
channel_id=C2147483705
channel_name=test
user_id=U2147483697
user_name=Steve
command=/weather
text=94070
response_url=https://hooks.slack.com/commands/1234/5678
```

## Herokuにデプロイする

Herokuにデプロイして，動かしてみましょう．
リモートリポジトリは設定済なので，環境変数を設定してからpushします．

```
$ cd botkit-omikuji-command
$ git init
$ git add .
$ git commit -m 'first commit'
$ heroku config:set BOTKIT_SLACK_CLIENT_ID=01234567890.12345678901
$ heroku config:set BOTKIT_SLACK_CLIENT_SECRET=abcdefghijklmnopqrstuvwxyz012345
$ git push heroku master
```

## Slash Commandを設定する

[Applications](https://api.slack.com/applications)にアクセスして，先程作成したSlack AppにSlash Commandを設定します．
「Slash Commands」の **Create new command** を押して，下記のように設定します．
Request URLには，HerokuのURL + `/slack/receive` を指定します．
(また，HTTPSを要求されるため，個人ではハードルが高いですね…．)
**Save** 実行時にRequest URLにアクセスされるようで，先にBotkitをデプロイする必要があります．

![create-new-command.png](https://qiita-image-store.s3.amazonaws.com/0/58742/96dbeb9e-0392-2e73-243a-96eed1a72d65.png "create-new-command.png")

最後に，HerokuのURL + `/login` にアクセスしてSlack Appをチームに追加します．
Slackで **Authorize** した後に，リダイレクト先で **Success** と表示されれば完了です．
これでようやく，Slash Commandを使えるようになりました．

![omikuji-command.gif](https://qiita-image-store.s3.amazonaws.com/0/58742/809edc06-eb24-7282-5bb3-b549182be35d.gif "omikuji-command.gif")

## まとめ

Heroku，Slack App...と下準備が大変でしたが，Slash Commandを使えるようになりました．
しかし，BotkitがSlackに特化しているおかげで，実際のコード量は少なくて済みました．
公開サーバとSSL証明書を持っていれば，もっと簡単にできそうですね．

また，以前[Hubot×ChatOps勉強会](http://hubot-chatops.connpass.com)でこんな話が出ていました．

> Bot(Hubot)ではコマンドの補完ができないのが不便だが，チャット側の対応も必要だよね．

Botをlash Commandに移行すれば，コマンドの補完が効くので良いかもしれません．
この場合は `message.command` で条件分岐させれば，複数のSlash Commandを1つのBotkitで扱うことができるハズです．

今回作成したSlack AppはGitHubで公開しています．

  - [hico-horiuchi/botkit-omikuji-command](https://github.com/hico-horiuchi/botkit-omikuji-command)

<a href="https://slack.com/oauth/authorize?scope=commands&client_id=12263613568.17678729859"><img alt="Add to Slack" height="40" width="139" src="https://platform.slack-edge.com/img/add_to_slack.png" srcset="https://platform.slack-edge.com/img/add_to_slack.png 1x, https://platform.slack-edge.com/img/add_to_slack@2x.png 2x"></a>