
はじめに
こんにちは。佐藤です。 前回から期間が空いてしまいましたが、、、2本目の投稿です。
今回は社内副業でGoogle Chat Botの開発に触れる機会がありましたので、
苦労したことを振り返りながら、備忘録がてら紹介していきたいと思います。
では早速始めていきましょう。
※前回の投稿はこちら
Google Chat Botとは?

そもそもGoogle Chat Botとは何かということを簡単に言いますと、
「 一定のタスクや処理を自動化し、人の代わりにさまざまな作業を行ってくれるもの」
というものです。
例えば以下のようなものが挙げられます。
・リマインド機能
・会議の設定
・アンケート収集
・勤怠管理
・通知機能(フォームの回答、Githubの更新情報など。)
依頼内容
今回の依頼は以下の通りでした。
◾️依頼内容
- 経費事前申請をGoogle Chatから申請できるようにする。
■環境やスキル等
・Google Apps Script(GAS)
完成までの流れ
Botの完成までの道のりとしては以下のような対応をしました。
1.GoogleGloudPlatformにてスラッシュコマンドを作成しGASと連携
2.スプレッドシートを用意(申請内容記録用)
4.Google Chatの対象スペースにアプリの追加(1.で作成したもの)
ダイアログって??
ざっくり説明をすると、
Google Chat内でスラッシュコマンドを発行した際に表示される入力フォームのようなものです。
経費申請などの定まった情報をユーザから収集する際に役立ちます。
以下GAS内に定義したダイアログのコードです。
typeにDIALOGを指定し、
widgetsに表現したいパーツ(入力欄、選択肢、ボタンなど)を組み込んでいきます。
※widgetsの細かい情報はこちら
"action_response": {
"type": "DIALOG",
"dialog_action": {
"dialog": {
"body": {
"sections": [
{
"header": "経費事前申請フォーム",
"widgets": [
{
"textInput": {
"label": "購入商品",
"type": "SINGLE_LINE",
"name": "productName",
}
},
{
"textInput": {
"label": "購入商品参考資料(URL等)",
"type": "SINGLE_LINE",
"name": "productUrl"
}
},
{
"textInput": {
"label": "購入金額",
"type": "SINGLE_LINE",
"name": "productPrice",
}
},
{
"textInput": {
"label": "購入理由",
"type": "MULTIPLE_LINE",
"name": "purchaseReason",
}
},
{
"divider": {}
},
{
"decoratedText": {
"text": "会社購入",
"switchControl": {
"controlType": "SWITCH",
"name": "purchasedBy"
}
}
},
{
"buttonList": {
"buttons": [
{
"text": "申請する",
"onClick": {
"action": {
"function": "recordPreApplicationRequest"
}
}
}
]
}
}
]
}
]
}
}
}
}
このコードで表現されるダイアログがこちらです。

カードって??
用途はダイアログとさほど変わらないのですが、
こちらは直接メッセージ上に表現されるものになります。
今回は前述のダイアログより申請がされた際に
スペースに送信するカードについてGASのコードを紹介します。
カードの表現はcardsV2で行います。(中で使用するwidgetsはダイアログと同じ)
"actionResponse": { "type": "NEW_MESSAGE" },
"text": "<" + mentionName + ">さんから申請がありました。",
"cardsV2": [
{
"cardId": id,
"card": {
"header": {
"title": "経費事前申請通知",
"subtitle": "内容を確認し対応をお願いします。"
},
"sections": [
{
"widgets": [
{
"textParagraph": {
"text": "申請ID: " + id
}
},
{
"textParagraph": {
"text": "申請者: " + applicantName
}
},
{
"textParagraph": {
"text": "購入商品: " + productName
}
},
{
"textParagraph": {
"text": "参考資料: " + productUrl
}
},
{
"textParagraph": {
"text": "商品価格: " + productPrice
}
},
{
"textParagraph": {
"text": "購入理由: " + purchaseReason
}
},
{
"textParagraph": {
"text": "購入方法: " + purchasedBy
}
},
{
"divider": {}
},
{
"buttonList": {
"buttons": [
{
"text": "承認する",
"onClick": {
"action": {
"function": "approvePreApplication"
}
}
},
{
"text": "差し戻し",
"onClick": {
"action": {
"function": "sendbackPreApplication",
"interaction": "OPEN_DIALOG"
},
}
}
]
}
}
]
}
]
}
}
]
実際にユーザから申請がされた際にスペースに届くメッセージがこちらです。

処理フロー
今回の処理フローはこんな感じです。

【備忘録】実装にあたり苦労したこと

ここでは開発を進めていて個人的に苦労した内容をご紹介します。
苦労ポイントその1:カードのボタンを押下してもイベントが発生しない。
事象:カードに実装したボタンを押下しても何も起きない
原因:「actionResponse」が未定義のためイベントが発生しなかった。
レスポンスを返す際にcardsV2からそのまま定義を実装していたのですが、
actionResponseの指定(投稿方法を構成するために使用するパラメータ)が必須項目だったようです。。。
いくつかレスポンスの種類があるので、
実装の際にはactionResponseの指定は忘れずにしておきましょう。
苦労ポイントその2:インラインスレッドで返信ができない。
事象:承認(または差し戻し)ボタン押下の際に新しいメッセージとして送信されてしまい、どの申請に対してのレスかわからなくなる。
原因:WEBHOOKを使用して返信する際に以下のパラメーターが必要だった。
申請に対し承認(または差し戻し)した際に新しい投稿としてメッセージが表示されてしまい、
どの申請に対してのメッセージかがわからなくなってしまいました。
テスト的に1人でやっている分にはいいのですが、一度に何人もの方が申請をすると
大変なことになってしまうので、どうにかインラインスレッドで返信できないかを試行錯誤しました。
結果WEBHOOKのURLに以下のパラメータを付与することで
各申請に対してインラインスレッドで返信することが実現できました。
&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD
苦労ポイントその3:情報の取得がうまくできない。
事象:
cardsV2からcardIdを取得したいが指定してもundefinedとなってしまう。 原因:cardsV2は連想配列だった。。。
承認(または差し戻し)後のボタンの非活性化のためにcardIdを使用しているのですが、
IDがうまく取れずに苦戦しました。。。
どうやらcardsV2は連想配列だったらしく取得の仕方がよろしくなかったようです。
誤:event.message.cardsV2.cardId 正:event.message.cardsV2[0].cardId
こちらはJavaScriptでの実装における自分の知識不足ですね。。。
最後に
いかがでしたでしょうか。
上級者の方にはそんなことに苦労したのかと思われるかもしれませんが、
知識が浅い私からしたら非常に難易度が高い開発でした。。。
毎日が勉強ですね。もっと精進せねば。。。