GASで社内のメールアドレスと氏名を対応づけようと思ったら、苦しかった話

# Google Apps Script便利ですね
`const`, `let`や`() => {}`記法が使えないなど、つらいところはありますが。
手軽さ、Googleの各種サービスとの連携のしやすさ、タイマー起動にも対応、Googleアカウントさえ持ってれば動かせるなど、便利ポイントがいっぱいです。

弊社では、G Suite (法人版Googleアカウント)を使っているので、Google Apps Scriptも便利に使っています。

# メールアドレスと氏名の対応が欲しかった
社内Webサイトをスクレイピングして、社員の氏名を取得して、メールアドレスに紐づけて、通知を送りたかったんですね。

氏名⇒メールアドレスの対応付けでもよかったんですが、全員に通知を送ると迷惑そうなので、通知欲しい人だけGoogle Formsで受け付けて、メールアドレス⇒氏名の対応を取ることにしました。
これに関しては、(同姓同名とか表記ゆれとか、そういうのさえ考えなければ)どっちでも難易度はさほど変わらない気がしています。

そんなん楽勝でしょ。そう思ったんですが、そうでもありませんでした。

# Google Contactsを使う
## ContactsApp.getContact(emailaddress)
[ありました!](https://developers.google.com/apps-script/reference/contacts/contacts-app#getContact(String)) これですよ!

```js:Contactsを使って名前を取得
var contact = ContactsApp.getContact(emailaddress);
console.log(contact.getFullName());
```

自分のメールアドレス入れてみたら、自分の名前とか、完璧に表示されちゃってます！
試しに先輩のメールアドレス入れてみるじゃないですか！　やっぱり表示されるじゃないですか！
完璧ですね！

……って思うじゃないですか。
なぜか人によってはcontactにnullが入ってるんですよね。

なんなんだろ、といろいろ考えたんですが、メールを受け取ったことがある相手じゃないと、コンタクトから取れないっぽいなー、という結論に達しました。

## ディレクトリってあるやん?
G Suiteの[Googleコンタクト](https://www.google.com/contacts)には、ドメイン内の人の名前、メールアドレスが表示される「ディレクトリ」というグループがあります。それ、使えないのかな? ということで。

```js:ディレクトリを探せ
console.log(ContactsApp.getContactGroups().map(function(g){return g.getName()}));
// result: [Starred in Android, System Group: Coworkers, System Group: Family, System Group: Friends, System Group: My Contacts]
```

それっぽいものが！　ない！

## 秘技：すべてMyコンタクトに追加
だんだん胡散臭い手法になってきました。
ディレクトリをすべて選択して、Myコンタクトに追加します。
新たに人が増えたら、また同じことをします。たいへんだー。

```js:(再掲)Contactsを使って名前を取得
var contact = ContactsApp.getContact(emailaddress);
console.log(contact.getFullName());
```

今度はnullは返ってこなくなりました。けれど、 `getFullName()` すると、 `""` が返ってきます。
試しに `getFamilyName()` や `getNickname()` も呼んでみましたが、やっぱり `""` が返ってきました。

StackOverflowを[調べる](https://stackoverflow.com/questions/14653914/contactsapp-getfullname-returns-nothing-from-contacts-added-from-directory)と。

> I can't be 100% sure, but I suspect strongly that adding a contact from the shared directory to your own contacts doesn't really copy it over until you do something that edits the contact (like sending them an email or adding them to a group). Apps Script sees the same contacts that are exposed in the Google Contacts GData API, but it does not currently have the ability to see directory contacts, so this would explain your results.

要するに、ディレクトリからMyコンタクトに入れても、手で編集しないと取れないよ、と言っているように見えます。
……ダメだこりゃ。

違う方向から攻めてみましょう。

# 全社宛てのメーリングリストのメンバーを取得してみる
弊社には、全員にメールを送るためのメーリングリストがGoogle Groupsで作ってあります。
じゃあ、そのメーリングリストのメンバー一覧を取得したら一件落着ですね。

```js:メーリングリストのユーザ一覧
console.log(GroupsApp.getGroupByEmail(mailinglist_addr).getUsers().length)
```

なんと……権限がありません……

[困ったときのStackOverflow](https://stackoverflow.com/questions/30179383/google-groups-api-getusers-you-do-not-have-permission-to-view-the-member-lis)です。スタック溢れどころか、1件も取得できてないのに。皮肉なサイト名です。

どうやら、自分がメーリングリストのオーナーかマネージャーじゃないと、リストは取得できないみたいですね。残念。
Admin SDKを使えば取れるよ、とも書いてあります。

# Admin SDKを使う
……って、やる必要あるんですかね?

Adminじゃないんだから、使えないだろ、と思って、試してません。
一応、[Googleのドキュメント](https://developers.google.com/admin-sdk/directory/)と[日本語のQiita記事](https://qiita.com/ttyokoyama/items/f41cd5f4f6c4dc2e9ee3)貼っておきますので、Admin権限持っている人はどうぞ。

# Googleカレンダーで全社イベントを拾って、参加者のリストを取得する
弊社では、月初に「全社会」なるイベントが開かれます。
全社会に呼ばれている人の一覧を取得したら社員全員の一覧が手に入るだろう、という算段です。

毎月開かれてますが、なかった月もあった気がしたのと、次回のも取れるなら、そっちの方が情報が新しいので、2ヶ月前から2週間後までの全社会を取ってきて、日付が一番新しいもののメンバーリストを取得してみましょう。

```js:カレンダーを検索して参加者を取得
  var start = new Date(Date.now() - (1000 * 3600 * 24 * 62)); // 2ヶ月前
  var end = new Date(Date.now() + (1000 * 3600 * 24 * 14)); // 2週間後
  var events = CalendarApp.getDefaultCalendar().getEvents(start, end, {search: "全社会"});

  if (events.length > 0) {
    var last = events[0];
    for (var i = 1; i < events.length; i++) {
      if (events[i].getStartTime() > last.getStartTime()) {
        last = events[i];
      }
    }
    console.log(last.getGuestList().map(function(g){return g.getName()}));
  }
```

なんと、返ってきたのは1件。それも、全社宛てメーリングリストの名前でした。

全社会イベントを作るとき、わざわざ全社員をゲストに入れるのは面倒です。そこで、全社宛てメーリングリストを招待したら、メーリングリストに入っている全員が招待されるんですね。
G Suite便利ですね。よくできてますね。……つらくなってきた。

[EventGuest型のドキュメント](https://developers.google.com/apps-script/reference/calendar/event-guest)を見ても。それっぽいメソッドは全く生えてませんね。
`getAdditionalGuests()`って名前は気になりますが、Integerを返すので関係なさそうです。ダメもとで呼び出したら0が返ってきました。

# Do you know Copy & Paste?
諦めた私は、Googleコンタクトにある「ディレクトリ」を、コピーして、Excelに貼り付けて加工してから、Google Spreadsheetに貼り付けることにしました。
Spreadsheetから値を引っ張ってくるのはとても簡単ですから……

```js:Spreadsheetから値を取り出す
function getAddrNameMap() {
  var sheetId = "シートのID";
  var sheet = SpreadsheetApp.openById(sheetId);
  var range = sheet.getDataRange();
  var values = range.getValues();

  var map = {}
  for (var i = 0; i < values.length; i++) {
    map[values[i][1]] = values[i][0];
  }
  return map;
}
```

言うまでもなく、難点は、社員が入ってくるたびに手動で更新しないといけないことですね。
(やっぱこれ、作った人がやるのか?……)

# どんな手が取れたのか、今後、どんな手が考えられるか
今回はエクセルとコピペで乗り切りましたが、未来永劫、これではマズい。
どんな手が取りえたのか振り返り、また、今後どういう手を使うのか、考えてみました。

- Google Formに名前入れるところ作ればよかった
- Google Formじゃなく、名前とメールアドレスをSpreadsheetに残すWebアプリにすればよかった

今回、通知はオプトイン形式にする、ということだったので。名前入れてもらったらよかったですね、という話です。
ていうか、簡単に取れると思ったから、そういう風に作らなかったのに、簡単に取れなかったんです。

- 管理者にリストを作ってもらう

社員やメールアドレスの対応表くらい、社内にあってもいいような気がします。
管理者ならAdmin API叩いて作れる、ということなら、管理者にやってもらってもよかった気がします。
今後も自動化やっていきたいなら、そういう社内環境を整えるよう、偉い人に相談してみるのもいいかもしれません。

- 社内Webサイトに手を加える

実は今回の社内Webサイト、氏名の出どころはGoogleアカウント。内部的にはメールアドレスと名前の対応を持っているのですが、それは見えるところには出てこないため、今回のように七転八倒することになりました。
そちらに手を加えて、取れるようにしてもよかったかもしれません。

**めげずにこれからも、自動化、効率化、やっていきましょう。**