Google Apps ScriptでGoogle Chatにメッセージを送る【GAS】
Google Apps Scriptには、Google Chatに送る為のメソッド類は装備されていません。Google ChatはREST APIとして提供されているので、Google Apps Scriptで利用する場合は、このAPIを叩いて送信する必要があります。
Webhookを利用して送りつける方法はあるのですが、自動化などで利用する場合には、そのWebhook URLを知っておく必要がある上に送信しか出来ないので、色々と不便です。そこでこのAPIを利用して色々出来ないか調べてみました。
※いわゆるチャットボットになります。
※OpenAIのGPT-3人工チャットAIのAPIを機能として追加。以下のエントリーを参考にしてみてください。
目次
今回利用するスプレッドシート等
今回のスクリプトは事前準備がかなり大変なので、一個作っておいて使いまわしするような形で運用すると良いでしょう。
事前準備
面倒なことに、Google Chat APIを叩く為にはサービスアカウントの準備等が必要のようで、Google Apps Scriptから叩くには手間が必要になっています。予め、Cloud Consoleにプロジェクトは用意しておいてください。このあたりの情報は以下のエントリーにまとめてあります。今回は、サービスアカウントの作成から開始します。
Cloud Consoleでの作業
APIの有効化
Cloud ConsoleのGoogle Chat APIを有効化しておきます
- サイドバーから「APIとサービス」⇒「有効なAPIとサービス」を開きます
- APIとサービスの有効化をクリック
- 検索窓でchatと入れて検索すると、Google Chat APIが出てくるのクリック
- もしGoogle Drive APIが有効化されていない場合は、Drive APIも追加しておきます
- 有効化をクリックする
今回は認証はOAuth2.0認証ではなくサービスアカウントでの認証となるのでこれでOK。OAuth同意画面のスコープにもいれておくとベストですが、今回特に追加してはいません。また、GAS側にもサービスとしてDriveやChat APIを追加していません。
※一部の環境で、実行時に「Permission denied to enable service [drive]」というようなエラーログが出るケースでは、GCP上でDrive APIを有効化してないであったり、OAuth同意画面のスコープでDrive APIを入れていない等の問題があるのかもしれません。
図:まずはAPIを有効化しておきましょう
Chat APIの構成を作る
有効化したChat APIを開いて、構成の中身を構築します。これがBotとなり、スクリプトからのメッセージを変わりに投稿してくれます。
- Google Chat APIを開く
- 構成タブを開く
- アプリ名を適当につける(今回はchatmanとしました)。これを控えておきます。
- アバターのURLは今回は、こちらのURLを利用しました。
- 機能の2つのチェックボックスをオンにします
- 接続設定はApps Script Projectをチェックします
- デプロイIDですが、後半にあるデプロイメントIDを取得するを参考に先にApps Scriptのデプロイを実行してDeployment IDを取得して入れます。
- 権限はドメイン内の全メンバーとしました。後述のアプリを検索で出てこない場合には、個別に公開設定にメアドをカンマ区切りで入れてみてください。今回は特定のグループアドレスと管理者のアカウントの2つを入れておきました。
Google Chat APIをGCPで設定するに辺り、1プロジェクト1設定しか出来ませんので、複数のGoogle Chat APIを利用するスプレッドシートがある場合は、別にGCPプロジェクトを作成して、上記の手順でデプロイメントIDを登録する必要があります。
図:これがChatbotの設定になります
サービスアカウントの作成
まずは、Google Chat用のサービスアカウントを用意します。
- サイドバーから「IAMと管理」⇒「サービスアカウント」を開きます
- 上部にあるサービスアカウントを作成をクリックする
- サービスアカウント名、IDを適当にセットし、作成して続行をクリック
- 「このサービス アカウントにプロジェクトへのアクセスを許可する」の項目は省略して進める。続行をクリック。
- 「ユーザーにこのサービス アカウントへのアクセスを許可」も同様に省略して進める。完了をクリック。
- これで、サービスアカウントの作成が完了。すると、アカウント名@プロジェクト名.iam.gserviceaccount.comでアカウントが出来ます。これを控えておきます。
- OAuth 2 クライアント IDを「一意のID」として控えておきます。
※ちなみにサービスアカウントでは、スペースのメッセージ取得などのメソッドがスコープを追加しても使えません。また、特に今回IAMに参加するユーザを登録したりロールを割り振ったりは行っていません。
図:サービスアカウント自体は作るのは簡単
秘密鍵の作成
このままだと、このサービスアカウントが誰でも利用可能な状態なので、秘密鍵を作成します。
- 作ったサービスアカウントをクリックして開く
- キータブをクリックする
- 鍵を追加をクリックして、新しい鍵を作成をクリック
- キーのタイプをJSONとして、作成をクリック
- 鍵が作成されて、JSONファイルがダウンロードされます。
- このファイルは大切に保管しておき漏洩しないように注意が必要です。
図:秘密鍵は必ず作成しておきます。
注意点
社内のGoogle WorkspaceとGCPで作成したChatbotですが、社内のユーザに公開して利用するために公開設定で設定をしています。社内で利用する分にはGoogle Workspaceの組織内アドオン同様にこれで社内で利用が可能と思います。
問題は外部ユーザを含めたスペースに於いて、作成したChatbotを加えている場合、社内メンバーはこの機能を利用できますが、外部メンバーは利用が出来ません。外部メンバーも利用可能とするとなると、メッセージにもあるようにアドオンの場合同様にMarketplaceに公開する作業が必要になってしまいます。GWSの全体の委任にgmail.comも追加が必要かもしれません(セキュリティ的におすすめしません)
もとより、この手のChatbotは社内向けで作成するものであるので、外部ユーザがホイホイ使えてしまうのは問題であるので、外部ユーザは使えない・使わせないという点に注意しましょう。
図:基本は内部で利用とします。
管理コンソールでの作業
Google Workspaceの管理コンソールでも作業が必要になります。
- 管理コンソールの「セキュリティ」⇒「アクセスとデータ管理」⇒「APIの制御」⇒「ドメイン全体の委任」を開きます
- APIクライアントの新規追加をクリック
- クライアントIDに控えておいた「一意のID」を追加する
- Scopeには、「https://www.googleapis.com/auth/chat.bot」を追加する
これで、Google Workspace内で利用が可能になります。
図:管理コンソールでAPI利用の許可をする
スプレッドシートの共有に追加
サービスアカウントに対して、スプレッドシートの読み書きなどの作業が伴う場合には、スプレッドシートの共有設定に予め控えておいた「サービスアカウント」のアドレスを追加し、許可してあげる必要性があります。ただし、外部ドメインを追加できる設定のテナントでなければサービスアカウントで読み書き設定は追加できません。また必須の作業ではないのでチャットを返すだけならばこの作業は省略可能です。
※本スプレッドシートには対象のChatスペースに参加してるユーザがアクセスできるように、アクセス権限のあるディレクトリに配置しておきましょう。
図:サービスアカウントからの読み書き許可
Google Apps Script側の準備
このままでは、Google Apps Scriptから操作が出来ないので、以下の作業をしてあげます。
プロジェクトの移動
Google Apps ScriptとCloud Consoleのプロジェクトを紐付けする作業が必要です。以下の手順でプロジェクトの変更を行います。
- Cloud Console側のプロジェクトのホームを開き、「プロジェクト番号」を控えておく
- Google Apps Scriptのスクリプトエディタを開く
- サイドバーからプロジェクト設定を開く
- GCPプロジェクトの「プロジェクトを変更」をクリック
- GCPのプロジェクト番号に、1.の番号を入力して、プロジェクトを設定をクリック
- これで紐付けが完了しました。
図:プロジェクトの移動も必須の作業です
秘密鍵の配置
サービスアカウント作成時にダウンロードされた秘密鍵ですが、これをGoogle Apps Scriptから利用する必要があるので、Google Driveにアップロードします。但しこのファイルは誰かと共有しないようにする必要があります。よって、アクセス権限は自分のみとし、流出しないようにします。
また、Google Apps Scriptから参照する為に、このアップロードしたファイルのIDを控えておきます。スクリプトから参照して値を取り出すのに必要です。自分の場合はこのファイルのIDをスクリプトプロパティに格納しておきます。今回はjsonmanという項目のスクリプトプロパティに手動でJSONファイルのファイルIDを追加しておきました。
※スクリプトプロパティにJSONの中身を全部コピペする方法もありますが、流出注意。
図:秘密鍵のIDをスクリプトプロパティに入れておく
認証用のライブラリを追加する
秘密鍵のJSONファイルを使ってサービスアカウントの認証をする為に、Google Apps Scriptにライブラリを追加します。以下の手順でOAuth2 for Apps Scriptライブラリを追加しましょう。
図:OAuth2.0認証ライブラリを追加する
デプロイメントIDを取得する
GCP側の構成で利用するIDとなります。
- デプロイをクリックし、新しいデプロイをクリック
- アドオンのまま、デプロイをクリックすると、Deployment IDが出てくるのでこれをコピーする
このIDをセットしないと、API実行時に「No bot associated with this project.」として404エラーが返ってきます。また、コードを変更したら、再度バージョンを変更してデプロイしないと反映しないので、要注意です。
図:これがないと、404エラーとなってしまう
appsscript.jsonに追記
以下の手順で文字列を追記します
- スクリプトのプロジェクト設定を開き、「appsscript.json」マニフェスト ファイルをエディタで表示するにチェックを入れる
- appsscript.jsonが表示されるので開く
- runtimeVersionの行の下にでも、以下の1行を追記する
1"chat": {}, - 上書き保存する
このappsscript.jsonの記載ですが、公式ではV8ランタイムでは動かないということなのですが、自分のほうでは動作しています。気になるようならば、V8 Runtimeの記載は削除して旧式のランタイムで動かすと良いでしょう。
ちなみに、上記ではメッセージナシの文字列ですが、実際には
1 2 3 |
"chat": { "addToSpaceFallbackMessage": "追加してくれてありがとう!!" }, |
みたいな形で記載すると、スペース追加時に自動メッセージが出るらしいです。ただ、後述してるonAddToSpace関数があるので、特にここで記述する必要性はありません。
図:謎の文字列を追記しておきます
Google Chat側の準備
Chatのスペースを作成しIDを取得する
今回はGoogle Chatのスペースに対してチャットをぶん投げるので、「キノコを語る会」というスペースを作成。その後URLを取得します。
- Google Chatを開く
- スペースの横の+アイコンをクリックして、スペースを作成をクリック
- スペース名を適当につける(今回はキノコを語る会とした)
- スペースのアクセスは、ドメイン全体を選択しておく
- 作成をクリックするとスペースへのURLが出る。「https://mail.google.com/chat/u/0/#chat/space/」に続けて表示されてるのが、スペースのIDとなるので、これを控えておく
- GAS側のコード内にある「spaceid」の中に5.のスペースのIDを記述して再度デプロイを実行しておく。
図:スペースのIDをもってメッセージを送信する
チャットボットの追加
チャットの左上にある+アイコンをクリックして、チャットボットを追加します。GCP側で設定しておいた構成で出てきますので、これを追加します。追加しないと、APIのみではメッセージ投稿が出来ないので、注意が必要です。
- Chatスペース名をクリックしてアプリと統合をクリック
- アプリを追加をクリックする
- Chat APIの構成を作るで控えておいたアプリ名を元に検索(今回はchatmanです)
- 検索結果に出てくるので、選んで、追加をクリックすると準備完了
もし出てこない場合、1度Google Chatをリロードしてからもう一度作業しましょう。またそれでも出てこない場合、再度デプロイを実施して新しいバージョンでデプロイし、Google Chatをリロードすれば出てくるはずです。
図:これでチャット自動投稿の準備完了
ソースコード
認証用コード
サービスアカウントの認証用のコードです。startoauthを実行してAccess Tokenを取得できればOK。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
//認証用各種変数 var tokenurl = "https://oauth2.googleapis.com/token" //メニューを構築する function onOpen(e) { let ui = SpreadsheetApp.getUi(); ui.createMenu('▶設定') .addItem('OAuth認証', 'startoauth') .addSeparator() .addItem("メッセージ送信","sendChatMessage") .addToUi(); } //OAuth2認証を実行する function startoauth(){ //UIを取得する let ui = SpreadsheetApp.getUi(); //認証を実行する let service = checkOAuth(); ui.alert("認証が完了し、Access Tokenを取得しました。") } //アクセストークンテスト function testAccessToken(){ let service = checkOAuth(); console.log(service.getAccessToken()) } //Google DriveにあるサービスアカウントキーのJSONファイルを取得する function getServiceAccKey(){ //JSONキーファイルを取得する //fileIdを取得する let Properties = PropertiesService.getScriptProperties(); let fileId = Properties.getProperty("jsonman"); //JSONファイルの中身を取得する let content = DriveApp.getFileById(fileId).getAs("application/json").getDataAsString(); return JSON.parse(content); } //OAuth2.0認証を実行する function checkOAuth() { //JSONファイルの中身を取得する let privateKeys = getServiceAccKey(); return OAuth2.createService('GoogleCloud:' + Session.getActiveUser().getEmail()) //アクセストークンの取得用URLをセット .setTokenUrl(tokenurl) //プライベートキーとクライアントIDをセットする .setPrivateKey(privateKeys['private_key']) .setIssuer(privateKeys['client_email']) //Access Tokenをスクリプトプロパティにセットする .setPropertyStore(PropertiesService.getScriptProperties()) //スコープを設定する .setScope('https://www.googleapis.com/auth/chat.bot'); } |
チャット投稿コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
//エンドポイントの設定 var endpoint = "https://chat.googleapis.com/v1/spaces/"; var spaceid = "ここにスペースのIDを入れる"; //サービスアカウントでChatに対して送信する function sendChatMessage() { let ui = SpreadsheetApp.getUi(); //OAuth認証情報を取得 let service = checkOAuth(); //メッセージをAPIへ渡す try{ if (service.hasAccess()) { //Access Tokenを取得 let accessToken = service.getAccessToken(); //エンドポイントを構築 let url = endpoint + spaceid + "/messages"; //メッセージを構築 let message = { "text" :"テストメッセージ送信♥" } //送信オプション let options = { "method" : "Post", "payload" : JSON.stringify(message), "muteHttpExceptions": true, "headers": { "Authorization": 'Bearer ' + accessToken, "Content-Type" : 'application/json; charset=UTF-8' } } //urlfetchでリクエスト var response = UrlFetchApp.fetch(url,options); console.log(response.getContentText()); //responseを取得する ui.alert("投稿完了\n"); } }catch(e){ //エラー発生時 ui.alert("error:" + e.message); } } |
- 控えておいたスペースのIDをspaceidに記述しておきます。
- OAuth2.0ライブラリによって、Access Tokenの取得が行われ、UrlfetchAppにてPOSTリクエストします。
- 今回のコードは単純に新規スレッドに投稿してるだけなので、毎回新規スレッドになりますが、responseの中にスレッドIDがあったりするので、それを元にスレッドに対して返信も可能です。
図:botとしてchatmanが投稿してくれました
自動応答メッセージのコード
Google公式のGoogle Apps Scriptのテンプレートを見ると、onMessageといった特殊なシンプルトリガーみたいな関数が用意されています。これらは、メンションでchatbotに対してメッセージを投稿した場合や、スペースにボットを追加・削除した場合に発動する特別な関数です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
//メンションでメッセージを受けた場合に自動応答 function onMessage(event) { var name = ""; if (event.space.type == "DM") { name = "You"; } else { name = event.user.displayName; } var message = name + " said \"" + event.message.text + "\""; return { "text": message }; } //スペースにボットを追加した時のウェルカムメッセージ function onAddToSpace(event) { var message = ""; if (event.space.singleUserBotDm) { message = "Thank you for adding me to a DM, " + event.user.displayName + "!"; } else { message = "Thank you for adding me to " + (event.space.displayName ? event.space.displayName : "this chat"); } if (event.message) { // Bot added through @mention. message = message + " and you said: \"" + event.message.text + "\""; } return { "text": message }; } //スペースからボットを削除した場合のメッセージ function onRemoveFromSpace(event) { console.info("Bot removed from ", (event.space.name ? event.space.name : "this chat")); } |
- eventの中にメッセージのタイプやら内容やらが詰まってるので、これを元に判定して返したりします。
- 日本語形態素解析APIのようなものを合わせて使う事で、チャットの内容を解析し、スプレッドシートの内容を検索して返すといったようなロボットを作る事が可能です。
- 他にもインタラクティブカードというタイプの返答をしたりも可能のようです。
- Slackのようなスラッシュコマンドもサポートしています。
- 自動応答メッセージでダイアログを表示して返答を受け付けるといったような機能も装備しています。
- 単純なボットというだけでなく、かなり色々とできそうな機能が装備されてるので、作り込み次第では、かなり業務軽減になる可能性を秘めていると思います。
図:オウム返しする自動ボット
独自のスラッシュコマンドを装備
Slackなどでよく見かける「スラッシュコマンド」。例えば、/member 社員番号と入れると部署名やらメアドやらの情報を返すといったようなコマンドをGoogle Apps Script側で構築することが可能になります。Chat APIの構成からセットアップが必要です。
- Google Chat APIの構成を開く
- スラッシュコマンドの項目で、ADD SLASH COMMANDをクリック
- /kinokoなどのスラッシュコマンドを記述
- コマンドIDはこのコマンドに対する明示的なIDで、GAS側でこの値を元にどんなスラッシュコマンドが来たのかを判定します(今回は1を指定しました)
- 完了をクリックで使えるようになる。
- GAS側のonMessage関数にて、以下のようなコードを追加する
123456789101112131415function onMessage(event) {if (event.message.slashCommand) {let slashman = event.message;switch (slashman.slashCommand.commandId) {case 1: // 投票コマンド//ここに投票用のコードを記述する//引数を取得するlet member = slashman.argumentText;member = member.replace(" ","");return { "text":member };}}}
argumentTextでスラッシュコマンドに対する引数を取得出来ます。今回は/kinoko membernameといった形でスペースで区切って渡して、取得した際にスペースは削除しています。 - 引数とコマンドの判定を行ったら、あとはGASで色々処理をして、返してあげたりする
図:Cloud Console側設定
図:スラッシュコマンドを打ってみた
メッセージを装飾する
文字の装飾
Google Chatへのメッセージ送信ではHTMLは利用する事が出来ません。代わりにMarkdown記号のような形で装飾記号を利用する事で簡単な装飾を行う事が可能になっています。例えば太字にする場合には、messageの前後をアスタリスクで括ることで太字になって投稿されます。
monospaceや打消線、イタリックといったことも可能になっています。
1 2 3 |
{ "text": "今日の夕食は、さつき名物の*サバの味噌煮定食*です" } |
図:太字にしてみた
ハイパーリンクを加える
messageのtextにURLを入れ込めば普通にURL文字列がリンクとなり表示されますが、特定の文字列に対してハイパーリンクを付けた状態にしたい場合には、以下のような形でtextに装飾をすると良いでしょう。
1 2 3 |
{ "text": "ハイパーリンクを設定する。<https://officeforest.org/wp/|officeの杜>" } |
<>で括って、<URL | 文字列>という形式にするだけの簡単仕様です。
図:文字にリンクが貼られました
Cardを送信する
Google Apps ScriptにはCardServiceという特殊なクラスがあり、Cardと呼ばれるものを構築できるのですが、以前もGMailのアドオンでUIを構築する場合には利用しました。今回メッセージではなくこのカードで送りたいと思い色々チャレンジしていたのですが、どうもGoogle Chatのメッセージで送る場合はこのクラスではなく、自分でCardのJSONを構築して送信すると旨く送信できるようです。
※Teamsで言うところのAdaptive Cardの形式になります。GoogleのこちらのサイトにCard Builderと呼ぶGUIでカードを構築するツールがあるので、そちらコードを生成すると作りやすいです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
//サービスアカウントでChatに対して送信する function sendChatMessage() { let ui = SpreadsheetApp.getUi(); //OAuth認証情報を取得 let service = checkOAuth(); //カードを生成する let card = createMessage() //画像ファイルをAPIへ渡す try{ if (service.hasAccess()) { //Access Tokenを取得 let accessToken = service.getAccessToken(); //エンドポイントを構築 let url = endpoint + spaceid + "/messages"; //送信オプション let options = { "method" : "Post", "payload" : JSON.stringify(card), "muteHttpExceptions": true, "headers": { "Authorization": 'Bearer ' + accessToken, "Content-Type" : 'application/json; charset=UTF-8' } } //urlfetchでリクエスト var response = UrlFetchApp.fetch(url,options); console.log(response.getContentText()); //responseを取得する ui.alert("投稿完了\n"); } }catch(e){ //エラー発生時 ui.alert("error:" + e.message); } } function createMessage(displayName, avatarUrl) { displayName = "🤖" avatarUrl = "https://officeforest.org/wp/library/icons/spinner.gif" const cardHeader = { title: `Hello ${displayName}!` }; const avatarWidget = { textParagraph: {text: 'Your avatar picture: '} }; const avatarImageWidget = { image: {imageUrl: avatarUrl} }; const avatarSection = { widgets: [ avatarWidget, avatarImageWidget ], }; return { cardsV2: [{ cardId: 'avatarCard', card: { name: 'Avatar Card', header: cardHeader, sections: [avatarSection], } }], }; } |
- createMessageにてカードを構築します。
- sendchatMessageでは、createMessageの中身を受け取って直接payloadにJSON.stringifyにて送信する
- CardではHTMLをサポートしているのでノーマルのテキストと違い、書式設定が可能です。
旨くいくと以下のスクショのようにカード形式で送れるようになるので、様々な装飾を施したメッセージを送ることが可能です。この手法は、Webhookへ投げる場合でも有効です。
図:画像とテキストをカード形式で投稿してみた
Advanced ServiceにChat API登場
いつの間にか、Google Apps Scriptの開発画面のサービスに「Google Chat API」が登場してるのですが、何故かこれを追加して以下のようなコードを記述し、認証を実行しようとすると400 invalid_scopeとして「https://www.googleapis.com/auth/chat.import」を求められてアクセスがブロックされます。
リファンレスページも用意されているのですが、どうしてなのか?調べてみたら、こちらに回答がありました。appsscript.jsonに以下のスコープを別途追加しないと認証出来ないようです。よって、これを使う場合は他のスコープ類もoauthScopesに追加することが必須になるので要注意です。
1 2 3 |
"oauthScopes": [ "https://www.googleapis.com/auth/chat.messages.create" ] |
送信する場合ですが、spaceNameはspaces/にスペースのIDをつなげたものを利用します。また、メッセージはJSON形式でtextに対して送信したいメッセージを記述し、最後にChat.Spaces.Messages.createにて送信すると送ることが可能です。以下のコードの事例は自分自身が自分として投稿してることになるので、サービスアカウントとして投稿でない形でのノーマルな投稿になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
var spaceName = "spaces/AAAAxxxx"; //Advanced Serviceで送信する function sendChatMessage2() { let ui = SpreadsheetApp.getUi(); //APIへ渡す try{ //送信メッセージ let message = {'text': '送信できるかな?'}; //メッセージ送信 Chat.Spaces.Messages.create(message,spaceName); //responseを取得する ui.alert("投稿完了\n"); }catch(e){ //エラー発生時 ui.alert("error:" + e.message); } } |
サービスアカウントを使っての投稿をする場合には、前述までのサービスアカウントのTokenを利用して投稿するようにします。すると全ユーザからはChatbotからの自動投稿として見えるようになります。利用するトークンで自分が投稿になるのか?サービスアカウントが投稿することになるのか?が分かれますので要注意です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
var spaceName = "spaces/AAAAxxxx"; //サービスアカウントでChatに対して送信する function sendChatMessage() { let ui = SpreadsheetApp.getUi(); //OAuth認証情報を取得 let service = checkOAuth(); //APIへ渡す try{ if (service.hasAccess()) { //Access Tokenを取得 let accessToken = service.getAccessToken(); //メッセージを構築 let message = { "text" :"テストメッセージ送信♥" } Chat.Spaces.Messages.create( message, spaceName, {}, {'Authorization': 'Bearer ' + accessToken}); //responseを取得する ui.alert("投稿完了\n"); } }catch(e){ //エラー発生時 ui.alert("エラー:" + e.message); } } |
図:サービスに登場して使えるようになってる
メンションを加える
概要
また、messageのtext中で使えるものとして、特定の相手に対して「メンション」する機能がありますが、Google Chat APIでメンションをする場合には、対象ユーザのUSER_IDが必要です。このUSER_IDはAdmin SDKやPeople APIで取得するメアドに紐づいてる数値であり、メアドそのものではありません。
決まった特定のユーザにだけということであればコードで毎回USER_IDを取得するのではなく、以下の手順で調べることが可能です。
- こちらのページを開く
- 右サイドバーの「API」をクリックする
- query欄に対象ユーザのメールアドレスを入れる
- readMask欄には「names」を入れる
- sources欄では「DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE」を選択する
- Executeをクリックする
- 認証画面になるので指示に従って認証を進める
- 下の欄に成功すると200が返ってきて、対象ユーザの情報が返ってくる。
- JSONの中のnames→metadata→source→idの値がUSER_IDとなります。
この値を元にGASの中でメンション用の書式で送信をすることになります。
図:手動でUSER_IDを調べることが可能です
図:返ってきた情報
なお、Admin SDKでUSER_IDを取得する為にはサービスでAdmin SDKを追加し、以下のコードを呼び出すことで対象のメールアドレスのUSER_IDを取得することが可能です。管理コンソールにおける管理者権限が必要になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//Admin SDKでUSER_IDを調べる function getUserIdByEmail(email) { try { //対象ユーザの情報を取得する const user = AdminDirectory.Users.get(email); if (!user || !user.id) { throw new Error("対象ユーザが見つからなかった"); } //USER_IDを返す return user.id; } catch (e) { Logger.log("エラー: " + e.message); } } |
メンションするコード
前述でメンション先のユーザのUSER_IDを取得したら、この値を元にメンションを送ります。以下のコードは
- privateMessageViewerで特定ユーザに対してだけ見えるメッセージにしています(通常は不要)
- \nというエスケープシーケンスを使って文中で改行を入れています。
- mention変数で<users/xxxxxx>という文字を構築し、メンション用の構文としています。
こうしてWebhookに対して送ることで、メンション付きでメッセージを送信することが可能になります。また、@全員でスペース全員宛にメンションを送れますが、APIで実現する場合は「<user/all>」で送れば実現可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
//Webhookに対してメッセージを送り込む function pushWebhook(chatman) { //メンション用変数 let userId = "ここにユーザのUSER_IDを入れる"; let mention = `<users/${userId}>`; //メッセージを作成 let message = { 'text' : "メッセージを送るよ🍎\n\n改行も出来るよ\n\nメンションも" + mention + "出来るよ", privateMessageViewer: { name: "users/ここにユーザのメアドを入れる" } }; //送信オプション let options = { 'payload' : JSON.stringify(message), 'method': 'post', 'contentType' : 'application/json' }; //POSTにて送信する let response = UrlFetchApp.fetch(webhookman,options); } |
図:メンションを送ることに成功しました
図:全員宛にメンション
その他の機能
Google Chatに送る場合、添付ファイルなどはつけることは出来ませんが、オプションとしていろいろな機能が実現できるようになっています。messageの変数内に含めて指定します。
- privateMessageViewer:対象のスペースに於いて特定のユーザにだけ見えるメッセージを送ることが可能です。
- accessoryWidgets:前述のCardを使ったインタラクティブなウィジェットを構築することが可能です。なお、インラインの場合、cardsV2を指定して構築します。
- thread.threadKey:特定のチャットのスレッドに対して返信します。threadIdが必要になる。
Webhookで投稿する方法
単純に機械的に投稿するだけ、ということであれば前述でも出てきた「Webhook」を設置して、そのURLに対してGoogle Apps Scriptで叩くだけでも投稿可能です。ただし投稿するだけなので、一方的な通知等といった用途に限られます。
しかし、前述のChatbotの設定やらサービスアカウントやらの設定が一切不要なのでお手軽にGASのみで叩けるメリットがあります。
Webhookの設置方法
まずはWebhookを設置してそのURLを取得します。
- Google Chatを開き、対象のスペースを開く
- 上のほうにある「スペース名▼」をクリックして、
- アプリと統合をクリックする
- ダイアログが出てきたら、「Webhookを管理」をクリックする
- 着信Webhookのダイアログが出るので、名前とアバターのアイコンURLを指定して、保存をクリック
- URLが出るので、コピーアイコンをクリックして、URLをコピーする。
図:ちょっとわかりにくい場所にある
図:このURLに対してGASで叩く
ソースコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
//webhookurl var webhookman = "ここにwebhook URLを入れる"; //Webhookに対してメッセージを送り込む function pushWebhook() { //uiを取得 let ui = SpreadsheetApp.getUi(); //メッセージを作成 let message = { 'text' : 'きのこの山が正義!!🍄' }; //送信オプション let options = { 'payload' : JSON.stringify(message), 'method': 'post', 'contentType' : 'application/json' }; //POSTにて送信する let response = UrlFetchApp.fetch(webhookman,options); //終了メッセージ ui.alert("Webhookにメッセージを送信しました。") } |
- POSTで送信する必要があります。
- メッセージはJSON.stringifyで括ってから送信する必要があります。
図:簡単に通知投稿が可能です。
関連リンク
- 【Google Chat API】Incoming Webhook を Go で触ってみる
- Google Chat API - Developing apps with Apps Script
- GoogleChat のREST APIを叩いてみるよ スペース情報をとってくる編
- 🤖ChatGPT APIを使って情シスのヘルプデスク業務を楽にする
- GASでGoogle Chatにメンションでメッセージを送信する方法
- Google Apps Script Chat app
- 【Google Apps Script】GASからGoogleChatにメッセージを送る方法
- apps-script-oauth2/samples/GoogleHangoutsChat.gs
- Apps Script で初めての Google ハングアウト chatbot を作成
- GASでhangouts chatのbotを作ってみる
- 【業務効率化】GoogleChatを自動化するスクリプトをGoogleAppsScript16行で書いていく【もう人間は何もしない】
- Google Chat に自動投稿する方法
- goo形態素解析APIをGoogle Apps Script(GAS)で利用する方法
- Google Workspace 従来のハングアウトのGoogle Chatへの移行のお知らせ
- テキスト メッセージを作成する
- カード メッセージを作成する
- 装飾テキスト
- How to @mention a user in Google Chat Chat App
- Google Chat API を使用してメッセージを送信する
- Google Chat botがメンションするのに必要なUser IDを開発者コンソールに出力するスクリプト
- Hangouts Chat Botのメッセージ仕様とかまとめ
- Googleチャットでメンションを付けてメッセージを投稿する方法~全員宛のメンション手順も
こんにちは。GoogleChatでchatGPTのAPIを利用しチャットできるようにしたいと調べていたらこちらへたどり着きました。
詳細に手順を記載いただきありがとうございます、とても参考になります。
手順通り実施してみて一点躓いた点があり、もし原因がおわかりでしたらご教示いただけないでしょうか。
認証用コードのcheckOAuth関数を実行すると、
let content = DriveApp.getFileById(fileId).getAs(“application/json”).getDataAsString();
の部分でエラーが出てしまいます。
デバッグでcontent 変数がundefinedなのでjsonファイルをうまく取得できないようなのです。
jsonファイルは同アカのドライブへアップし、URLの○部分(https://drive.google.com/file/d/○〇〇/)をjsonmanとしてスクリプトプロパティへ格納しました。
GASからドライブへのアクセス権限がないことも原因かと思い、ドライブAPIを有効にしマニフェストでドライブのスコープを追加してみたのですがエラーは解消しませんでした。
主様で他に実施された設定などはございませんでしょうか?
AN様
officeの杜管理人です。
対象のJSONファイルのIDが間違っていないことを前提に考えた場合であれば、以下のようなコードでも取得できないでしょうか?
var prop = PropertiesService.getScriptProperties();
var fileid = prop.getProperty(“jsonman”)
var file = DriveApp.getFileById(fileid);
var jsondata = JSON.parse(file.getBlob().getDataAsString(“UTF-8”));
fileidが○の部分が入ってくる。そして、それを元にDriveAppで取得し、中身をgetblobのgetdataasstringで取得する。
ならどうでしょうか?単純にfileidを元に引っ張ってきてるだけなので、jsonmanにファイルIDが入っていないか、誤っているか?ではないかなと思います。前述のコードもこのコードもどちらでも、JSONファイルの中身を先程試してみたら取得出来ていたので、fileIdがきちんと取得できてるかを確認してみてください。
管理人様
返信いただきありがとうございます。
いただきましたコードで試してみたところ、無事取得jsonを取得できました。
全体を通して実行してみたいと思います。
お忙しい中ご連絡いただきありがとうございます。