Google Apps ScriptとMicrosoft Graph APIの連携 - Teamsログ取得編【GAS】

社内外でTeamsのチームに投稿されたチャットログを集計したい要望が非常に高くなっています。しかし、このデータを取得するのは容易ではありません。Power Automateだと新規メッセージ投稿時か?自分自身にメンションがあった場合のデータを取ることは出来ますが、それ以外のケースは取れません。また、いいね等の集計も出来ません。誰かが返信やメッセージ投稿したら全て取得するアクションがあれば良いのですが、現在は用意されていません。

そこで使う事になるのがMicrosoft Graph API。ただし管理者権限が必要になります。今回はこのAPIを使って、Google Apps ScriptからTeamsのチャットログを取得してみたいと思います。

※最近Google Apps ScriptはAPI追加とかほとんど止まってるからなぁ。KeepやMeetのAPI用意してくれたらいいのに。

今回使用するスプレッドシート等

事前にAzure ADにてプロジェクトの作成が必要になっています。また、アクセス権限として管理者権限が必要となるので、許可を得る必要があります。

事前準備

利用する為にはAzure ADでアプリ登録を行い、Client IDとClient Secretの2つ取得しておく必要があります。微妙に以前とは取得方法が異なっている為、改めてここで2020年6月現在の取得方法を記述しておきたいと思います。現在は、Azure ADも無償で利用が可能になっているので、フリーアカウントの場合でも構築する事が可能になっています(ただしフリーアカウントで認証を実行できるようにするには、ちょっと手順が必要です。)。この為だけにMicrosoft365契約するのはちょっとね・・・

GAS側の事前準備

ライブラリの追加

以下の手順でOAuth2 for Apps Scriptライブラリを追加しましょう。

  1. スクリプトエディタを開きます。
  2. メニューより「リソース」⇒「ライブラリ」を開きます。
  3. ライブラリを追加欄に「1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF」を追加します。
  4. 今回はバージョンは38を選択してみます。
  5. 保存ボタンを押して完了

これで、OAuth2.0認証にまつわる様々な関数を手軽に利用できるようになります。

図:ライブラリを追加した様子

コールバックURLを取得する

コールバックURLとは、認証を完了しAccess Tokenを取得したら戻るべきURLを指定するものです。これは、スクリプトIDをもとに作られているので、スクリプトIDを取得して組み立てます。

  1. スクリプトエディタのメニューより「ファイル」⇒「プロジェクトのプロパティ」を開く
  2. 情報の中にある「スクリプトID」を控えておく。
  3. https://script.google.com/macros/d/スクリプトID/usercallback として組み立てる。これがコールバックURLとなる。

図:スクリプトIDはファイル毎に異なるのです。

Azureでプロジェクトを作成

  1. アプリの登録にて登録を開始する
  2. 新規登録をクリックする
  3. 名前を入力(今回はteamsscraperと入力しました)、リダイレクトURIは「webを選択しhttps://script.google.com/macros/d/スクリプトID/usercallback」を入力
  4. 登録ボタンをクリックする
  5. 出てきた中で、「アプリケーション(クラと書かれているのがクライアントID」なので、このコードをメモしておく
  6. 左サイドバーより、「証明書とシークレット」をクリック
  7. 新しいクライアントシークレット」をクリックする
  8. 今回は特に有効期限を設けないで追加をクリック
  9. これで値に「クライアントシークレット」が生成されて手に入りました。このシークレットはこの時だけしか表示されないので、注意してください。
  10. つづけて、左サイドバーより「APIのアクセス許可」をクリックする
  11. Microsoft APIの中にある「Microsoft Graph」をクリックする。
  12. 委任されたアクセス許可」をクリックする
  13. デフォルトでUser.ReadがすでにONなので、今回はoffline_accessGroup.Read.AllUser.Read.AllGroup.ReadWrite.All、ChannelMessage.Read.Allを検索してONにしましょう。
  14. アクセス許可の追加をクリックする
  15. 追加出来たら、xxxxxに管理者の同意を与えますをクリックします。すると、状態が緑色になります。
  16. 次に左サイドバーより「認証」をクリック
  17. 暗黙の付与にて、「アクセストークン」にチェックを入れる
  18. サポートされているアカウントの種類に於いては、「マルチテナント」にしておきました。
  19. 保存をクリック
  20. 概要のエンドポイントをクリックすると、いろいろなエンドポイントURLが出る。
  21. 概要のディレクトリ(テナントの数値はメモっておきます。あとでプログラム中で使用します。
  22. デフォルトでは組織アカウントでなければOAuth2.0認証が出来ません。フリーのMicrosoftアカウントでも認証できるようにするためには以下の手順が必要です。
  23. 左サイドバーのマニフェストをクリックします。
  24. マニフェストエディタが起動します。その中にあるsignInAudienceの値を「AzureADandPersonalMicrosoftAccount」へ変更して保存をクリック。これで例えばhotmailアカウントでも認証が通るようになります。

※3.でWebを選ばないSPAを選んでしまうと、Proof Key for Code Exchange by OAuth Public Clientsといったエラーが出てしまい認証ができませんので注意。

※個人アカウントでうまく動作しないなぁと思った場合には、Azure Portalのユーザ画面にてonmicrosoft.comのアカウントを作成してそれで認証作業を行うと良い。この時、グループとロールにおいては「アプリケーション管理者、クラウド アプリケーション管理者」の2つが割り当てられてればOKです。個人的にはこの作業をオススメします。以降、ログイン認証等もこのアカウントで行います。

図:アプリの登録から全ては始まります。

図:Graphを選択する

図:アクセス権限付与した状態

図:認証の設定変更に注意

図:フリーアカウントでも可にするとこういう表示になる

図:onmicrosoft.comアカウントのロール

Teamsのログ取得に必要な情報を集める

Teamsのログ取得に必要な情報を集めなければなりません。集めた情報でGraph Explorerを使い実際に取得できるのかどうかをテストする事が可能です。

  1. Graph Explorerに行き、ログインしておく
  2. サンプルクエリにてteamsで検索。チャネル内のメッセージをクリックする
  3. 取得するURLは、https://graph.microsoft.com/beta/teams/{group-id-for-teams}/channels/{channel-id}/messages/{message-id}といったようなスタイル
  4. グループIDがチームのIDとなります。チームを開きこのチームへのリンクを取得でリンクをまず取得します。
  5. groupIdが入ってるので、そのIDを取得します。
  6. 次にチャネルIDが必要です。対象のチャネルを開きます。
  7. URLを見てみると、threadId以下に数字2桁:半角英数文字列~@thread.skypeの文字があります。これがチャネルIDとなるので控えておきます。
  8. チームのIDをgroup-id-for-teamsに入れ、チャネルIDをchannel-idに入れる。message-idは不要なので削除する。
  9. 組み立てたURLを入れてクエリ実行をする
  10. まだこの段階では取れていません。ここでアクセス許可の修正をクリック
  11. 出てきたアクセス一覧の全ての同意をクリックする。この時、onmicrosoft.comアカウントではない場合、ChannelMessage.Read.Allの同意が出来ない事があります。
  12. 無事に取得できると、OK - 200が返ってきて対象のチャネル内の全メッセージが取得出来ます。
  13. ちなみに、なんとなくわかると思いますが、JSONの中のbodyがメッセージ本文、reactionsの中のreactionTypeがいいねに該当します。displayNameが投稿した人の名前ですね。

図:求められたアクセス許可

図:認証が通ると全メッセージがJSONで取得される

認証を行う処理を作成する

OAuth2 for Apps Scriptのページの「Create the OAuth2 Service」にあるコードを元に、Google Apps Script側で構築をします。この時、Microsoft365側で取得したアプリケーションIDやシークレットを使います。また、今回はいつもよりも要求するアクセス権限が多い点と、利用するAPIはBetaを利用する点に注意が必要です(まだ現在、Graph APIでのTeamsログ取得はv1.0では取得出来ません)

GAS側コード

  • 今回利用するGraph APIのエンドポイントはhttps://graph.microsoft.com/betaとなります。
  • 要求する権限はscopeに半角スペースで区切って、Azure AD側で用意したものと同じものを設定します。
  • startoauthを実行して認証を実行すれば、スクリプトプロパティにAccess Tokenが格納されます。

HTML側コード

template.htmlというダイアログ用のファイルを用意します。ここでアクセス承認を実行し、ログインをすると、アクセストークンその他が取得可能になります。

  • 実際にこれらのコードで、startoauthを実行すると、スプレッドシート上で認証用のダイアログが出ます。
  • 認証でMicrosoft365アカウントにログインします(もしくは作成したonmicrosoft.comのアカウント
  • 取得したAccess Tokenほかはスクリプトプロパティのoauth2.Graphという項目にガッツリ値が格納されます。ここにはAccess Token, Refresh Token, expire_inのタイムなどが入っています。
  • reset関数はログアウトされて、再度認証ができるようになります。
  • Chrome v83.xを利用している場合、認証実行時にリダイレクトURLにジャンプ出来ずにエラーになることがあります。認証用URLの中のredirect_uriの文字がオカシナ文字に置き換わっていてredirect_uriが違うと怒られるケースがあります。その場合はURLを直接リダイレクトURIの部分を書き換えてください。

図:無事に認証画面へ到達出来た

図:スクリプトプロパティにAccess Tokenが格納された

取得したTeamsログをスプレッドシートに書き出す処理

大雑把ではありますが、GAS側から必要最低限の対象のチャネルのチャットログをすべて取得してみたいと思います。実用するには以下のような装備も別途必要ではないかと思います。またスレッドに対する返信は別のAPIを呼び出して返信を取得させる必要があるので、返信レスは親スレッドの数だけAPIを叩く必要性があります。

  1. idを使って取得済みのエントリーについてはスルーする仕組み
  2. 今回取得対象にしていない「添付ファイル」などについても必要であれば、Google Driveにコピーするような処理
  3. 取得対象にするチャネルを固定ではなく、選択できるように動的に指定する仕組み(これもGraph APIで一覧が取得可能です)
  4. 取得したデータを元に分析・レポート自動作成機能など
  5. 他のAPIを利用してデータを特定のアドレスに対して送り込む機能
  6. 他のAPIを利用してTeamsにチャットを送信する機能など(ただしこちらは、API使わずともIncoming Webhookでも代用できるけれど)
  7. 日付でフィルタするような仕組みも必要かもしれません。
  8. 今回はいいねは数だけを集計していますが、リアクションの種類で集計を変えるような手法も面白いかもしれません。

  • teamsurlをまず叩いて、親スレッドの一覧を取得します。親スレッドの項目をレコードとして追加しましょう。
  • 親スレッドのidを元に次に返信レスがあるかteamschildを叩いて、返信レスの一覧を取得します。0件の場合には処理をせずに次の処理へ移動します。
  • 処理すべき返信があったら、返信先に親スレッドのidを入れて、親スレッドの時と同様にJSONを分解してレコードを追加します。
  • いいねの数reactionsのlengthで取得させています。
  • その他レスした日付、メッセージ本文、送信者名を取得してappendRowでレコードを追加しています。
  • データの取得はgraphTeamsGet関数がすべて担当しています。ここでAccess Tokenを使ってGraph APIを叩き、Teamsのログを取得しています。
  • スレッドの数やレス数が膨大な場合、UrlfetchAppのQuota制限GASの実行時間制限(Basicだと6分)に引っかかる可能性があります。回避する為にsleepを入れたり、うまいこと制限を回避するような仕組みも必要かもしれません。
  • Node.js + Passport Azure AD + Electronでアプリケーションを組んだほうが現実的かもしれません(こちらなら制限が掛かりにくい)。
  • ちなみに、データは投稿された日付が新しいほうから取得されていくので、最新版のレスが一番上になります。

図:親スレッドと返信データをすべて抽出できました

Teamsのタブに埋め込む

Google Apps Scriptで作ったウェブアプリケーションやスプレッドシートは、Teamsのタブに埋め込む事が可能です。作業はひどく簡単なので、こうしておくことで、実際にそのTeams関連のアプリをダイレクトにその場で作業ができるのもメリットの一つです。

  1. Google SpreadsheetやGoogle Apps Scriptで作成したウェブアプリケーションのURLを取得する
  2. Teamsのチャネル上部にある+ボタンをクリック
  3. タブの追加では「Webサイト」をクリック
  4. タブ名を入れて、1.のURLを入れて、保存ボタンをクリック
  5. これでチャネルにスプレッドシートやウェブアプリが埋め込み可能です。流れ作業でそのまま関連の仕事をできるので非常に効率アップします。

図:スプレッドシートにアプリを埋め込み

返ってくるJSONのサンプル

今回のTeamsデータ取得にてGraph APIから返ってくるJSONのサンプルは以下のような感じになります。valueにJSONの塊としてレスが複数入ってくるイメージです。

関連リンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)