Slack Boltからmessage buttonsとmenusを使う

こんにちは、 [@hico_horiuchi](https://twitter.com/hico_horiuchi) です。
先日、会社の昼休みLT会(通称#random-jp会)でBoltを紹介したので、そのまとめです。

## 目次

- Boltとは?
- Hello Worldをやってみる
- message buttonsを使う
- message menusを使う
- まとめ

## Boltとは?

[Bolt](https://slack.dev/bolt) はSlack社が開発しているSlack Appのフレームワークです。
(Botだけでなく、Slash Commandなども簡単に開発できるようになっています。)

2019年4月にv1.0.0がリリースされたばかりの、比較的新しいOSSです。
[Release Introducing Bolt - v1.0.0](https://github.com/slackapi/bolt/releases/tag/%40slack%2Fbolt%401.0.0)

## Hello Worldをやってみる

最初に「hello」に対して「Hey there @hiconyan!」と返す機能を作ってみます。
Slack Appの作成方法などは、下記の公式情報を参考にすると分かりやすいです。

- [Bolt 入門ガイド](https://slack.dev/bolt/ja-jp/tutorial/getting-started)
- [Hello World, Bolt! ⚡️ Bolt フレームワークを使って Slack Bot を作ろう](https://qiita.com/girlie_mac/items/93538f9a69eb4015f951)

今回はBotの名前を「boltnyan」にしてみました。
[BotkitでHubotみたいにscriptsを読み込む](https://qiita.com/hiconyan/items/51397eda93bca68811e4) と同様のファイル構成にしています。

サンプルコードはこちら: [hico-horiuchi/boltnyan](https://github.com/hico-horiuchi/boltnyan)

```
boltnyan/
  bin/
    bolt
  node_modules/
  scripts/
    hello.js
  package.json
```

hello.jsはこちらです、とても簡単ですね。
(Slack Appで [イベントの設定](https://slack.dev/bolt/ja-jp/tutorial/getting-started#%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AE%E8%A8%AD%E5%AE%9A) をするのをお忘れなく。)

```javascript:scripts/hello.js
app.message(`boltnyan hello`, ({ message, say }) => {
  say(`Hey there <@${message.user}>!`);
});
```

`bin/bolt` を実行して `boltnyan hello` を声をかけると、挨拶が返ってきました。

![スクリーンショット 2019-12-15 17.03.54.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58742/cfee0bfd-e110-b06f-d2b4-4aec6cd06c4d.jpeg)

## message buttonsを使う

Boltを使うと [message buttons](https://api.slack.com/docs/message-buttons) も簡単に実装することができます。

`say()` の `attachments[].callback_id` でbuttonに識別子を付けて(ここでは `game_button` )、
それを `action()` で指定することで、ユーザが該当のbuttonをクリックしたときのレスポンスを受け取ることができます。

`ack()` でattachmentsを返すことで、該当のボタンを含むメッセージを書き換えることもできます。

```javascript:scripts/button.js
app.message('boltnyan button', ({ message, say }) => {
  say({
    text: 'Would you like to play a game?',
    attachments: [{
      text: 'Choose a game to play',
      fallback: 'You are unable to choose a game',
      callback_id: 'game_button',
      color: '#3AA3E3',
      attachment_type: 'default',
      actions: [{
        name: 'game',
        text: 'Chess',
        type: 'button',
        value: 'chess'
      }, ...]
    }]
  });
});

app.action({ callback_id: 'game_button' }, ({ body, ack, say }) => {
  let user = body.user.id;
  let value = body.actions[0].value;

  ack({
    text: 'Would you like to play a game?',
    attachments: [{
      text: `Choose a game to play¥n¥n<@${user}> clicked ${value}`,
      color: '#3AA3E3',
      attachment_type: 'default',
    }]
  });
});
```

`boltnyan button` でbuttonを表示させて「Chess」をクリックすると、このようになります。

![2019-12-15 17.27.30.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58742/cc3c6fb8-d53f-bc37-da10-4f1dcab64bdd.gif)

## message menusを使う

message buttonと同様に [message menus](https://api.slack.com/docs/message-menus) も実装してみます。

`callback_id` として `game_menu` を指定して、ユーザが該当のmenuを選択したときのレスポンスを受け取っています。
`say()` で投稿する場合のattachmentsの構造や `ack()` で受け取る `body.actions[]` の構造が違うので注意してください。

```javascript:scripts/menu.js
app.message('boltnyan menu', ({ message, say }) => {
  say({
    text: 'Would you like to play a game?',
    response_type: 'in_channel',
    attachments: [{
      text: 'Choose a game to play',
      fallback: "If you could read this message, you'd be choosing something fun to do right now.",
      color: '#3AA3E3',
      attachment_type: 'default',
      callback_id: 'game_menu',
      actions: [{
        name: 'games_list',
        text: 'Pick a game...',
        type: 'select',
        options: [{
          text: 'Hearts',
          value: 'hearts'
        }, ...]
      }]
    }]
  });
});

app.action({ callback_id: 'game_menu' }, ({ body, ack, say }) => {
  let user = body.user.id;
  let value = body.actions[0].selected_options[0].value;

  ack({
    text: 'Would you like to play a game?',
    attachments: [{
      text: `Choose a game to play¥n¥n<@${user}> selected ${value}`,
      color: '#3AA3E3',
      attachment_type: 'default',
    }]
  });
});
```

`boltnyan menu` でmenuを表示させて「Chess」を選択すると、このようになります。

![2019-12-15 17.36.45.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58742/6ae24e19-5aff-ca13-330d-9285c366e173.gif)

## まとめ

実はbuttonsとmenusが非推奨になり [blocks](https://api.slack.com/messaging/attachments-to-blocks) に移行していることを、この記事を書いている最中に知りました…。
次回はBoltとblocksの連携や、Hubotで同様の機能を実装する方法についてご紹介したいと思います。