こんにちは、 id:numanuma08 です。今私たちは新規サービスとして競馬予想大会開催サービス「ダービーアリーナ」を開発・運用しています。
新規サービスを素早くローンチして顧客に価値を提供するため、MVPを定義し可能な限り実装コストを抑える工夫を施しています。今回はその中で特に活用したSlackワークフローについて紹介をします。
Slackワークフローとは
コミュニケーションツールのSlack上でタスクを構築するツールです。ワークフローを使うと
- 決まった手順を自動化する
- 入力フォームを使ってデータを回収する
- 外部システムと接続し、データの取得や書き込みをする
- 外部APIを呼び出す
などが実現できます。私たちが作っているMVPは変化に柔軟に対応しなければなりません。顧客のフィードバックやいくつかの仮説を検証するため、サービス内で必要なフローは大きく変化します。アプリケーションを実装し運用するより、今は人の手を使って手順を模索しつつすすめるべきです。一方で大枠では決まっている手順も存在します。MVPとは言え、顧客に迷惑をかけず素早く出力を提供するためいくつかの自動化により手順を効率化したい欲求もあります。
この不安定な状態、複雑な要求の橋渡しとしてSlackワークフローを採用しました。Slackワークフローは基本的にコーディングは不要であること、動作の修正もビルダーを使って簡単に実現できること、複雑なフローも実装可能なことが選定理由です。
Slackワークフローで実現したこと
今回実装したSlackワークフローで実現した手順は以下です。
- ユーザーがWebアプリ上でデータを登録する。データはGoogleスプレッドシートに保存される
- Googleスプレッドシートの変更をトリガーにSlackワークフローが実行される
- 運用チームメンバーが必要なデータをそろえる
- Slackワークフローのフォームに必要なデータを記入する
- SlackワークフローがSendgrid APIを呼び出してユーザーにメールで通知する
この中でステップ3の「運用チームメンバーが必要なデータをそろえる」部分が特に変化が大きいくあいまいな手順です。MVPの状態によって収集するべきデータが変わっていきます。したがって、この箇所をアプリケーションとして実装するよりまずは手動で実行するフローとして定義しました。
Slackワークフローは手順実行のトリガーとユーザーへの通知に専念します。
一つ一つの手順について公開可能な範囲でどう実現したか紹介します。
Googleスプレッドシートの変更をトリガーにSlackワークフローが実行される
Googleスプレッドシートが変更されたときSlackワークフローを実行するにはGoogleスプレッドシートのAppsScriptからSlackワークフローのWebhookエンドポイントの呼び出しで実現しています。
まず、Slackワークフローを実行するトリガーの一つのWebhookから
を使ってワークフローを実装します。
Webhookからワークフローをトリガーするときパラメータとして受け取る変数を設定可能です。今回、スプレッドシートに追加されたデータを特定するIDをパラメータに取る設定をしました。ワークフローを設定すると「ウェブリクエストのURL」が発行されます。このURLをHTTP Postで呼び出すとワークフローを実行できます。
AppsScriptから次のコードで呼び出します。
// スプレッドシート内から取得した行ID const body = { taikai_id } const webHookURL = // ウェブリクエストURL; const options = { "method": "POST", "contentType": "application/json", "payload": JSON.stringify(body) }; UrlFetchApp.fetch(webHookURL, options);
AppsScriptはGoogleスプレッドシートの編集時・変更時などでトリガーします。AppsScriptの詳細は今回省略します。
運用チームが必要なデータをそろえる
Slackワークフローが実行されると次のメッセージがチャンネルに投稿されます。
メッセージでスプレッドシートより取得した情報を使って運用チームメンバーに次に何をしたらいいのか手順を表示します。スプレッドシートから情報を取得するにはSlackワークフローの「スプレッドシートの行を選択する」を使います。
Webhook呼び出し時に受け取った行IDを使って必要な行を取得します。取得した行の内容は以降のステップ内で変数として参照可能です。そのため、「メッセージを送信する」ステップ内でデータの表示が可能です。
メッセージの最後に「続行する」ボタンを設置して以降のステップに繋げます。ちなみにボタンを設置すると「誰が」「いつ」押したのか分かるので、後でフローを見直す場合などに便利です。
Slackワークフローのフォームに必要なデータを記入する
運用メンバーが用意したデータは次の「情報をフォームで収集する」ステップで入力させます。
Slack上にフォームが表示され、必要なデータの入力を促します。入力された情報はユーザーに表示したいので、ユーザーが閲覧可能なスプレッドシートに記入されます。スプレッドシートの更新もSlackワークフローの「スプレッドシートを更新する」ステップで実現可能です。
編集する対象の行IDと列をセットします。値は先ほどフォームの値を使います。
SlackワークフローがSendgrid APIを呼び出してユーザーにメールで通知する
編集が完了したらユーザーに、データの準備が完了を通知します。通知はメールで行うのでSendgridを使います。まず、送信するメールの内容をSlackのスレッドに送信します。
ちなみにSlackワークフロー中のメッセージはどのトリガーに対しての操作か一目でわかるので、スレッドで投稿しています。チャンネルに投稿してしまうと、複数のワークフローがトリガーされたときにどのワークフローに対する操作か分からなくなってしまいます。
メッセージを確認して「メールを送信する」を押すとメールが送られます。しかし、残念ながらSlackワークフローはSendgridと連携する機能は無いため、Custom Functionsとしてこの機能を実装しています。
Custom Functionsは特定の動作を「関数」として定義してワークフローやスラッシュコマンドなどで実行する仕組みです。
メールのメッセージ組み立てとSendgrid APIの呼び出しをCustom Functionsとして定義し、パラメータや出力を定義しています。SendgridはDynamic Templateを使ってPOST時のパラメータからメッセージを生成しています。
try { const response = await fetch("https://api.sendgrid.com/v3/mail/send", { method: "POST", headers, body, }); if (response.ok) { return { completed: true, outputs: { message: "メールを送信しました", }, }; } else { const json = await response.json(); return { error: ` Sendgrid APIの呼び出しでエラーが発生しました ${JSON.stringify(json, null, "\t")} `, }; } } catch (error) { console.error(error); return { error: ` APIの呼び出しに失敗しました。 ${JSON.stringify(error, Object.getOwnPropertyNames(error), "\t")} `, }; }
ちなみにSlack Custon Functionsのランタイムはdenoが採用されています。Sendgridのクライアントはnpm用パッケージはありますが、deno bundleに失敗するのでfetch APIを直接呼び出しています。
メール送信に成功したら「メールが送信されました」とメッセージが投稿されます。
エラー時の対応
Slackワークフロー実行時にエラーが発生するとSlackbotが私(id:numanuma08)にDMでメッセージを送ってきます。
私しかエラーに気がつけないのはちょっとどうかな・・・と思いますが。まあ今のワークフローなら時間がたっても実行が完了しない、などで以上に気がつけますからとりあえずヨシです。
まとめ
Slackワークフローの導入事例を紹介しました。Slackワークフローのみで対応できない物に
- 連携していないAPIの呼び出し(Custom Functionが必要)
- 条件分岐・繰り返しは実現できない
などがありますが、変化に柔軟なMVPを実装しリリースするにあたってちょうどいい方法だったかなと思います。