Slack API 新機能を使ってアプリのホーム・ヴューを活用しよう🏡

> Published: 2019-11-24
> Updated: 2020-02-11


> *OAuth 権限設定についての変更がありましたので、チュートリアルのその設定部分を編集しました。*

先日おこなわれた [TinySpec Osaka & Tokyo](https://qiita.com/seratch/items/d67f36d0ff7dfaadcc3b) でお約束した通り、日本語の  App Home チュートリアルを書きました。
![App Home Demo -Stickies](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339293/ef5f9c3c-cfc6-d463-0808-c9e3bfe44936.png)


今回新しくリリースされた機能、**App Home** は、ユーザと Slack を１対１で繋ぐことができるスペースで、さらにユーザに直感的にアプリを使ってもらうために加えられた機能なのです。App Home には３つのタブがあり、アプリについての情報をみるための _About_、チャット対話式ボット機能がある場合にそのボットとダイレクトメッセージで会話できる _Messages_、そして今回新しく _Home_ タブが加わりました。

この Home タブは、アプリと各ユーザの間を繋ぐプライベート・スペースで、動的で継続的なコンテンツを提供できるビジュアル・インターフェイスなのです。


### App Home って前からあったよね?

もしかしてすでに、ユーザがアプリを開いた時に発生するイベント、 [`app_home_opened`](/reference/app-home) をご存知かもしれません。以前に紹介した Bolt JavaScript framework チュートリアルの [Hello world, Bolt⚡️ Bolt フレームワークを使って Slack Bot を作ろう](https://qiita.com/girlie_mac/items/93538f9a69eb4015f951) でも触れられています。今回はさらにそれがバージョンアップして、 Home タブとともに、メッセージだけではなく [Block Kit](/reference/block-kit/blocks) を使って動的なコンテンツを表示するとこが可能になりました。

## App Home タブ活用例

Google カレンダーの App Home タブ活用例を見てみましょう。
![App Home in Google Calendar](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339293/33fd5f73-aa72-a3a0-eaa5-14ca6e07c07d.png)
ユーザは Home タブから毎日のカレンダーを見ることができるだけではなく、予定への返答を変更したり、Zoom などのカンファレンス・コールへのジョインなどもここから行うことができるのです。
他、ミーティングなどの予定のある１分前に通知をする機能もついているのです。グーグル、グッドジョブ👍


# 🔨 App Home を実装してみよう

さて、この App Home をどうやって使うことができるのか、一緒にアプリを制作しながら試してみましょう。ここでは、ユーザがちょっとしたメモをとることができる Stickies（付箋）アプリを作ってみます。

### ユーザー・フロー

ここでのユーザー・フローは下の通りになります。

1. ユーザが Slackクライアントの左メニューからアプリ名をクリック
2. アプリの Home タブが開く
3. ユーザが Home ビュー上にある Add a stickie (メモの追加）ボタンをクリック
4. モーダルが表示されるので、ユーザがメモ内容を入力し Create（作成）をクリック
5. Home が自動的に更新され、入力内容が ビューに反映される

![App Home user flow GIF](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339293/95c7f245-e24e-d102-3a10-544231339e46.gif)


### アプリケーション・フロー

ここでのアプリ側のフローは次の通りになります。

1. ユーザが App Home をオープンすることにより `app_home_opened` イベントが発生し、アプリのサーバに情報が送信される
2. アプリは、そのイベント・ペイロードから user ID を抽出し [`views.publish`](/methods/views.publish) メソッドによって、ボタン UI のある初期画面を表示
3. ユーザがその “Add a Stickie” ボタンをクリックすることにより、そのインタラクションがトリガーされる
4. アプリは [`views.open`](/methods/views.open) メソッドで入力フォーム　UI のあるモーダルを表示
5. ユーザがそのフォーム内容を送信することによって、`view_submission` のついたもうひとつのインタラクションがトリガーされる　
6. [`views.publish`](/methods/views.publish) メソッドで App Home ビュー表示を更新

![Diagram](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339293/1383da98-50b6-6b0c-0a9f-cf4fb615bf11.png)


さて、大まかなフローがわかったところで実際に Stickies アプリを作ってみましょう。 Glitch というサービス上にソースコードを置いてありますので "remix" してみましょう（リミックスとは GitHub の fork のような物、と思ってください）。このサービスではウェブ上でコードのエディットと実行ができますので、デプロイは不要なのでそのまま動かしてみましょう。

🎏🥫 [Glitch でソースコードだけ覗いてみる](https://glitch.com/edit/#!/apphome-demo-note)
🎏🍴 [Remix (fork) して自分で使う](https://glitch.com/edit/#!/remix/apphome-demo-note)


## ⚙️ アプリのセットアップ

まずは Slack アプリの設定をしなければなりません。 [**Slack App マネージメント**](https://api.slack.com/apps?new_app=1&ref=tutorial_app_home_modal_qiita) からアプリの作成をします。ポップアップが出ますのでそこでアプリの名称を設定し、アプリのインストール可の（＝自分がアドミン権限を持つ）ワークスペースを選択してください。ない場合は[新規でワークスペースを作成して](https://slack.com/get-started#create)から始めましょう。

今度は **Features > OAuth & Permissions** で Bot トークンのスコープを追加します。`chat.write` を選択してください。 (実際にはこのサンプルアプリにはチャットの機能がありませんので、このスコープを使う必要がないのですが、とりあえず追加してください！ この最近追加された、以前より細やかになった権限設定については [OAuth 2.0, Version 2 ドキュメント](https://api.slack.com/authentication/oauth-v2)を参照してください。)

次に **Features > App Home** (下のスクリーンショットの、ステップ１参照)へ行ってください。

このサンプルアプリではダイレクトメッセージは使用しませんが、Message タブはそのまま有効のままでも良いでしょう。

次に **Features > Event Subscription** 画面へ行き、イベントを有効にしてください。(下のスクリーンショットのステップ１参照)。そして **Request URL** (ステップ 2) を入力します。Glitch 上のコードをリミックスして作業を進めている方は、 Request URL は `https://プロジェクト名.glitch.me/slack/events` のようになります。 (Glitch は新規でプロジェクトが作成される毎にプロジェクト名を自動生成します。 たいがいはハイフンで繋いだ２つの英単語からランダムに生成されるので、 _fluffy-umbrella_ のようなものになっていると思います。自分でカスタムのプロジェクト名もつけられますので、その際はそのカスタム名を使ってください。自分のサーバを使用している際は、そのURL + `/slack/events` にします。) 

Request URL 入力が終わったら、**Subscribe to bot events** までスクロールし、 `app_home_opened` イベントを加えてください (ステップ 3)。そして緑の *Save Changes* ボタンで保存します。(ステップ 4).
![events.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339293/35db6b0b-72b9-7743-08c1-3358065a059e.png)

次は同じように **Features > Interactive Components** で Slack サーバにインタラクティブ・ペイロードの送信先を伝える必要があります。 ここでも Request URL を指定してください。URL は `https://プロジェクト名.glitch.me/slack/actions` となります。そしてここでも *Save Changes* ボタンで変更を保存してください。

ここで一度インストールしましょう。 **Features > OAuth & Permissions** からインストールボタンで自分のワークスペースにこのアプリをインストールします。画面の指定にしたがってインストールしてください。一旦 OAuth を使ってのインストールプロセスが終了しましたら、次の場面でアクセス・トークンが発行されます。

さて、次はブラウザ上で使えるIDEの Glitch プロジェクト、またはお使いのコード・エディタに移りましょう。インストール終了時に発行された `xoxb-` トークンをコピーし、環境変数ファイル、**.env** の `SLACK_BOT_TOKEN`の値としてペーストしてください。
![Glitch .env](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339293/36a3b197-982f-4c6c-ba22-bfca357f12c4.png)

もう一つの環境変数である、 Signing Secret キーは、Slack App マネージメント画面の **Settings > Basic information** から取得することができます。


## 🖼 App Home を表示させよう

### Express server で Node アプリをセットアップ

このチュートリアルでは、Node.js と Express サーバを使ってアプリを書いています。ここでは全ての API コールは、ごく一般的な HTTP リクエスト、レスポンスで行っていますので、Node 以外の言語をお使いの皆さんにも、ソースコードをみてもらえればどこでどのように API を呼び出しているかわかっていただけるかと思います。

>⚡️ もっと簡単に Bolt フレームワークで書きたい！という方のためにも [Bolt で書かれたソースコードも用意しています](https://glitch.com/edit/#!/apphome-bolt-demo-note?path=README.md:1:0)。しかしこのチュートリアルの説明そのものは Bolt や SDK、他のボット・フレームワークを使わない「バニラ」コードでの説明となります。

まず Node コードで、依存モジュールを追加して Express サーバを走らせます。そして raw リクエスト・ペイロードを使ってリクエスト情報の認証をします。詳しい説明は割愛しますが、[index.js の 31 - 38 行目を参照](https://glitch.com/edit/#!/apphome-demo-note?path=index.js:10:0)してください。 143 -145 行目では Express サーバを実行しています。

リクエスト情報の認証についてもう少し詳しく知りたい方は、以前書いたチュートリアルの **Slack メッセージ・アクション API を使ってディスカバラブルなアプリを作ろう** の中のセクション、[🔐 リクエスト情報の認証](https://qiita.com/girlie_mac/items/21fedcc6ce07aa44a670#-%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E6%83%85%E5%A0%B1%E3%81%AE%E8%AA%8D%E8%A8%BC))を読んでみてください。


### `app_home_opened` イベント・ペイロードを受け取るアプリケーション・エンドポイント

次に HTTP POST のルーティングメソッドを使って、イベントペイロードを受け取るエンドポイントを定義します（前のステップで設定した、Request URL）。何かのイベントが発生した際にはこのエンドポイントに Slack API からの JSON ペイロード情報が届くので、そこでイベントが  `app_home_opened` であるかチェックし、そうであれば App Home ヴューを表示させる準備をします。

![app_diagram_app_home_opend.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339293/b76f8978-18cf-d5fb-9286-d99d24205630.png)

ここでは、読みやすさを考え、簡略化されたコード・スニペットを使っていますが、全てみたい方は、 [index.js 45 - 70 行目](https://glitch.com/edit/#!/apphome-demo-note?path=index.js:44:0))をみてください: 


```js
app.post('/slack/events', async(req, res) => {
  const {type, user, channel, tab, text, subtype} = req.body.event;

  if(type === 'app_home_opened') {
    displayHome(user);
  }
}
```
さて、ビューの表示には、リッチなコンテンツを [Block Kit](/messaging/composing/layouts) を使って構築しましょう:

```js
const displayHome = async(user, data) => {
  
  const args = {
    token: process.env.SLACK_BOT_TOKEN,
    user_id: user,
    view: await updateView(user)
  };
  const result = await axios.post('/views.publish', qs.stringify(args));
};
```

Home App にコンテンツを表示させるには `view.publish` メソッドを使います。 ここでは `axios` モジュールをを使って HTTP POST 経由で API を呼び出しています。


### Block Kit でリッチ・コンテンツを構築

ここでの例では、別の関数、`updateView` を呼んでコンテンツを JSON で構築しています。この関数は Home ビュー UI を更新する毎に呼びだしています。

初期 UI の設定はこのようになります:


```js
const updateView = async(user) => {
    let blocks = [ 
    {
      // Section with text and a button
      type: "section",
      text: {
        type: "mrkdwn",
        text: "*Welcome!* \nThis is a home for Stickers app. You can add small notes here!"
      },
      accessory: {
        type: "button",
        action_id: "add_note", 
        text: {
          type: "plain_text",
          text: "Add a Stickie"
        }
      }
    },
    // Horizontal divider line 
    {
      type: "divider"
    }
  ];
  
  let view = {
    type: 'home',
    title: {
      type: 'plain_text',
      text: 'Keep notes!'
    },
    blocks: blocks
  }
  
  return JSON.stringify(view);
};
```
`blocks` 配列部分は、[Block Kit をつかったプロトタイプはこちらから見ることができます](https://api.slack.com/tools/block-kit-builder?mode=appHome&view=%7B%22type%22%3A%22home%22%2C%22blocks%22%3A%5B%7B%22type%22%3A%22section%22%2C%22text%22%3A%7B%22type%22%3A%22mrkdwn%22%2C%22text%22%3A%22*Welcome!*%20%5CnThis%20is%20a%20home%20for%20Stickers%20app.%20You%20can%20add%20small%20notes%20here!%22%7D%2C%22accessory%22%3A%7B%22type%22%3A%22button%22%2C%22action_id%22%3A%22add_note%22%2C%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Add%20a%20Stickie%22%7D%7D%7D%2C%7B%22type%22%3A%22divider%22%7D%5D%7D)。

実際のコードでは、この関数では、モーダルからユーザ入力された値を使い動的なコンテンツを扱います。この部分は後ほど。


### ユーザのボタンクリックのハンドリング

ユーザがボタンをクリックするとモーダルが開きます。
![app_diagram_button_click.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339293/8dd51d13-50ed-b927-83c5-db1cc72756d8.png)

Block Kit メッセージ・ビルディングブロックには `action_id` を定義してください。これはデータを受け取る識別子となります。（ここでは `add_note`)。

ユーザがボタンをクリックすると API サーバから Request URL に送信されるこのアクションについてのペイロードには `trigger_id` が含まれます。これはモーダルを開くための識別子となります。


```js
app.post('/slack/actions', async(req, res) => {
  const { token, trigger_id, user, actions, type } = JSON.parse(req.body.payload);
  if(actions && actions[0].action_id.match(/add_/)) {
    openModal(trigger_id);
  } 
});
```


### モーダルを開く

次に入力フォーム・エレメントを、モーダルの中に表示しましょう。この例ではごくシンプルに、付箋にメモするテキストを入力するマルチライン（複数行）インプット・ボックスと、付箋の色を指定するドロップダウン・メニューを表示させます。

モーダルを表示させるには `views.open` メソッドを呼びます:

```js
const openModal = async(trigger_id) => {
  
  const modal = {
    type: 'modal',
    title: {
      type: 'plain_text',
      text: 'Create a stickie note'
    },
    submit: {
      type: 'plain_text',
      text: 'Create'
    },
    blocks: [
      // Text input
      {
        "type": "input",
        "block_id": "note01",
        "label": {
          "type": "plain_text",
          "text": "Note"
        },
        "element": {
          "action_id": "content",
          "type": "plain_text_input",
          "placeholder": {
            "type": "plain_text",
            "text": "Take a note... "
          },
          "multiline": true
        }
      },
      
      // Drop-down menu      
      {
        "type": "input",
        "block_id": "note02",
        "label": {
          "type": "plain_text",
          "text": "Color",
        },
        "element": {
          "type": "static_select",
          "action_id": "color",
          "options": [
            {
              "text": {
                "type": "plain_text",
                "text": "yellow"
              },
              "value": "yellow"
            },
            {
              "text": {
                "type": "plain_text",
                "text": "blue"
              },
              "value": "blue"
            }
          ]
        }
      }
    ]
  };
  
  const args = {
    token: process.env.SLACK_BOT_TOKEN,
    trigger_id: trigger_id,
    view: JSON.stringify(modal)
  };
  
  const result = await axios.post('/views.open', qs.stringify(args));
};
```

一見長いコードですが、ほとんどは JSON で UI を描いているだけです。 [Block Kit プロトタイプはここから見る](https://api.slack.com/tools/block-kit-builder?mode=modal&view=%7B%22type%22%3A%22modal%22%2C%22title%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Create%20a%20stickie%20note%22%2C%22emoji%22%3Atrue%7D%2C%22submit%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Create%22%2C%22emoji%22%3Atrue%7D%2C%22blocks%22%3A%5B%7B%22type%22%3A%22input%22%2C%22block_id%22%3A%22note01%22%2C%22element%22%3A%7B%22type%22%3A%22plain_text_input%22%2C%22placeholder%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Take%20a%20note...%20%22%7D%2C%22multiline%22%3Atrue%7D%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Note%22%7D%7D%2C%7B%22type%22%3A%22input%22%2C%22element%22%3A%7B%22type%22%3A%22static_select%22%2C%22action_id%22%3A%22color%22%2C%22options%22%3A%5B%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22yellow%22%7D%2C%22value%22%3A%22yellow%22%7D%2C%7B%22text%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22blue%22%7D%2C%22value%22%3A%22blue%22%7D%5D%7D%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Color%22%7D%7D%5D%7D)ことができます。


### フォーム・サブミッションのハンドリング

ユーザからのフォーム・サブミッションのハンドリングは、ボタンクリックのハンドリングと同じようにすることができます。
![app_diagram_modal_submit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339293/29a3672d-b31a-5b5d-2ec4-796801c3d478.png)

モーダル内のフォームがサブミットされた際には、このアクションのエンドポイントにペイロードが届きます。ボタンクリック時と区別するにはペイロードに含まれる `type` の値をチェックしてください:


```js
app.post('/slack/actions', async(req, res) => {
  const { type, user, view } = JSON.parse(req.body.payload);
  else if(type === 'view_submission') {
    res.send(''); // Make sure to respond to the server to avoid an error
    
    const data = {
      note: view.state.values.note01.content.value,
      color: view.state.values.note02.color.selected_option.value
    }
    displayHome(user.id, data);
  }
});

```
この部分のコード全てを見るには [ index.js 107 - 133 行](https://glitch.com/edit/#!/apphome-demo-note?path=index.js:106:0)を参照してください。

### App Home ビューの更新

ここでユーザから追加されたデータを元のデータに追加して、 Home タブの内容を
 [`views.publish`](/methods/views.publish) メソッドと使って書き換えます。

このサンプルコードでは、単純化するために永続データの格納に [`node-json-db`](https://www.npmjs.com/package/node-json-db) モジュールを使用しています。ユーザが新しいメモを追加する毎に、そのデータが下のデータ配列にプッシュされていきます。

その新しいデータを、元の JSON にアペンドしてできた新たな UI ブロックを、 `views.publish` メソットで再表示しています。

実際のソースコードは [appHome.js の 17 - 152 行](https://glitch.com/edit/#!/apphome-demo-note?path=appHome.js:17:1)をみてみてください。このあたりは自分の好きなように処理してみてください。

### アプリを実行してみよう

さて、これで一通りアプリが動くはずですので試してみましょう。Slack クライアントのサイドメニューの **App** でみられるアプリ一覧からこの Stickies! アプリを探してクリックしてみてください。無事、App Home が表示されましたか？

Add a Stickie ボタンを押して、新しい付箋が Home ビューに反映されたか確認してみてください。🎉
![tada_it_works.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339293/ca70b1ef-5682-f72a-8ce0-af8085590381.png)

## よりよいユーザ・エクスプリエンスを確立するには

さて、このチュートリアルが、新しいアプリを作る、もしくは既存のアプリをアップデートするためのインスピレーションになっていただけたらうれしいです。

ここでは `views` メソッドを用いた基本的な App Home 構築について書きましたが、次のチュートリアルでは、[Shay DeWael （シェイ・デワエル）](https://qiita.com/shaydewael)がベスト・プラクティスについて書いてゆきますので乞うご期待！

## 📄Related Slack API Documentation
- [Surfaces for building Slack apps](https://api.slack.com/surfaces) *Slack Platform documentation*
- [app_home_opened Event type](https://api.slack.com/events/app_home_opened) *Slack Events API documentation*
- [Using `app_home_opened` for onboarding] (https://api.slack.com/reference/app-home) *Slack Platform documentation*

---
*ご質問などがありましたら、私、 Tomomi [@girlie_mac](https://twitter.com/girlie_mac) か [@SlackAPI](https://twitter.com/slackAPI)へ日本語もしくは英語でツイートするか、feedback@slack.com に連絡ください！*

原文(英語）: [Building a home for your app 🏡](https://api.slack.com/tutorials/app-home-with-modal) by Tomomi Imura (Slack)