マエカワの備忘録的な何か

思い立ったが吉日

SlackとGAS(Google Apps Script)を連携してBotを作ろう

はじめに

 こんにちは.マエカワです.

 皆さん,せっかく何かを発信したのに,その発信に対する反応がないと悲しくなってしまいますよね.
 また,皆さんは当然のごとくSlackを使用して連絡を取り合っていることでしょう.

 ということで今回は,Slackの投稿に反応するチャットBotを作っていこうかと思います(唐突).

 以前,今回紹介するシステムを使用した勤怠管理システムを個人的に開発して,今でも運用しているんですが,そのときの記憶があいまいになってきたので,一度整理しておこうかと思いこの記事を書いています.整理された内容かどうかはわかりませんが,何卒ご容赦を.

目指すもの

 繰り返しになりますが,今回の目標を書いておきましょう.Slackの指定したチャネルに,これまた指定した文字列から始まるメッセージが投稿されたときに返事をしてくれるBot,これをを作ります.

システム構造

 これから作っていくBotは以下のようなシステムで動いています.イメージのために載せておきます.

f:id:maekawa_yoshimiki_1119:20200318013432p:plain
システムイメージ
やっていることは単純です.トリガーが来たらPOSTを投げて,それに対応する処理をしてSlackに返してあげるだけです.ややこしそうに見えますが,アプリやらライブラリを入れたら結構簡単にできるんですなこれが.抵抗感を持たずに,やっていきましょう.

下準備

Slackワークスペースの準備

 以下のページから手順に沿ってワークスペースを作成します.特に詳しい説明は無し.
slack.com

Outgoing Webhookの準備

 ワークスペースが作成できたら,GASと通信するためのアプリを導入していきましょう.以下の手順に沿って導入していきます.

  1. サイドバーにある「Apps」をクリック
  2. サーチボックスに「outgoing webhooks」と入力して,該当アプリをクリック
  3. 「View in App Directory」をクリック
  4. 「Add to Slack」をクリック
  5. 「Add Outgoing WebHooks integration」をクリック
  6. Integration Settingsにある値をカスタマイズ
    • Channel
      • このフィールドには,ワークスペース内のチャンネルを指定します.指定したチャンネルに,トリガーとなる文字列が投稿されると,指定したURLにPOSTが飛ぶようになっています.
      • 今回はdeveloppingチャンネルを新たに作成し,設定しました.
    • Trigger Word(s)
      • トリガーとなる文字列をここに記述します.文字列は複数指定することができ,その場合は「,(半角コンマ)」で区切りましょう.
      • 今回は「#Check」をトリガーとして設定しました.
    • URL(s)
      • この項目には,後ほど取得するGASのURLを入力します.
    • Token
      • デフォルトのままでOK.変更は基本的にしません.後ほど,GASの中で使用します.

URLなどはGASが出来上がってから入力するので,ブラウザは閉じずに,すぐ見れるようにしておきましょう.
 以下,画像を使って説明しています.文字ベースで分かりにくかったら参考にしてみてください.

f:id:maekawa_yoshimiki_1119:20200317231953p:plain
「App」からOutgoing WebHooksを検索し,選択
f:id:maekawa_yoshimiki_1119:20200317232014p:plain
「Add to Slack」をクリック
f:id:maekawa_yoshimiki_1119:20200317232030p:plain
「Add Outgoing WebHooks integration」をクリック
f:id:maekawa_yoshimiki_1119:20200317232809p:plain
Integration Settingsにある値をカスタマイズ

GASの用意

 GASがなくては何も始まりません.以下の手順に従って導入していきましょう.

  1. まずGoogle Drive内でGASを保存したいフォルダに移動
  2. 「新規」⇒「その他」⇒「Google Apps Script」をクリック
  3. ファイル名をつけて保存

これでOKです.

f:id:maekawa_yoshimiki_1119:20200317195956p:plain
GAS導入の手順

Legacy tokensの取得・登録

 以下のリンクに飛んで,Legacy tokenを取得しましょう.
api.slack.com
 取得できたら,以下の手順に沿ってGASに登録していきましょう.

  1. 「ファイル」⇒「プロジェクトのプロパティ」を選択
  2. スクリプトのプロパティ」タブに移動
  3. 「行を追加」をクリック
  4. プロパティ名とその値を入力して保存

これで完了です.プロパティ名は,他のサイトなどを踏襲して「SLACK_ACCESS_TOKEN」にしました.以下,図を使った説明.文字で分かりにくかったら参考にしてみてください.

f:id:maekawa_yoshimiki_1119:20200317213716p:plain
「プロジェクトのプロパティ」を選択
f:id:maekawa_yoshimiki_1119:20200317214108p:plain
スクリプトのプロパティ」タブに移動を選択
f:id:maekawa_yoshimiki_1119:20200317213803p:plain
「行を追加」をクリック
f:id:maekawa_yoshimiki_1119:20200317213824p:plain
値を入力して保存

注意 -- Legacy tokenが2020年5月5日で取得不可能になります

 以下のサイトで,2020年5月5日をもってLegacy tokenの新規取得ができなくなるとの通知が上がっています.
api.slack.com
ドキュメント内に

Create a Slack app and ask for the permission scopes corresponding to your goals. You can install your app without implementing OAuth by copying and pasting the displayed token to your preferred programming environment.

とあり,Slackアプリを登録してそれぞれで認証すればいいとのこと.ここら辺の手順もアップデートせねば.そのうち追記します.

Slack Appライブラリの導入

 以下のQiitaの記事を書いてくださった方が公開してくれています.ありがたい....
qiita.com
導入方法はこの記事に書いてありますが,一応ここでも書いておこうと思います.

  1. 「リソース」⇒「ライブラリ」を選択
  2. Add a libraryフィールドに「M3W5Ut3Q39AaIwLquryEPMwV62A3znfOO」を入力し,「追加」をクリック
  3. ライブラリを最新バージョンに設定(2020年3月17日時点でバージョン22が最新でした)
  4. 「保存」をクリック

以上で導入完了です.これで,便利メソッドを使用できるようになりました.
 以下,画像で説明しています.ここまでで詰まってしまったら参考にしてみてください.

f:id:maekawa_yoshimiki_1119:20200317215122p:plain
「ライブラリ」を選択
f:id:maekawa_yoshimiki_1119:20200317215152p:plain
ライブラリのトークンを入力・追加
f:id:maekawa_yoshimiki_1119:20200317215215p:plain
最新バージョンに設定・保存

本実装

コーディング

基本的に,doPostメソッドの中身を記述していくだけです.得られたテキストで条件分岐させてなど,複雑な処理はここで書いてしまいます.

function doPost(e) {
  var token = PropertiesService.getScriptProperties().getProperty('SLACK_ACCESS_TOKEN');
  var slackApp = SlackApp.create(token);
  var botToken = "***************"; //ここにOutgoing WebHooksのTokenを記述する
  var channelID;
  var message;
  var userName;
  var iconUrl;
  
  if(botToken == e.parameter.token){
    channelID = "****"; //チャンネルID(後述)
    message = "Hello!!";
    userName = "Test Service";
    iconUrl = "https://2.bp.blogspot.com/--tK6L-RmW-Y/WxvJot722lI/AAAAAAABMio/eKmnsWVyybAs7fZVuuDiNBbDXSSyZ7cgQCLcBGAs/s800/bird_menfukurou.png";
  }else {
    channelID = "****"; //チャンネルID(後述)
    message = "不正アクセスされている可能性があります";
    userName = "Warning Message";
    iconUrl = "https://1.bp.blogspot.com/-JgjGUdCLrIg/UUQ0qXejRiI/AAAAAAAAO3k/LGEKQA3rFd8/s1600/Tyrannosaurus.png";
  }
  
  slackApp.postMessage(
    channelID, 
    message, 
    {
      username: userName,
      icon_url: iconUrl
    }
  );
}

 ここで,各パラメータについて説明しておきましょう.

  • 2~3行目
    • ここはライブラリを使用するためのおまじないみたいな感じです.書きましょう.
  • 4行目
    • POSTの中に書かれているtokenの値と比較して,Slackからの通信なのかを判断するため,WebHooksのTokenの値をここで保存しています.後半のif文で条件分岐させるために必要です.
  • 5行目
    • チャンネルIDですが,ブラウザでSlackを起動したときのURLに含まれています.具体的には「/(スラッシュ)」で区切ったとき,の一番最後の要素に当たる文字列がチャンネルIDです.
  • 10~21行目
    • 前述したtokenで条件分岐しています.Slackからの通信だった場合,「Test Service」というアカウント名で文字列「Hello!!」を投稿するようにしています.それ以外からの通信だった場合は,「Warning Message」というアカウント名で,不正アクセスの可能性をユーザに教えるようにしています.
    • ここの処理部分をカスタマイズすることで,いろいろな言葉を返してくれるBotを開発できます.
  • 23行目~
    • Slackにメッセージを投稿するメソッドです.引数にはチャンネルIDとメッセージの内容,オプションでアカウント名とアイコンの画像を指定できます.今回はいらすとやからアイコンを引っ張ってきました.何が出るかはお楽しみ.

デプロイ

 コーディングが完了したら,webアプリケーションとして公開する必要があります.以下の手順に沿って公開していきましょう.

  1. 「公開」⇒「webアプリケーションとして導入」
  2. バージョンを「New」に設定
  3. 公開範囲を「Anyone, even anonymous(匿名もOK)」に設定
  4. 「Deploy」をクリック
  5. 得られたURLをOutgoing WebHooksのURL(s)に設定

これで完了.もし新しい関数や処理を追加した場合は,新しいバージョンでデプロイしましょう.そうすることで変更がアプリケーションとして反映されます.
 以下,画像での説明です.分からなかった場合は参考にしてください.

f:id:maekawa_yoshimiki_1119:20200318015219p:plain
「webアプリケーションとして導入」
f:id:maekawa_yoshimiki_1119:20200318012956p:plain
バージョンを「New」に,公開範囲を「匿名も可」に設定
f:id:maekawa_yoshimiki_1119:20200318013036p:plain
「デプロイ」をクリック
f:id:maekawa_yoshimiki_1119:20200318013059p:plain
得られたURLをOutgoing WebHooksのURL(s)に設定

デモ

Slackからのアクセス時

 Slackで「#Check」と投稿してみましょう.そのときのレスポンスが以下の画像です.

f:id:maekawa_yoshimiki_1119:20200317203934p:plain
Slackからアクセスしたときのレスポンス
ちゃんと「Hello!!」と返ってきていることが分かります.なかなかうれしい.アイコンもかわいらしくてなんだか和んでしまいます.メンフクロウちゃんかわいい.

外部からのアクセス時

 次に不正アクセスした(想定したtokenからの通信でなかった)場合を見てみましょう.windowsInvoke-RestMethodコマンドを使用して,GASにPOSTを投げてみます(もちろん,curlでも大丈夫です).

$ Invoke-RestMethod -Uri "GASのURL" -Method POST
<!DOCTYPE html><html><head><link rel="shortcut icon" href="//ssl.gstatic.com/docs/script/images/favicon.ico"><title>エラー</title><style type="text/css">body {background-color: #fff; margin: 0; padding: 0;}.errorMessage {font-family: Arial,sans-serif; font-size: 12pt; font-weight: bold; line-height: 150%; padding-top: 25px;}</style></head><body style="margin:20px"><div><img alt="Google Apps Script" src="//ssl.gstatic.com/docs/script/images/logo.png"></div><div style="text-align:center;font-family:monospace;margin:50px auto 0;max-width:600px">スクリプトが完了しましたが、何も返されませんでした。</div></body></html>

 このときのレスポンスは以下の画像のような感じです.

f:id:maekawa_yoshimiki_1119:20200317204011p:plain
外部からアクセスしたときのレスポンス
がおー.ティラノサウルスくんが不正アクセスの可能性があることを教えてくれました.このメッセージが投稿されたら,Legacy tokenを再発行するなどの対策が必要になります.そこら辺の話はちょっと勉強不足で書くことができません.ごめんなさい.

おわりに

 ここまで読んでいただきありがとうございます.
 これで,「疲れた」と投稿したときに「大丈夫?」と返事をくれるBotが簡単に出来上がってしまいます!! 一回作ったら周りから「悲しいやつめ」と言われました.悲しい.
 あとはですね,GASって優秀でスプレッドシートを編集することもできます.自分はこの機能とIFTTTを連携させて,簡易勤怠システムを開発して個人的に使用していたりします.IFTTTで家のWi-Fiandroid端末の接続,切断を検知してSlackにトリガー付きのメッセージを投稿するんですね.そのあとはこの記事でやっていることと同じです.トリガーを検知してWebHooksでPOSTして,GASがスプレッドシートに時間やらグラフやらを自動で生成してくれる.そんなシステムです.一時期,IFTTTの連携がストップしたのかわかりませんが,サービスがストップしていましたが,現在は元気に動いています.システム構造は以下の通り.

f:id:maekawa_yoshimiki_1119:20200318020401p:plain
簡易勤怠システム
 こんな感じでいろいろとややこしいことも簡単にできてしまったりするので,GASとSlackの連携おすすめです.IoTデバイスをこの機能でなんやかんや~なんていう方もいらしましたね.自分の役に立ちそうなものだったり,「なんやそれすげぇ!!」といったことを自分で実装して楽しむことができるのは,なんとも至福だなぁと最近思っているマエカワからの報告でした.それでは.


読んでくださった方に感謝を
マエカワ