GASにGoogleカレンダーのイベント変更検知機能が実装されたので試してみた

>February 26, 2018
Calendar event triggers are now available. You can use these triggers in conjunction with the Calendar advanced service to discover recently changed calendar events via regular sync operations.

[GAS](https://developers.google.com/apps-script/) にGoogleカレンダーのイベント変更検知機能が実装されました。
これを使うことで、自分のカレンダーに予定が書き込まれたときだったり、(G Suite利用時の）会議室カレンダーに予定が登録されたとき、なんかにも応用できるかと思います。

サンプルのGASを書いてみたので参考までにどうぞ。

## 実装
### サービスの有効化
まず `Caledar Service` を使うので有効にしておく。

1. `Resources > Advanced Google Services` で `Calendar` を ON
2. `Google API Console` で `Calendar API` を 有効にする

### コード
`calendarUpdatedSample()` をイベント検知のトリガーにする。
ポイントとしては次の3つになる。

1. 仮引数の `e` には変更された予定情報が設定されているわけではない
2. 登録・変更・削除された予定情報を取得するには `syncToken` を予定一覧取得APIを投げるときに設定する
3. `syncToken` は予定一覧取得APIを投げるとレスポンスに入っている

今回の実装では `ScriptPropeties` に `syncToken` を保存するようにしている。
`ScriptPropeties` にない場合に、予定一覧取得APIを投げて `syncToken` を取得する形。
※手抜き実装なのでトリガー設定してから2回目以降からしか差分イベントを処理していない

```javascript:GASのコード
var scriptProperties = PropertiesService.getScriptProperties();
var nextSyncTokenKey = 'NEXT_SYNC_TOKEN';

function calendarUpdatedSample(e) {
  console.info('calendarUpdatedSample() ------------------------------');
  console.log('authMode:%s/calendarId:%s/triggerUid:%s', e.authMode, e.calendarId, e.triggerUid);

  var calendarId = e.calendarId;

  // 予定取得時にsyncTokenを指定して差分イベントを取得
  var optionalArgs = {
    'syncToken': getNextSyncToken(calendarId)
  };
  var events = Calendar.Events.list(calendarId, optionalArgs);  
  console.log('取得した予定数:%s', events.items.length);

  // 差分イベントを処理（サンプルなのここではログ表示しているだけ）
  for (var i = 0; i < events.items.length; i++) {
    var event = events.items[i];
    console.log('event.summary:%s/event.start:%s/event.end:%s/status:%s', event.summary, event.start, event.end, event.status);
  }

  // 今回処理したイベントを対象外とするためsyncTokenを更新
  saveNextSyncToken(events.nextSyncToken);
}

function getNextSyncToken(calendarId) {
  // ScriptPropetiesから取得
  var nextSyncToken = scriptProperties.getProperty(nextSyncTokenKey);
  if (nextSyncToken) {
    console.log('getNextSyncToken(from property):%s', nextSyncToken);
    return nextSyncToken
  }

  // ScriptPropetiesにない場合は、カレンダーから取得
  var events = Calendar.Events.list(calendarId, {'timeMin': (new Date()).toISOString()}); // 最後の予定を取らないといけない？みたいなので timeMinを指定
  nextSyncToken = events.nextSyncToken;
  console.log('getNextSyncToken(from calendar):%s', nextSyncToken);
  return nextSyncToken;
}

function saveNextSyncToken(nextSyncToken) {
  console.log('saveNextSyncToken:%s', nextSyncToken);
  scriptProperties.setProperty(nextSyncTokenKey, nextSyncToken);
}

```

### トリガーの設定
実装が終わったら`Edit > Current project's triggers` からトリガーを設定する。
`From calendar` が今回増えたやつで、最後の入力欄にカレンダーIDを入力する。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/f4c8f40b-4b18-fed0-dab0-15eabb73af17.png)


## 実際に動かしてみる
### 次のような予定を３つ作る
予定A、予定B、予定Cを順に作っていく
![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/842af587-5ed3-67a9-db17-1ad6388c450c.png)

すると次のようなログが表示される。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/35595/e8504c82-431b-fea0-52bc-cc2c9d9fac01.png)

1件ずつ予定が取れていることが確認できる。
注意点として、この `予定A` を取得した `syncToken` で再び予定一覧取得APIを投げると、`予定A`,`予定B`,`予定C`すべての予定が取得される。
これは、`syncToken` は以降の変更されたイベントを取るようなパラメータであるため。


## その他
### 削除予定
削除した予定の判別は、`status: cancelled` になるので、 `status` プロパティから判断すれば良い。

## 参考サイト
[Google Calendar events | Event Objects  |  Apps Script  |  Google Developers](https://developers.google.com/apps-script/guides/triggers/events#google_calendar_events)