こんにちは! Wantedly で Web エンジニアをしている木村( @hachiblog )です。8月にβ版をリリースした、コンディション・マネジメントサービスの Wantedly Pulse を開発しています。
Pulse は毎週の調子を記録する機能や、社内のメンバーを称賛できる機能を提供していますが、そのほとんどを Slack アプリ内で実現しています
その他にも Wantedly Visit で募集に応募があった際は Slack に通知を送ることができるなど、Wantedly プラットフォームのいろんなところで Slackアプリが活用され始めています。この記事はこれから Slack アプリを開発する方がスムーズに開発を始められるように書きました。
Slack アプリでは、bot としてメッセージを送ったり Web hook を受け取ったりだけでなく、メッセージにボタンなどのインタラクティブな要素を追加したり、モーダルや Slack アプリの home タブなどリッチな UI を作ることが可能です。
それゆえ、Slack API は提供されている機能が多くなっています。基本的には 公式ドキュメント を参照すると全て書いてありますが、一部知らなくて困ったことやドキュメントで分かりづらいところがあったため、 自分が最初これを知っていれば開発をうまく進められた!と思うことを書いています。 公式リファレンスを参照しつつ、虎の巻的に用いていただけるといいと思います。
目次 Wantedly Slack アプリの構成 Slack API 概要 通信の種類 blocksとInteractive Component column: blocks のエラーは分かりづらい Surfaces column: Ephemeral メッセージについて よくある Slack API とのやりとりの例〜interactivity〜 Access token と Scope Access tokenの種類 Scope の更新 Events API における Scope ツール おわりに Wantedly Slack アプリの構成 Slack アプリ一般の話をするために例があるとわかりやすいので、Wantedly Slack アプリを例に用いて説明します。Wantedly Slackアプリは上の図のような構成になっています。真ん中から右の点線枠内が Wantedly 側のシステム、左の点線枠外が Slack 側です。Wantedly 側のそれぞれ実線の四角は Kubernetes namespace を表していて、それぞれ別の責務を持ったマイクロサービスが協調してサービスを構成しています。
この図のとおり、 slack-app というサービスが Slack API とのすべての通信を担っています。pulse や wantedlyなどのサービスからデータを受け取って Slack メッセージを生成して送ったり、Slack からのイベントを受けてそれぞれのサービスにデータを送信したりしています。
Slackから来たリクエスト(後述する Event API や Interactive Component によるリクエスト)は 3秒以内 に Slack API にレスポンスを返す必要があります。そのため、slack-appでは全てのリクエストに対して、非同期ジョブをキューイングする処理のみを行い、レスポンスを即時に返す実装になっています。実際のリクエストに対する処理は、非同期ジョブの中で実行されます。
また、slack-app の重要な責務として、Slack アカウントと Wantedly ユーザーアカウントの紐付けや Slack Access token の管理もしています。
Slack API 概要 Slack は使ったことがあるけど、 Slack API は使ったことがないという方に向けて、Slack API についてざっくり解説します。また、前述の Wantedly Slack アプリを例に Slack API の使い方を説明します。
通信の種類 Slack APIには大きく3種類通信があります。(この他にもショートカットやスラッシュコマンドを設定できますが、 主要なのはこの3つです)
slack-app から Slack API にリクエストを送って Slack ワークスペースを操作したり情報を取得する Web API Slack ワークスペース上でのイベントを slack-app に送ってくれる Events API Interactive Component (後述) の操作を送ってくれる Interactivity Web API はslack-app側(アプリ開発者側)が Slack API に送るもの、それ以外の2つは Slack API から slack-app に送られるものという認識を持っておくと理解しやすいです。
Event API や Interactivity を用いるには下の画像のように Slack アプリ設定画面でリクエスト先のURLを設定する必要があります。そのほかにも設定画面は Slack アプリの機能が整理されていて、ドキュメントへのリンクも多いので初めは管理画面でおおよそできることを把握するといいと思います。
blocksとInteractive Component Slackには豊富なUIコンポーネントが用意されています。あなたも Slack を使っていればアプリの通知を取り消すボタンやテキスト入力欄、複雑なレイアウトのメッセージを見たことがあると思います。それらはすべて Slack では blocks と呼ばれています。
blocks はその実態は json のメッセージです。モーダルや、通常のメッセージも blocks で作られています。次の json は Slack に送るリクエストの一例です。このリクエストを送ると図3のようなメッセージが表示されます。blocks というフィールドがあり、その中に表示内容が記述されているのがわかります
{
"type": "message",
"subtype": "bot_message",
...省略...
"blocks": [
{
"type": "section",
"block_id": "8iOMA",
"text": {
"type": "mrkdwn",
"text": "*良い :smile: で回答しました*",
"verbatim": false
}
}
],
...省略...
}
blocks はいろいろなコンポーネントによって構成されていますが、Interactive Component はその中でも特別なコンポーネントです。Interactive Component を用いると、Slackのメッセージやモーダル内でユーザーが入力できる要素を表示できます。下の画像は Wantedly Slack アプリで Interactive Component を用いて作っているモーダルです。
テキストを入力したり、項目を選択したりすると、slack-appに入力や選択が送信されます。モーダルの場合は送信ボタン(画像における「伝える」)ボタンを押すとまとめて送信することも可能です。
blocks については、 「失敗しないSlackアプリの作り方~高UX実現のためのBlock kitの使いどころ~」 というスライドが大変参考になるので、目を通しておくと感じがつかめると思います。また、後述する Block Kit Builder や Slack Developer Tools を用いるとより理解が深まり、手早く blocks を作成することができます。
column: blocks のエラーは分かりづらい blocks はいろいろな制約があります。たとえば、Actions Block は、5つのコンポーネントしか内部にコンポーネントを持てなかったり、 block 内で action_id は unique であることを求められたりします。それ自体の記載はリファレンスにあるのですが、実際に chat.postMessage で制約に違反したリクエストを投げても invalid_argument というエラメッセージしかエラーの情報は帰ってきません。
情報が少ないため、開発中のデバッグはもちろん、本番でエラーが起きたときに何でエラーが起きたのか突き止めるのが難しいので注意してください。
現状私達は blocks を生成するコードをコメントアウトして原因のコードを二分探索的に探したうえで、原因のコンポーネントのリファレンスを見つつ対処していますが、典型的なエラーは Slack API に頼らずリクエストする前に slack-app の方で validation をかけようと考えています。
Surfaces Slack では主要な UI が3種類あります。そのどれもが blocks によって構成されており、Slack では Surfaces と呼ばれています。すでに例として用いているものもありますが、ここではそれぞれの Surface を送信するメソッドと共に紹介します。ここで紹介しているメソッドは Web API の中でもかなり高頻度で使うことになるはずです。
Messages いうまでもなくメッセージの形式で送信します。Web API の chat.postMessage を用いると、送信できます。chat.update を用いると更新できたり、 chat.postEphemeral を用いると送信対象の本人だけに見えるメッセージが表示できたりします。 Modals 先程の画像のようなモーダルを送信します。Web API の views.open を用いると送信でき、 views.update で表示しているモーダルを更新したり、 views.push ですでにあるモーダルの上にモーダルを重ねたりすることができます。 The Home tab 次の画像のように、Slack アプリの設定や常に表示しておきたい情報を表示する画面を描画できます。Web API の views.publish を用いると更新できます。更新タイミングは任意ですが、Wantedly Slack アプリでは、 Event API の app_home_opened というイベントでアプリが開かれたことが分かるので、そのタイミングで基本的には更新しています。 Surface によって、blocks でつかえるコンポーネントが制限されていることがあるのでドキュメントを読んでうまく使い分けてください。現状テキストボックスは Messages surface では使えないようです。
column: Ephemeral メッセージについて Ephemeral メッセージは下の画像のように、送信対象の人にしか見ることができないメッセージです。これを送信すること自体は前述のとおりchat.postEphemeral メソッドを使えば簡単に送れますが、たとえばもしこのメッセージにボタンがついていてボタンを押したときにこのメッセージを更新したり削除したりしたい場合、通常のメッセージとは違う方法を取る必要があります。なぜならメッセージを送ったアプリ自信も Ephemeral メッセージは見えないし認識できないからです。
そんなときは、ボタンを押したときに slack-app に送られる Interactivity のリクエストパラメータに含まれている、 response_url フィールドを用いることで操作できます。
response_url は発行されてから30分間に最大5回まで利用でき、発行元の blocks を操作するリクエストを投げることができます。たとえばメッセージを更新したい場合はこのようにリクエストを送ります。
Ephemeral メッセージの更新時に送るリクエスト
POST https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
Content-type: application/json
{
"text": "Thanks for your request, we'll process it and get back to you.",
"response_type": "ephemeral"
}
メッセージを削除したい場合は、下のようにリクエストを送ることができます。
Ephemeral メッセージの削除時に送るリクエスト
POST https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
Content-type: application/json
{
"delete_original": "true"
}
https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX が送られてきた response_url です。
response_url の詳しい使い方は 公式ドキュメント と、 Slack Japan の 瀬良さんの Qiita の記事 が大変参考になります。
よくある Slack API とのやりとりの例〜interactivity〜 ここまでの説明から、実際に Wantedly Slack アプリで起きている通信を例に、API の使い方を説明します。Wantedly Slack アプリには、毎週会社のメンバーに調子を聞く機能があります。会社が設定した時間になるとメンバーに調子を聞くメッセージが送られます。
下の画像のように、blocks をパラメータに含めて、chat.postMessageメソッドで Slack API にリクエストを送ると、メンバーにメッセージが送信されます。(他にもリクエストに必要なパラメータはありますが、省略しています)
その後、ユーザーが Slack 上の「とても良い」ボタンをクリックするとのように、Interactivity によって slack-app の/slack/interactiveにリクエストされ、ボタンがクリックしたことを slack-app で受け取れます。
このようなやり取りをすることで、Web API で送ったメッセージやモーダル内の Interacitive Component の操作を slack-app で受け取ることができます。このように Slack と相互に通信することでアプリの機能を実現しています。
Access token と Scope Slack アプリはワークスペースに対する操作が大半を占めるので、ほぼすべてのリクエストに Access token が必要になります。また、Scope を設定することによってアプリが使える Slack API のメソッドを指定できます。他のサービスの API を使ったことのある方は Scope の概念に馴染みがあるかもしれませんが、Slack 特有の仕様もいくつかあるので注意してください。
Access tokenの種類 Slack API の Access token は2020年12月現在、 bot token と user token の2種類あり、似たような Scope でもできることがかなり違う場合があるので注意が必要です。
たとえばどちらの token にも、 channels:history Scopeがありますが、 bot token では bot が参加しているチャンネルしかメッセージ内容を取得できないのに対して、 user token では public チャンネルすべての情報を取得することができます。同じようなAPIメソッドを利用しても得られる結果が token の種類によって違うということです。注意して token の種類と Scope を選んでください。
それぞれの token の Scope で何ができるようになるかは bot token の場合は、Slack API reference の Scope 一覧を見ると大体わかります。user token は少しむずかしいです。user token は Scope によって bot token の権限を拡張したものや、 bot token とはまったく違う権限になっていることもあります。
基本的な考え方はユーザーに成り代わっていろんなことを実行できるのですが、実際にできることのイメージは Scope によって違うので、
管理画面で user token の Scope の説明をちゃんと読むこと 期待する動作をするかをプロトタイプを作って確かめること がより必要になってきます。
Scope の更新 アプリ側で利用している Scope を更新しても、ユーザーごとの token の Scope が更新されるわけではありません。ユーザーそれぞれに Access token の更新をしていただく必要があります。
また、Slack アプリの Access token は更新しても token の文字列自体に変化はなく、Scope が変わっても token 自体から気づくことはできません。token それぞれがどの Scope を持っているのかを記録しておく必要があります。
Wantedly Slack アプリでは、アプリが要求する Scope を更新した日をドキュメントとして残しており、token を記録しているレコードの最終更新日と比較して token の持っているスコープを判断しています。
Events API における Scope Events API では、 subscribe するイベントを選択できますが、その際にも Scope が必要なイベントがあります。下の画像の Required Scope がそれに当たります。Scope を追加した場合はユーザーに更新してもらうまで新しい Scope が必要なイベントは subscribe できません。前述の bot token、user token でも受け取れるイベントは違うので注意しましょう。
ツール Slack アプリを開発するときに使うと便利なツールを紹介します。
ngrok ローカルで開発する場合、何も工夫しないと Slack からのリクエストを受け取る事ができません。私達は Slack API の tutorial でも紹介 のある、 ngrok を用いて Slack からのリクエストをローカルのサーバーに届くようにしています。
$ ngrok http 8080
と書くだけで、ngrok が生成した URL へのリクエストがローカルサーバーに届きます。この場合 ngrok はランダムな URL を発行します。Event APIや Interactivity はリクエスト先の URL を必要があるのでランダムなURLだと毎回 Slack アプリ設定画面に行って設定を変える必要があり不便です。
ngrok は無料プランの場合、8時間でセッションが無効になり、毎回Slack側で設定をしなおす必要があります。有料プランを使うと固定の URL を発行できるようになり、その手間が省けるので、本格的に開発する際は有料プランの利用がおすすめです。
Block Kit Builder blocksとInteractive Component で説明をした Slack 上のUIを構成する blocks は json で書かれていますが、パッと表示を確かめたりレイアウトを作ったりするのは素の json では難しいです。
Slack には Block Kit Builder という神ツールがあります。実際に生成される json とメッセージの表示を見ながらいろんなメッセージや UI を試すことができます。Slack で送れるメッセージの種類を色々と確かめるのにもいいと思います。
Slack developer tools Slack 上で他のアプリのメッセージや、昔自分が作ったメッセージがどのような blocks で構成されているのか気になる事があると思います。そのような場合に Slack developer tools が便利です。
ワークスペースに Slack developer tools をインストールしておくと、メッセージを作っている json を見ることができ、前述のBlock Kit Builder で見たりいじったりもできて大変便利です。
changelog Slack API は開発速度が早く、新機能が頻繁に開発されていたり、たまに breaking change があったりします。先日もチーム内であったらいいのにと話していた機能が偶然翌日に実装されていて驚いたことがありました。
そのような変更にいち早く気づくために changelog の RSS feed を subscribe するのがお勧めです。私達は Pulse を開発しているメンバーがいる Slack チャンネルにメッセージが来るようにしています。
Slack チャンネルに送られてきているメッセージ例
おわりに 以上、駆け足になりましたが、Slackアプリを開発する前に知っておきたいことを書きました!Slackアプリ開発を始める全人類の参考に少しでもなれば嬉しいです。
この記事で Pulse の開発に興味を持っていただけたらさらに嬉しいです!ぜひ気軽に話を聞きに来てください!