Slackのリアクションに反応するアプリをGASで作ってみた話
こんにちは。RELATIONS株式会社の大川です。
最近はSUMMER SONICの3日目が楽しみで仕方がないです。
普段はWistantの開発・運用をしていますが、 今回はGoogle Apps Script・Slack・スプレッドシートを連携させたアプリ開発の話をしていきます。
きっかけは、OJT中の新入社員に声をかけられたことでした。
(OJTの様子はこちらからご覧いただけます。)
彼は社内にバリュー(行動規範)を浸透させていくために、Slack上の「バリューを体現する行動を称賛する投稿」を増やし、バリュー自体を意識してもらう回数を増やそうとしていました。
弊社ではすでに「(各バリューに対応するカスタム絵文字)(メンション)称賛メッセージ」の形式で、バリューを体現した行動を称賛できるSlackアプリが存在していました。
ですが、これだけだとメッセージを作成することが面倒だったり、他の人が称賛した内容と同じメッセージを送ることに対してハードルがちょっと高いというフィードバックがありました。
そこで、もっと簡単にSlackメッセージにリアクション(絵文字)をつけるだけで投稿としてシェアされるようになると良いのでは、という話になり、開発が始まりました。
ちなみに「リアクションに反応するアプリ」というアイデアは以前に見かけていたランサーズ株式会社さんのAdvent Calenderの記事を参考にしていました。
(参考: Slackで感謝を送り合うツールを3日で実装、導入した話 - Qiita)
つくったもの
Slack上で、バリューに対応する絵文字のリアクションがされると、バリューを発揮した行動が特定のチャンネルに投稿されるようにしました。
アプリの流れは以下の通りです。
Slack Events APIでリアクションの追加イベントをサブスクライブしておき、
イベントデータがGASに渡ってきたらユーザー情報やメッセージ内容を取得して、
スプレッドシートに記録&Slackにメッセージを送信するようになっています。
SlackアプリのインフラをGoogle Apps Scriptにする
自分が開発するのであればAWS Lambdaを使うのですが、
プログラミング未経験の新入社員にも実際に手を動かしてもらおうということで、
動作環境には Google Apps Script(以下、GAS)を採用しました。
結果として、ペアプロしつつ自分も半分くらい実装することになったのですが、 以下の点でGASを利用してよかったなと感じています。
- 特別な開発環境構築が不要なので入門のハードルが低い
- ドキュメントが充実してるのでスプレッドシート連携も簡単
- 運用コストがかからない
- 自分がGASを使ったことなかったのでノウハウが溜まった
開発のTips
GAS
Webアプリとして実装するのも doPost / doGet関数を用意するだけなので簡単でした。
ただ、公開されているWebアプリに変更を反映させるためには毎回デプロイが必要なので、デバッグは工夫が必要でした。
関数の実行はデプロイしなくても試せるので、1つの関数でやることを小さくしてテスト用関数を用意しながらデバッグを進めていきました。
POSTリクエストに反応するdoPostメソッドは、別関数を呼び出すだけのシンプルな実装になっています。
// POSTリクエストの処理 function doPost(e) { handleDoPost(e); }
handleDoPost関数も、POSTリクエストで渡ってきたイベントデータの抽出や スプレッドシートとの連携など色々な処理が多いですが、ほぼ別の関数に切り出してその呼び出しを行うようにしています。
// POSTリクエストの処理の中身 function handleDoPost(e) { // (...略) if(checkReaction(reaction)){ // (...略) // イベントデータを抽出したり、スプレッドシートに記録したり、メッセージを構築したり postMessageToSlack(message) } }
以下はSlackにメッセージを送信するpostMessageToSlack関数と、 Slackからのリアクション内容をチェックするcheckReaction関数です。
// Slackへのメッセージ送信 function postMessageToSlack(message) { var data = { 'text': message, link_names:1 }; var options = { 'method' : 'post', 'contentType': 'application/json', 'payload' : JSON.stringify(data), muteHttpExceptions: true }; UrlFetchApp.fetch(incomingWebhookUrl, options); } // リアクションの絵文字がバリュー関連のものか確認する function checkReaction(stamp){ return stamp === 'value_team' || stamp === 'value_shinka' || stamp === 'value_sekai' }
Slack API
Slackの「リアクションが追加された」というイベントに反応するためEvents APIを利用しました。
SlackにはWeb API / Events API / RTM APIといくつか種類がありますが、
どれを利用するべきかフローチャートが用意されていたので参考にしました。
(参考: 必要な Slack API はどれ? - Slack アプリの作成のためのヒント | Slack
Events APIは全パブリックチャンネルでのリアクション追加に反応します。
しかしSlack Botを用意することでBotが参加しているチャンネルのイベントのみをサブスクライブできるようになります。
(参考: Slack API: Receive events in specific channels only? - Stack Overflow
今回はどのチャンネルでも使えるように特にBotは用意していません。 Slackのメンバー数にもよりますが、弊社ではまだGASの実行回数リミットを過ぎてしまって実行できなくなる…ということも発生していないです。
また、POSTリクエストで渡ってくるイベントデータだけでは、スプレッドシートに記録する情報が不足していたので、
ユーザー情報やメッセージ内容を取得するために別途OAuth認証を行いWeb APIを利用しましたが、ライブラリを利用して簡単にOAuth認証に対応できました。
(参考: gsuitedevs/apps-script-oauth2: An OAuth2 library for Google Apps Script.)
今後について
GASを利用してSlackアプリを開発するのは初めてでしたが、すごく簡単に実装できてよかったです。
ただし、今運用されているシステムにはいくつか課題を抱えています。
監視をちゃんとしてない
stackdriverでのログ出力はできるので、異常検知するような仕組みも用意しなきゃいけないなと考えています。
リトライ処理に対応できない
SlackのAPIは3秒以内にレスポンスを返さないとリトライ処理を行うので、 今回構築したシステムも3秒以内にレスポンスを返せないとリトライ処理の分だけ動作してしまいます。
これに対応するためには、POSTリクエストヘッダーの値を見てリトライかを判断する必要があるのですが、 GASではリクエストヘッダーにアクセスできずにリトライ処理をさばけませんでした。
こちらはそもそもリトライ処理を生まない仕組みに改善する必要があり、 ざっと調べてみた感じだとGAS上でジョブキューを構築するような方法があるそうです。(未検証)
手軽に始められたGASでのSlackアプリ開発ですが、 Events APIのサブスクライブ、OAuth認証後のWeb API呼び出し、スプレッドシート連携など 処理が複雑になってきたのでリトライ処理を生まない仕組みにするついでにAWS Lambdaに移行しようかとも考え中です。
まとめ
今回はGASを利用してSlack APIとスプレッドシートを連携させるアプリ開発について紹介しました。
初めてのGASでしたがノウハウが結構たまったので、
社内勉強会など開いて他のメンバーもGASを使って生産性を向上できるようにしていけたらいいなと考えています。