Google Apps ScriptでContactsをPeople APIで弄る【GAS】

Androidの連絡帳でもあり、メールの送信先管理でも使うGoogle Contact。これを扱うGoogle Apps ScriptのクラスがContactsAppだったのですが、これ2022年1月19日に廃止されていて現在は非推奨となっています。またSunset Scheduleにも記載が追加されており、今更これで構築するのはオススメできません。

現在はPeople APIへと置き換えられているようですが、必ずしもContactsAppと同じことができるわけでもないようなので、今回このAPIについて調べてみました。

図:連絡先を弄るためのAPIです

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

概要

People APIはAdvanced Serviceから有効化してから使う仕組みなのですが、Google Apps Scriptから使うリファレンスが充実しておらず、ContactsAppで構築していた人は結構書き直すのに苦労するのではないかなと思います。

Google Contactsについて

また、AdminのDirectoryも表示はされていますが、iPhoneなどから接続して参照するのは連絡先の項目だけなので、ここをきちんと整備しておかないと、組織で使うiPhoneの電話帳リストが古いままということになります。

また、この連絡帳はGoogle WorkspaceのDirectoryとはリンクしていないので、管理者が整備しておいて皆が参照するといった使い方が出来ません。個別に作業をして入れておく必要があります。1つのマスターを参照して連絡帳を構築したい場合はLDAPやCalDav、icsファイルなどを使う必要があります。

今回のファイルは各個人が最新の連絡先情報として更新させる為に使うものになります。

APIの独特の仕様

今回のAPIを利用するに当たって独特の2つのポイントがあります。

1つはetag。これは手動自動問わず編集をすると変わると特性があり、これを書き込み時に指定をしないと更新が出来ず、値が現在のetagの値と一致してない場合エラーになります。一覧取得で値を取り出し即座に編集時に利用します。

2つ目はリソースネーム。people/meで自分自身を表すリソースネームとなります。これは各人のメールアドレスと一対になっているもので、これを持って連絡帳の対象者を特定しています。メアドを変更するとリソースネームの値は変わってしまいますが、それ以外を編集しても変化しません。各個人に割り当てられてるリソースネームは重複しないユニークな値になっています。

このようにちょっと今までのAPIと違った特性があるので、ここを意識しないとうまく実装することが出来ません。

People APIの制限

People APIの既定のリミットは以下のような感じ。GCPの対象のAPIの割当に記述があります。結構わかりにくい。引っかかりそうなくらい大量にリクエストを送るような場合は、適度にSleepを入れて回避する必要がありそうですが、課金されるようなAPIではないので、超えても429エラーになるだけです。

右側にある「割当の編集」を開いて割当の増加を申し込むをクリックすると、Google Formが開いて上限引き上げを申し込むことも可能です。デフォルト値が1分辺り180件なので、300名いるような組織ならば2分にわけて更新するようなウェイトで十分行けるでしょう。

※このQuotaの値が組織によって異なるようです。自分は180だけれど別の組織だと90だったり・・・

図:ここにQuotaの記述があります。

図:割当上限引き上げが可能

iPhoneの連絡先として使う

iPhoneの連絡先は電話アプリを開き、連絡先を開くと色々と連絡先リストが出てきます。ここでGoogle Contactsの連絡先を追加して利用することが可能です。利用する手順は以下の通り。今回のスクリプトで更新するとこの中身が入れ替わる仕組みです。Androidの場合は標準のGoogle Contacts(連絡帳)というアプリが入ってるので、使用時には既にリンクされています。

  1. 設定アプリを開き、連絡先を開く
  2. アカウントをタップする
  3. アカウントを追加をタップする
  4. 「Google」をタップする
  5. GoogleアカウントにログインをするとそのアカウントのContactsとリンクします
  6. 2.の画面に戻って追加されたGmailのアカウントを開き、連絡先のスイッチがオンになってるか確認
  7. 電話アプリを起動し、連絡先を開く
  8. Gmailが出てくるので開いてみる
  9. Google Contactsのリストがズラっと出てくる

図:連絡先にアカウントを追加する

図:電話アプリからContactsのリストを見る

図:Androidの場合

事前準備

本APIを利用する為には、GCP側でAPIをオンにして、尚且Google Apps Script側でもappsscript.jsonの編集が必要です。

GCP側の準備

こちらの作業はGCP側でモニタをしたり、外部メンバーを交えてのテストなどでは必要になりますが、通常の利用では行う必要はありません

GASのプロジェクトを紐付けする

外部のメンバー参加の場合には、Google Apps ScriptとCloud Consoleのプロジェクトを紐付けする作業が必要です。以下の手順でプロジェクトの変更を行います。前述のプロジェクトの控えておいた番号を利用します。そうでない場合はプロジェクト移動をせずとも利用が出来ました(内部メンバーのみの場合)。

  1. Cloud Console側のプロジェクトのホームを開き、「プロジェクト番号」を控えておく
  2. Google Apps Scriptのスクリプトエディタを開く
  3. サイドバーからプロジェクト設定を開く
  4. GCPプロジェクトの「プロジェクトを変更」をクリック
  5. GCPのプロジェクト番号に、1.の番号を入力して、プロジェクトを設定をクリック
  6. これで紐付けが完了しました。

図:プロジェクトの移動も必須の作業です

APIを有効にする

Google Cloud Console側でもAPIを有効化する必要性があります。

  1. GCPのプロジェクトを開く
  2. 左サイドバーからAPIとサービスにて、「APIとサービスの有効化」をクリックする
  3. peopleと検索すると出てくるので、クリックします。
  4. 有効化をクリックします。
  5. 認証情報の作成は不要です

図:有効化をしておくだけでOK

GAS側の準備

appsscript.jsonに記述を追加する

スクリプトエディタの左サイドバーから「プロジェクト設定」を開き、「appsscript.json」マニフェスト ファイルをエディタで表示するにチェックを入れて、appsscript.jsonを表示する。その後そのファイルを開き、以下のように記述を行います。必須の作業です。

People APIで使うScopeを上記のように指定する必要があります。これを指定しておかないと403エラー Permission Denied等のエラーが発生してしまいます。

サービスの追加

スクリプトエディタのサイドバーにある「サービス」の横の+ボタンを押して、リストから「Peopleapi」を探し出して追加する必要があります。これにより、GASからPeople APIが利用可能になります。

図:これを追加しておく必要性があります。

初回認証をしておく

ここまでの準備で適当な関数を用意して実行すると認証が始まります。連絡先の表示、編集、ダウンロード、完全な削除というスコープが出てくるはずです。

図:認証を実行するとこの画面が出る

ソースコード

通常の単発の取得、新規追加、更新、削除のコードを使っています。また一括でそれらを行うBatchGetだけ今回のサンプルに入れてありますが更新や追加、削除にもBatchのメソッドがあり、速度を稼ぐ場合に利用しますが制限があります。

スプシのデータで更新し無ければ追加する

スプレッドシートを基準にしてループで1件ずつ更新します。連絡帳に無い連絡先は新規に追加するようにしある場合には更新をします。ただし今回はスプレッドシートに無いデータを連絡帳から削除するといったコードは追加していません。削除してはならない個別の連絡帳を手動で追加してるケースも有り得るので、例えば退職者の連絡先を消したい場合には、retireシートに記録しておいて、後述の連絡先の削除を実行すると良いでしょう。

スプレッドシートとWebアプリ側からのアクセスで処理を分岐する用にargmanの引数を付けています。

  • personkunでまずは一覧でetagと個人のリソースネーム、メアド一覧を取得しておきます。
  • スプレッドシートのメアドと一致するものがあった場合には、リクエスト内容構築します。この時リクエストには必ずetagを指定する必要があります。この値は編集後に別の値に変わる特性があるので使いまわしは出来ません。
  • そして、People.updateContactにて、リクエストと相手のリソース名、更新する場所をupdatePersonFieldsで指定します。
  • 電話番号と部署だけ更新するので、差異がなければ処理をスルーするようにしています(Quota回避と速度向上の為

単一の連絡先をラベル付きで新規追加する

指定のラベルで連絡帳に新規追加します。指定のラベルが無い場合にはラベルも作成するという仕様になっています。今回は名前、電話番号、メアド、所属組織でメンバーを新規追加しています。

usernameは半角スペースで区切られた姓名を入れておく必要があり、半角スペースで区切りそれぞれを、givenName(名前)、familyName(姓)に分けて入れるようにしています。

※ただし新規追加はとても遅いのでBatchで追加するか?初回だけはCSVでインポートのほうが望ましいです(6分の制限を超えてしまう可能性が)。

バッチリクエストで一括インポート

個別で1件1件ループで登録は非常に遅いです。とても数分で終わらないので、こういった場合にはバッチリクエストでcreateContactを実行出来ます。500件ほどで試してみましたが、非常に高速で追加が可能です。

ただし1度にバッチで送り込めるのは200人分までなので、送信するデータが200人を超える場合には複数回に分割してバッチリクエストをする必要があります。バッチ結果からresourceNameを取り出してcontactGroups.members.modifyでラベルをつければ完璧です。

※また圧倒的にリクエスト回数を減らせるのでQuota回避が容易になるので、必須のテクニックです。

図:一括インポートはバッチが必須

バッチリクエストで一括アップデート

前述のコードは一括インポートですが、次は一括アップデート。同様にQuotaの回避や実行時間の削減の為にバッチリクエストで連絡帳のアップデートを高速に行うことが出来ます。

ただし1度にバッチで更新できるのは200人分までなので、送信するデータが200人を超える場合には複数回に分割してバッチリクエストをする必要があります。またリクエストボディの作り方がちょっと特殊で、おまけにetagの値も必要な上に、前述のようにcontactsに入れるのは配列なのではなく、resourceNameをキーに使ったオブジェクトとして構築しなければならないので、かなり厄介です。

※バッチインポートとバッチアップデートの2つを使えば高速に追加と更新が出来るようになるので、このテクニックも必須です。

ちなみに、リクエストボディは実際に構築すると以下のようなスタイルになります。people/7575757といったユーザのリソースネームをキーにして、複数contactsに加えていくスタイルなので、配列ではない点をobj[key] = temparrで実現しています。こうすることで、変数をキー名に使って連想配列を連結していくことが可能です。

連絡帳のメンバー一覧を取得する

連絡帳のメンバーを全取得します。ただし仕様上各データにはラベルに関するデータがなく、ラベルは別管理扱いとなっているようです。1ターンで取れる人数は最大で1000までです。それを超える場合にはpageTokenで次のページを指定することで次の1000名が取れるという仕様です。

今回は名前、メアド、所属組織を取得します(personFieldsに指定してる内容がそれに該当する)。

連絡先が0件の場合にそなえて、その場合空の配列を返すように加えてあります。

その他のコード

自分自身のリソースネームを知る

通常はpeople/meで自分自身のリソースネームを意味します。ですが、あえて指定したい場合には自分自身のリソースネームを知りたい場合があります。其の場合には、以下のような形でコードを記述します。personFieldsで指定した領域の特定の値も合わせて取得が可能です。

連絡先を削除する

単発で1名分の連絡先を削除したい場合は、対象者のリソースネームを調べてから以下のようなコードを実行すれば、その1名だけ削除することが可能です。

特定のラベルのメンバーをバッチで取得

特定のラベルをつけたメンバーをまとめて取得します。一括で取得する場合BatchGetというのを利用するのですが、これが使い勝手が悪く最大200名までしか取得出来ませんし、nextPageTokenといったものもありません。People.ContactGroups.getでまずガッツリ取得して、そこから200人ずつをPeople.People.getBatchGetのresourceNamesに突っ込んで順番に取る必要があります。

また、この際にラベルのリソースネーム(例:contactGroups/00011110000ff)を指定する必要があるのですが、この値はラベルをクリックした時のURLに含まれています。

図:グループリソースネームの場所

特定のラベルのメンバーをバッチで削除

前述の特定のラベルのメンバーをバッチで取得するをさらに応用して、そのラベル内のメンバーを一括で削除する方法になります。削除はなぜか1度にバッチで500件まで削除が可能です。続けて、特定ラベル付きでメンバーを追加すればメンバー入れ替え更新という形が出来ます。

ウェブUI

スプレッドシートから実行する分には、スプレッドシートにアクセスしてる人(社内の人間)の権限で動作することになるので、各々の連絡帳に対して処理が実行されます。しかしこれをウェブアプリケーション上のボタンから実行させたい場合は以下の点に注意が必要です。

  1. 次のユーザとして実行では、デプロイする場合はウェブアプリケーションにアクセスしているユーザで設定
  2. アクセスできるユーザは組織内のユーザとして指定する
  3. 組織外のユーザに使わせたい場合、GCPのOAuth同意画面のテストユーザにアドレスを追加する必要があります(場合によってはGCPのプロジェクトユーザとして追加が必要な場合も)
  4. 内部メンバーだけの場合にはOAuthにメンバー追加などをする必要はありません。

ウェブ側のソースコードは以下のようなスタイルです。

図:各人の権限で実行するようにデプロイ

図:社外の人に同時に使わせるには色々手順が必要

図:シンプルにボタン1個だけ

Directory APIで実現する

概要と注意点

People APIは組織に登録されている「人」に関するデータの入出力を行うことが可能ですが、Admin SDKにも元々Directory APIという似たようなAPIが用意されています。管理者権限で実行する必要がありますが、People API同様にAdmin Consoleに登録されてるユーザに関するデータを取得が可能です。

しかし、1点問題があります

そのユーザの画像サムネイルへのURLが取得出来るのですが、この値のURLにアクセスするとリダイレクトされて別のURLに移動した後に画像が表示されます。この値をHTMLの画像のURLに使うと表示されないことがあります。しかし、リダイレクト先のURLを取得出来ない為、これを利用して画像を表示が難しいです(其の場合、リダイレクトするとこの画像が返ってきたりします)。

よって、Directory APIを使った場合には、user.id(People APIで言うところのuserのresourceName)を取得しこれを元にPeople APIに投げて情報を再取得すると、こちらは「リダイレクト後のサムネイルへのURLが取得可能」なので、こちらを利用します。People APIでも十分情報は取れるので、ユーザ情報はDirectory APIではなく、People APIを利用しましょう。

※いっそのこと、people.listDirectoryPeopleを使ってDirectoryのlistをPeople APIで取得しちゃうのもアリかも。この場合連絡帳にアップしてるユーザのリストではなくDirectoryにアクセスして取ってきてくれます。

図:Directoryに登録されてるユーザ情報を取得出来る

ソースコード

Directory APIを使った場合のユーザ情報の取得について記述しています。user.thumbnailPhotoUrlは使えないURLが返ってくるので、そこだけPeople APIにて別にサムネイル画像へのURLを取得させています。

People APIでディレクトリにアクセス

通常、People APIは連絡帳のデータにアクセスする為のものであり、ディレクトリにアクセスする為のAPIではないのですが、Admin SDKを使わずにディレクトリのユーザデータも実は取ることが可能なので、Directory APIを使わずにメンバーのデータを取得することが可能です。それが、People.listDirectoryPeople

利用する場合には、appsscript.jsonのoauthScopesに対して「"https://www.googleapis.com/auth/directory.readonly"」を追加する必要があります。

実際に取得するソースコードは以下の通りで、1回のリクエストで最大1000人まで取得可能。それ以上はpageTokenで次の1000名を取得するスタイルです。

関連リンク

コメントを残す

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

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