Slack メッセージ・アクション API を使ってディスカバブルなアプリを作ろう
Slack メッセージ・アクション API を使ってディスカバブルなアプリを作ろう:
原文 Tutorial: Developing an Action-able App by Tomomi Imura (Slack)
Slack には、ユーザーがメッセージに対して絵文字リアクションやシェアを行う標準機能がありますが、Actions (アクション) 機能を使うとユーザーがメッセージを送信したときにアプリを起動することもできます。
例えば、メッセージから直接プロジェクトマネージメントアプリのタスクを作成したり、バグトラッカーアプリにバグを送ったり、メッセージ内容をヘルプデスクにコピーして送信したりなど、いろいろなことが可能になるのです。この機能をうまく自分のアプリに取り込めば、より多くのユーザーにあなたのアプリを知ってもらうことにもなるでしょう。
というわけで、このチュートリアルでは、この API を使ってアクショナブルなアプリを作る説明をしていきたいと思います。
これから作るアプリは「ClipIt! for Slack」というアプリです。あなたが架空の ClipIt! (クリップ・イット)というサービスをすでに運営していると仮定し、そのサービスに対応するスラックアプリを作るという設定でいってみましょう。このウェブサービスは、ユーザーがインターネット上で「クリップ」(保存)したテキストをデータベースに保存、同期していきます。さて、あなたはこのサービスを拡張してスラックのメッセージも保存できるようにしたいと思っています。と、いうことで今からこのアクション機能を使ってユーザーがスラック上のメッセージをクリッピングできるようにしてみましょう。
このスラックアプリは下のように動作します。
ユーザーインタラクションは次のような感じになります。
���� ソースコードは GitHub にありますが、このチュートリアルでは説明しやすいようにさらに簡略化したコードを使っています。そちらのソースは Glitch という、Node アプリをウェブブラウザで書いて実行させることができるウェブ IDE 上に置いてありますので、そのコードをこのリンク https://glitch.com/edit/#!/remix/slack-clipit-simplified から "remix" してください。Glitch の remix とは、GitHub の fork のような機能で、リミックスしたコードは自分のリポジトリとなりますので、好きなように書き換える事ができます。
まずは、開発用に使える Slack ワークスペースにサインインしてください。今からそのワークスペース上で新規のアプリを作成、インストールしていきます。新規アプリは、 https://api.slack.com/apps の Create New App ボタンから作成してください。ボタンをクリックすると表示されるダイアログ内でアプリ名を入力し、開発用ワークスペースを選択してください。
Create App と書かれた緑のボタンをクリックしてください。
次に Basic Information の App Credentials セクションまでスクロールします。ここにはアプリの API 認証キーがいくつかがありますが、このチュートリアルでは Signing Secret を使います。
この Signing Secret は隠されている状態ですが、まずそれを表示させ、その値を Node アプリのルートにある .env ファイルに 環境変数
もう少し下にスクロールしていくと Display Information がありますのでそこでアプリのアイコンや詳細など編集することができます。
次に、左のメニューから Interactive Components をクリックして、そのページトップになる Interactivity をオンにしてください。するとフィールドが表示されます。
ここでは Request URL を入力します。このリクエスト URL とは、ユーザーがアクション機能を呼び出した際にスラックの API サーバーから送信されるペイロードデータの受け取り場所となる URL と考えてください。
注:この URL はあなたのアプリが稼働しているサーバーの URL となりますが、このチュートリアルでは Glitch を使っていますので、リクエスト URL は https://my-project.glitch.me/actions のようになります。この my-project 部分は各自異なる文字列になっています。確認してみてください。もし ngrok などの他のサービスでローカルホストをトンネルする場合はこのサービスの URL を使用してください。ngrok については Using ngrok to develop locally for Slack (英語)を参考に。
Request URL を設定し終わったら、Actions までスクロールし、Create New Action ボタンをクリックして、次のように入力します。
Create ボタンを押し、次に場面で Save Changes をクリックしてください。
次に左のメニューから Bot Users へいきます。そこで Add a Bot User をクリックすると次にようにボットの名前の設定ができます。
ページの下になる Add Bot User をクリックして、Save ボタンもクリックします。
次は OAuth & Permissions へ行き Install App to Workspace をクリックして一旦このアプリをインストールします。インストール画面が表示されますのでそのまま続行し、ワークスペースにインストールしてください。し終わってから OAuth & Permission ページに戻ると access tokens が表示されていますのでそれを取得します。トークンは他のキー同様、 .env ファイルに保存します。
次に、同じページ内でパーミションスコープを有効にする必要があります。下にスクロールして Scopes セクションまで行き、
さて、アプリの設定がようやく終わりました!次は早速アプリのコーディングです。
冒頭でも述べたように、このチュートリアルでは Slack API そのものの使い方を説明していますので、Node.js の Express.js モジュールなどを使用して直接 API を呼んでいます。
さて、まず依存するモジュールをインストールしていきましょう。 POST リクエスト実行のための
次はコード部分をみていきましょう。少しづつコード・スニペットで説明していきますので後からどんどんこのコードに追加・編集していきます。
まず、 index.js を作成し、Express アプリのインスタンス化し適当なポートナンバーでサーバを接続します。
次に進む前に、まず今から構築していくスラックアプリがどう作動するかの説明を示したダイアグラムをみてみましょう。
各フローは、ユーザーがメッセージメニューからアクションを起こした時に始動します。ここで
受け取りのエンドポイント側では、下のように書くことができます。この時、先に設定したパスである
13行目では、イベントタイプが
先のコード・スニペットの
33行目では axios モジュールを使って POST リクエストをスラックに送信しています。
同様に、このダイアログがユーザーによって送信された際も先ほどと同じエンドポイントが呼び出されます。少し上の code snippet 2 の
この時も、ダイアログが無事に返信されました、とサーバーに伝える必要があるので、ここでは空の HTTP 200 リスポンスをまず送り返します。(4 行目)
次に、クリップされたメッセージをデータベースに保存するという仮定で進めます。(コードの 7 行目ではデータベース部分は擬似コードで省略してありますのでこのまま使うとエラーになります。)保存を同期がおわった時点で
この最後の確認メッセージの過程は、アプリのユーザー・エクスペリエンスのためには非常に重要ですので、この先新しいアプリを作っていく際にもぜひ、ユーザー視点に立って使いやすさについて考えてみましょう。
さて、では一旦このコードを実行してみましょう。これでメッセージメニューに、このアプリのアクションが追加されているはずなので、クリックしてみてください。架空のデータベースパート以外はきちんと動作していることを確認してください。
では最後に、アプリのセキュリティ面を改善しましょう。
ここまでのコードでも動作はしますが脆弱性があります。エンドポイントで受け取ったリクエストが本当にスラックから来たものなのかがわからないからです。それをリクエスト毎に確認をする必要があるので、今から signing secretsを使って認証してみましょう。
この Signing secrets (サインイング・シークレット、サイン認証)は、今まで Slack API で使われてきた verification tokens に代わるもので、セキュリティ面をさらに強化するために、各リクエストごとに HTTP ヘッダーに
ここでの
この例ではすでに Express と body-parser を使っていますので、body-parser の
始めのコード・スニペット1 に戻りましょう。
実際の暗号化に関する関数は verifySignature.js のほうですでに用意しましたので、あとはこのファンクションを index.js の冒頭部に追加するだけです。
そして、Snippet 2 の、イベント・エンドポイントでこのサインイング・シークレットをハッシュを比較することによってリクエストの認証をします。
この例では、イベント発生時のエンドポイントでのみに認証をしていますが、この先スラックアプリを構築する際はペイロードを受け取る時に逐一、認証をする必要があります。詳しくは Verifying requests from Slack をお読みください。
さて、Node コードをもう一度実行してみましょう。おめでとう、Action-able なアプリの完成です!
Questions? Comments? コメントや質問はここ、もしくは Tomomi @girlie_mac か Slack Platform @SlackAPI へツイートしてくださいね。
原文 Tutorial: Developing an Action-able App by Tomomi Imura (Slack)
Slack には、ユーザーがメッセージに対して絵文字リアクションやシェアを行う標準機能がありますが、Actions (アクション) 機能を使うとユーザーがメッセージを送信したときにアプリを起動することもできます。
例えば、メッセージから直接プロジェクトマネージメントアプリのタスクを作成したり、バグトラッカーアプリにバグを送ったり、メッセージ内容をヘルプデスクにコピーして送信したりなど、いろいろなことが可能になるのです。この機能をうまく自分のアプリに取り込めば、より多くのユーザーにあなたのアプリを知ってもらうことにもなるでしょう。
というわけで、このチュートリアルでは、この API を使ってアクショナブルなアプリを作る説明をしていきたいと思います。
"ClipIt! for Slack" を作る
これから作るアプリは「ClipIt! for Slack」というアプリです。あなたが架空の ClipIt! (クリップ・イット)というサービスをすでに運営していると仮定し、そのサービスに対応するスラックアプリを作るという設定でいってみましょう。このウェブサービスは、ユーザーがインターネット上で「クリップ」(保存)したテキストをデータベースに保存、同期していきます。さて、あなたはこのサービスを拡張してスラックのメッセージも保存できるようにしたいと思っています。と、いうことで今からこのアクション機能を使ってユーザーがスラック上のメッセージをクリッピングできるようにしてみましょう。このスラックアプリは下のように動作します。
ユーザーインタラクションは次のような感じになります。
- まずユーザーがメッセージにマウスポインタを合わせ、表示された … メニューから Clip the message を選択
- ダイアログが表示されるのでユーザーはその中のフォームに、必要な情報を編集または追加
- ユーザーが送信 (Clipit) ボタンを押す
- ClipIt! for Slack がそのメッセージを ClipIt データベースに追加し同期(ここは架空の過程)
- ClipIt! for Slack が そのユーザーに DM で完了したことを通知
���� ソースコードは GitHub にありますが、このチュートリアルでは説明しやすいようにさらに簡略化したコードを使っています。そちらのソースは Glitch という、Node アプリをウェブブラウザで書いて実行させることができるウェブ IDE 上に置いてありますので、そのコードをこのリンク https://glitch.com/edit/#!/remix/slack-clipit-simplified から "remix" してください。Glitch の remix とは、GitHub の fork のような機能で、リミックスしたコードは自分のリポジトリとなりますので、好きなように書き換える事ができます。
⚙️ アプリの作成と設定
まずは、開発用に使える Slack ワークスペースにサインインしてください。今からそのワークスペース上で新規のアプリを作成、インストールしていきます。新規アプリは、 https://api.slack.com/apps の Create New App ボタンから作成してください。ボタンをクリックすると表示されるダイアログ内でアプリ名を入力し、開発用ワークスペースを選択してください。Create App と書かれた緑のボタンをクリックしてください。
次に Basic Information の App Credentials セクションまでスクロールします。ここにはアプリの API 認証キーがいくつかがありますが、このチュートリアルでは Signing Secret を使います。
この Signing Secret は隠されている状態ですが、まずそれを表示させ、その値を Node アプリのルートにある .env ファイルに 環境変数
SLACK_SIGNING_SECRET
として保存します。このシークレットキーの使い方についてはのちの 「リクエスト情報の認証 」セクションで説明します。SLACK_SIGNING_SECRET=15770a…
次に、左のメニューから Interactive Components をクリックして、そのページトップになる Interactivity をオンにしてください。するとフィールドが表示されます。
ここでは Request URL を入力します。このリクエスト URL とは、ユーザーがアクション機能を呼び出した際にスラックの API サーバーから送信されるペイロードデータの受け取り場所となる URL と考えてください。
注:この URL はあなたのアプリが稼働しているサーバーの URL となりますが、このチュートリアルでは Glitch を使っていますので、リクエスト URL は https://my-project.glitch.me/actions のようになります。この my-project 部分は各自異なる文字列になっています。確認してみてください。もし ngrok などの他のサービスでローカルホストをトンネルする場合はこのサービスの URL を使用してください。ngrok については Using ngrok to develop locally for Slack (英語)を参考に。
Request URL を設定し終わったら、Actions までスクロールし、Create New Action ボタンをクリックして、次のように入力します。
Create ボタンを押し、次に場面で Save Changes をクリックしてください。
次に左のメニューから Bot Users へいきます。そこで Add a Bot User をクリックすると次にようにボットの名前の設定ができます。
ページの下になる Add Bot User をクリックして、Save ボタンもクリックします。
次は OAuth & Permissions へ行き Install App to Workspace をクリックして一旦このアプリをインストールします。インストール画面が表示されますのでそのまま続行し、ワークスペースにインストールしてください。し終わってから OAuth & Permission ページに戻ると access tokens が表示されていますのでそれを取得します。トークンは他のキー同様、 .env ファイルに保存します。
SLACK_ACCESS_TOKEN=xoxb-214…
bot
と command
スコープがすでに有効になっているのを確認してください。さて、アプリの設定がようやく終わりました!次は早速アプリのコーディングです。
☕️ アプリの構築
冒頭でも述べたように、このチュートリアルでは Slack API そのものの使い方を説明していますので、Node.js の Express.js モジュールなどを使用して直接 API を呼んでいます。さて、まず依存するモジュールをインストールしていきましょう。 POST リクエスト実行のための
Express
とミドルウェアの body-parser
、そして HTTP リクエストクライアントの axios
と、クエリストリングのパーサーである、qs
をインストールします。$ npm install express body-parser axios qs dotenv --save
まず、 index.js を作成し、Express アプリのインスタンス化し適当なポートナンバーでサーバを接続します。
/* Snippet 1 */ require('dotenv').config(); // To grab env vers from the .env file const express = require('express'); const bodyParser = require('body-parser'); const axios = require('axios'); const qs = require('qs'); const app = express(); // The next two lines will be modified later app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); const server = app.listen(5000); // port
各フローは、ユーザーがメッセージメニューからアクションを起こした時に始動します。ここで
message_action
イベントがトリガーされ、スラック側がそのイベントのペイロードを、指定されたエンドポイント(前のステップで設定した、Request URL)に送信します。受け取りのエンドポイント側では、下のように書くことができます。この時、先に設定したパスである
/actions
を使っています。/* Snippet 2 */ app.post('/actions', (req, res) => { const payload = JSON.parse(req.body.payload); const {type, user, submission} = payload; // Verifying the request. I'll explain this later in this tutorial! if (!signature.isVerified(req)) { res.sendStatus(404); return; } if(type === 'message_action') { // open a dialog! } else if (type === 'dialog_submission') { // dialog is submitted } });
message_action
か確認しています。これはユーザーがアクションを実行した際に送られるイベントタイプです。true
である場合にはダイアログを開きます。先のコード・スニペットの
// open a dialog!
とコメントのある部分に次のコード (Snippet 2.1) を追加します。ここではダイアログの内容の定義をし、dialog.open
メソッドでスラッククライアント上でダイアログボックスを表示しています。/* Snippet 2.1 */ const dialogData = { token: process.env.SLACK_ACCESS_TOKEN, trigger_id: payload.trigger_id, dialog: JSON.stringify({ title: 'Save it to ClipIt!', callback_id: 'clipit', submit_label: 'ClipIt', elements: [ { label: 'Message Text', type: 'textarea', name: 'message', value: payload.message.text }, { label: 'Importance', type: 'select', name: 'importance', value: 'Medium ��', options: [ { label: 'High', value: 'High ����✨' }, { label: 'Medium', value: 'Medium ��' }, { label: 'Low', value: 'Low ⚪️' } ], }, ] }) }; // open the dialog by calling the dialogs.open method and sending the payload axios.post('https://slack.com/api/dialog.open', qs.stringify(dialogData)) .then((result) => { if(result.data.error) { res.sendStatus(500); } else { res.sendStatus(200); } }) .catch((err) => { res.sendStatus(500); });
dialog.open
メソッドでダイアログ表示に成功したら即座に HTTP status 200 を送り返す必要があります。同様に、このダイアログがユーザーによって送信された際も先ほどと同じエンドポイントが呼び出されます。少し上の code snippet 2 の
// dialog is submitted
というコメント部分に次のコード (snipet 2.2) を追加します。/* Snippet 2.2 */ else if (type === 'dialog_submission') { res.send(''); // Save the data in DB db.set(user.id, submission); // this is a pseudo-code! // DM the user a confirmation message const attachments = [ { title: 'Message clipped!', title_link: `http://example.com/${user.id}/clip`, fields: [ { title: 'Message', value: submission.message }, { title: 'Importance', value: submission.importance, short: true }, ], }, ]; const message = { token: process.env.SLACK_ACCESS_TOKEN, channel: user.id, as_user: true, // DM will be sent by the bot attachments: JSON.stringify(attachments) }; axios.post('https://slack.com/api/chat.postMessage', qs.stringify(message)) .then((result => { console.log(result.data); })) .catch((err) => { console.log(err); }); }
次に、クリップされたメッセージをデータベースに保存するという仮定で進めます。(コードの 7 行目ではデータベース部分は擬似コードで省略してありますのでこのまま使うとエラーになります。)保存を同期がおわった時点で
chat.postMessage
メソッドを使ってユーザーに確認メッセージを送りましょう(36 行目)。この最後の確認メッセージの過程は、アプリのユーザー・エクスペリエンスのためには非常に重要ですので、この先新しいアプリを作っていく際にもぜひ、ユーザー視点に立って使いやすさについて考えてみましょう。
さて、では一旦このコードを実行してみましょう。これでメッセージメニューに、このアプリのアクションが追加されているはずなので、クリックしてみてください。架空のデータベースパート以外はきちんと動作していることを確認してください。
では最後に、アプリのセキュリティ面を改善しましょう。
�� リクエスト情報の認証
ここまでのコードでも動作はしますが脆弱性があります。エンドポイントで受け取ったリクエストが本当にスラックから来たものなのかがわからないからです。それをリクエスト毎に確認をする必要があるので、今から signing secretsを使って認証してみましょう。この Signing secrets (サインイング・シークレット、サイン認証)は、今まで Slack API で使われてきた verification tokens に代わるもので、セキュリティ面をさらに強化するために、各リクエストごとに HTTP ヘッダーに
X-Slack-Signature
を追加しています。ここでの
X-Slack-Signature
は、 HMAC SHA256 でハッシュ化されたリクエスト・ペイロードで、 Signing Secret を使ってキー化します。この例ではすでに Express と body-parser を使っていますので、body-parser の
verify
ファンクションオプションを使ってこのペイロードを取得してみましょう。始めのコード・スニペット1 に戻りましょう。
//The next two lines will be modified later
というコメント部の body-parser ミドルウェアの設定部分(12–13行目)を次のコードに置き換えてください。/* Snippet 3 */ const rawBodyBuffer = (req, res, buf, encoding) => { if (buf && buf.length) { req.rawBody = buf.toString(encoding || 'utf8'); } }; app.use(bodyParser.urlencoded({verify: rawBodyBuffer, extended: true })); app.use(bodyParser.json({ verify: rawBodyBuffer }));
const signature = require('./verifySignature');
if(!signature.isVerified(req)) { // when the request is NOT coming from Slack! res.sendStatus(404); // a good idea to just make it “not found” to the potential attacker! return; }
さて、Node コードをもう一度実行してみましょう。おめでとう、Action-able なアプリの完成です!
��Related Slack API Documentation
-
Actions
Slack Platform documentation
-
Dialogs
Slack Platform documentation
-
chat.postMessage
API method documentation
-
Signing Secret: Verifying requests from Slack -
Slack Platform documentation
Questions? Comments? コメントや質問はここ、もしくは Tomomi @girlie_mac か Slack Platform @SlackAPI へツイートしてくださいね。
コメント
コメントを投稿