Slack の Bolt フレームワークのチュートリアルを Heroku 上で実行する

最近 Slack のイベントにも参加したりした時に色々と聞いたので、自分でもやってみたものです。
ほとんど公式ドキュメントをなぞったものなので、難しい内容とかは特にないかと思います。
シンプルに 「Heroku で開発するならどうやるのか」 を試した感じです。

## 前提条件
これからやる作業の前提条件として、次のことはすでに済ませてある前提で進めていきます。

1. Heroku のアカウントを作成済み
2. Heroku CLI をインストール・設定済み（ログインとか）
3. Node.js をインストール済み
4. Git をインストール・設定済み

エディタはお好みのものをご利用ください。

## これからやること
1. Slack の Bolt フレームワークにあるアプリを作成
2. 作成したアプリを Heroku 上にアップする
3. 余力があればカスタマイズ

## 手順
### アップロード先の Heroku アプリの準備
Slack の App を作る前に、次のコマンドであらかじめアップロード先の Heroku アプリを作成しておきます。

```
$ heroku apps:create <アプリの名前を半角英数字で>
Creating ⬢ <アプリ名>... done
https://<アプリ名>.herokuapp.com/ | https://git.heroku.com/<アプリ名>.git
```

`heroku apps:create` の後に何も入力せずに実行すると、Heroku が自動でランダムな名前を生成して設定しますが、分かりやすいように名前を指定しておくことをお勧めします。

アプリの作成が終わると、Web の URL と Git の URL の２つが表示されますので、どちらも控えておきます。
URL はこんな感じです。

Web の URL

```
https://<アプリ名>.herokuapp.com/
```

Git の URL

```
https://git.heroku.com/<アプリ名>.git
```

控え損ねた場合は、`heroku apps:info <アプリ名>` で確認もできます。(ブラウザからダッシュボードでもいけるはず)

### Slack App の作成
今度は Slack 側のアプリの作成、設定をしていきます。

1. Slack API のサイトの [Your Apps](https://api.slack.com/apps?new_app=1)の画面から **Create New App** ボタンをクリックします。
    ![New_App_Create_Screen.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34297/d3688add-0cf0-dee5-fe3e-0f0b93c3a270.png)

2. ポップアップでアプリ名とインストール先のワークスペースを聞かれるので、必要事項を入力して **Create App** ボタンをクリックします。
3. アプリが作成されると **Basic Information** の 画面に遷移します。このページの **App Credential** の欄には、後ほど使用する認証情報が記載されています。
4. 左側のメニューから **Bot Users** をクリックします
5. **Add A Bot User** をクリックして、表示名とユーザ名を設定し、**Add Bot User** をクリックします
6. 左側のメニューから **Install App** をクリックします
7. **Install App to Workspace** をクリックしてワークスペースにインストールします
8. OAuth トークンが２種類生成されます
    ![OAuth_Token_Info.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34297/754a37c3-284e-7446-d039-c302e77fe550.png)

この後で **Bot User OAuth Access Token** の方を使っていきます。

## Slack App の開発
ここからは実際にサンプルの Slack App を作っていきます。
なお、Heroku アプリへのデプロイには Git を使用するため、途中で Git コマンドも使用します。

1. プロジェクトディレクトリを作成し、中に移動します（ディレクトリの名前は任意）

    ```
    $ mkdir <プロジェクトディレクトリ名>
    $ cd <プロジェクトディレクトリ名>
    ```
2. `git init` コマンドで Git の管理対象に設定します

    ```
    $ git init
    ```

3. `.gitignore` ファイルを作成します（中身は [Heroku の公式サンプル](https://github.com/heroku/node-js-getting-started.git)を参考にしました。）

    ```
    $ vi .gitignore
    ```
    
    ```bash:.gitignore
    # Node build artifacts
    node_modules
    npm-debug.log
    
    # Local development
    *.env
    *.dev
    .DS_Store
    
    # Docker
    Dockerfile
    docker-compose.yml
    ```
4. [Procfile](https://devcenter.heroku.com/articles/procfile) を作成します。Procfile は起動時にアプリが実行するコマンドを記載するファイルです。ここでは `node app.js` コマンドを記載します。

    ```
    $ vi Procfile
    ```
    
    ```bash:Procfile
    web: node app.js
    ```

5. `npm init` コマンドでプロジェクトの設定を行います（私はこんな感じで実行しました）

    ```bash
    $ npm init
    This utility will walk you through creating a package.json file.
    It only covers the most common items, and tries to guess sensible defaults.
    
    See `npm help json` for definitive documentation on these fields
    and exactly what they do.
    
    Use `npm install <pkg>` afterwards to install a package and
    save it as a dependency in the package.json file.
    
    Press ^C at any time to quit.
    package name: (<プロジェクトディレクトリ名>) 
    version: (1.0.0) 
    description: 
    entry point: (index.js) app.js
    test command: 
    git repository: 
    keywords: 
    author: 
    license: (ISC) 
    About to write to <プロジェクトディレクトリの親のパス>/bolttest/package.json:
    
    {
      "name": "bolttest",
      "version": "1.0.0",
      "description": "",
      "main": "app.js",
      "scripts": {
         "test": "echo \"Error: no test specified\" && exit 1"
      },
      "author": "",
      "license": "ISC"
    }
    
    
    Is this OK? (yes) 
    ```
    
    出来上がった `package.json` ファイル
    
    ```package.json
    {
      "name": "bolttest",
      "version": "1.0.0",
      "description": "",
      "main": "app.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "author": "",
      "license": "ISC"
    }
    ```

6. Bolt パッケージをインストールします

    ```
    $ npm install @slack/bolt
    ```

7. app.js 内に次のコードを記述します。[Heroku の場合、ポート番号は動的に割り振られる](https://help.heroku.com/P1AVPANS/why-is-my-node-js-app-crashing-with-an-r10-error)ため、process.env.PORT の書き方で良いようです。

    ```
    $ vi app.js
    ```
    
    ```app.js
    const { App } = require('@slack/bolt');

    const app = new App({
      token: process.env.SLACK_BOT_TOKEN,
      signingSecret: process.env.SLACK_SIGNING_SECRET
    });

    (async () => {
      // Start your app
      await app.start(process.env.PORT || 3000);

      console.log('⚡️ Bolt app is running!');
    })();
    ```

8. git でローカルにコミットします

    ```
    $ git add .
    $ git commit -m "First Commit"
    ```

9. ローカルリポジトリのリモートの設定を追加します。Heroku アプリへのデプロイは、ローカルのリポジトリを Heroku のリポジトリにそのままプッシュすることなので、あらかじめ登録しておきます。URL は **アップロード先の Heroku アプリの準備** の手順ででた Git の URL です。

    ```
    $ git remote add heroku <Git の URL>
    ```

10. heroku リポジトリにプッシュします

    ```
    $ git push heroku master
    ```

11. トークンを設定します。Heroku では [config vars](https://devcenter.heroku.com/articles/config-vars) という仕組みがあるので、それを使用します

    ```bash
    $ heroku config:set SLACK_SIGNING_SECRET=<サインインシークレット>
    $ heroku config:set SLACK_BOT_TOKEN=xoxb-<Bot トークン>
    ```

12. インスタンスを heroku コマンドで立ち上げます

    ```
    $ heroku ps:scale web=1
    ```

13. ログを確認します。ログの確認には `heroku logs` コマンドを使用します

    ```
    $ heroku logs -t
    ```
    次のログが出ているかと思います。

    ```
     ⚡️ Bolt app is running!
    ```

14. Slack api の左側のメニューから **Event Subscription** をクリックして、イベント URL を登録し **Save Change** をクリックします。設定する URL は Heroku の Web の URL の末尾に `/slack/events` をつけたものとなります。

    ```
    https://bolttestbot.herokuapp.com/slack/events
    ```
    こんな感じになれば OK です。
    ![Event_Subscription.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34297/446ab335-780e-3beb-7838-4880578c219a.png)


15. 現状だと何も反応しないため、`app.message` の処理を追加します。

    ```
    $ vi app.js
    ```
    
    ```app.js
    const { App } = require('@slack/bolt');

    const app = new App({
      token: process.env.SLACK_BOT_TOKEN,
      signingSecret: process.env.SLACK_SIGNING_SECRET
    });

    // Listens to incoming messages that contain "hello"
    app.message('hello', ({ message, say }) => {
      // say() sends a message to the channel where the event was triggered
      say(`Hey there <@${message.user}>!`);
    });

    (async () => {
      // Start your app
      await app.start(process.env.PORT || 3000);

      console.log('⚡️ Bolt app is running!');
    })();
    ```

16. ローカルにコミットします

    ```
    $ git add app.js
    $ git commit -m "Add hello message"
    ```

17. Heroku にプッシュします

    ```
    $ git push heroku master
    ```

これで チャンネルに Bot ユーザを招待して、`hello` を含むメッセージを投稿すると反応してくれます。

![Bot_Response.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34297/bc20a057-d47e-b740-3c7b-1330ec9888d9.png)

### メッセージのカスタマイズ
メッセージがテキストのみなので、ボタンを追加するチュートリアルにしたがってやっていきます。

1. Slack api の画面左側の **Interactive Components** をクリックして、Request URL を登録し、**Save Change** をクリックします。登録する URL は **Event Subscription** のところで登録したものと同じものを登録します。

    ![Interactive_Components.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34297/57982038-b97f-3d29-29d1-622c2607029c.png)

2. ボタン付きメッセージを返すようにコードを修正します

    ```
    $ vi app.js
    ```
    
    ```app.js
    const { App } = require('@slack/bolt');

    const app = new App({
      token: process.env.SLACK_BOT_TOKEN,
      signingSecret: process.env.SLACK_SIGNING_SECRET
    });

    // Listens to incoming messages that contain "hello"
    app.message('hello', ({ message, say }) => {
      // say() sends a message to the channel where the event was triggered
      say({
        blocks: [
        {
          "type": "section",
          "text": {
            "type": "mrkdwn",
            "text": `Hey there <@${message.user}>!`
          },
          "accessory": {
            "type": "button",
            "text": {
              "type": "plain_text",
              "text": "Click Me"
            },
            "action_id": "button_click"
          }
        }
        ]
      });
    });

    (async () => {
      // Start your app
      await app.start(process.env.PORT || 3000);

      console.log('⚡️ Bolt app is running!');
    })();
    ```

3. ローカルにコミットします

    ```
    $ git add app.js
    $ git commit -m "change message to block"
    ```

4. Heroku にプッシュします

    ```
    $ git push heroku master
    ```
これで `hello` を含むメッセージを投稿した時にボタン付きのメッセージがボットから返されますが、ボタンをクリックした時の処理がないので何も起こりません。（正確には、ボタンを押すと右側にビックリマークが出ます）

5. ボタンクリック時のアクションを `app.action` で追加します

    ```
    $ vi app.js
    ```
    
    ```app.js
    const { App } = require('@slack/bolt');

    const app = new App({
      token: process.env.SLACK_BOT_TOKEN,
      signingSecret: process.env.SLACK_SIGNING_SECRET
    });

    // Listens to incoming messages that contain "hello"
    app.message('hello', ({ message, say }) => {
      // say() sends a message to the channel where the event was triggered
      say({
        blocks: [
        {
          "type": "section",
          "text": {
            "type": "mrkdwn",
            "text": `Hey there <@${message.user}>!`
          },
          "accessory": {
            "type": "button",
            "text": {
              "type": "plain_text",
              "text": "Click Me"
            },
            "action_id": "button_click"
          }
        }
        ]
      });
    });

    app.action('button_click', ({ body, ack, say }) => {
      // Acknowledge the action
      ack();
      say(`<@${body.user.id}> clicked the button`);
    });

    (async () => {
      // Start your app
      await app.start(process.env.PORT || 3000);

      console.log('⚡️ Bolt app is running!');
    })();
    ```

6. ローカルにコミットします

    ```
    $ git add app.js
    $ git commit -m "Add button click action"
    ```

7. Heroku にプッシュします

    ```
    $ git push heroku master
    ```

これでボタンをクリックした時にメッセージが投稿されるようになりました。

![Button_Clicked.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34297/86d54869-2c93-7dff-b008-61da19b20eff.png)

私が実施したソースコードは GitHub のリポジトリにあります。
[silverskyvicto/bolttest
](https://github.com/silverskyvicto/bolttest)

## 余談
Slack の瀬良さん @seratch がサンプルのものをすでに作成していて、それを Heroku Button として公開していることを後で知りました。

[Slack Bolt app on Heroku](https://elements.heroku.com/buttons/seratch/bolt-on-heroku)

## 参考
- [Bolt 入門ガイド](https://slack.dev/bolt/ja-jp/tutorial/getting-started)
- [Why is my Node.js app crashing with an R10 error?](https://help.heroku.com/P1AVPANS/why-is-my-node-js-app-crashing-with-an-r10-error)