Googleカレンダーの編集をトリガーにして、スプレッドシートの予定も更新したい時の準備メモ

## はじめに
以前、[スプレッドシートからGoogleカレンダーへイベントを登録・編集する記事](https://qiita.com/cazimayaa/items/6af9dd1fd1228c93cf89) を書きましたが、これは **スプレッドシート -> カレンダー** の単方向だったので、逆方向の **カレンダー -> スプレッドシート** の編集もできるようになりたいと思い調べたところ、スプレッドシート側でカレンダーの編集を検知できるようにするまでの設定が意外と大変（主にgcp側が）だったので、その時の手順をこの場をお借りして整理することにしました。

# 手順概要
gas（Google Apps Script）側とgcp（Google Cloud Platform）側両方で **Calendar APIを有効化する** 必要があり、以下の手順が必要になります。

- gas側でやること
    - Calendar APIを有効化する。
    - スプレッドシートからカレンダーへイベント登録した時、カレンダーの内容とスクリプトプロパティを同期化する関数を作る。
    - カレンダー側で予定を変更したとき、前回同期化した時からの差分を取得する関数をつくる。
    - トリガーを作成する。差分を取得する関数をトリガーに設定。

- gcp側でやること
    - プロジェクト作ってない場合、作る（プロジェクト番号をgas側に設定するのでメモ）
    - Calendar APIを有効化する。
    - アプリケーションの登録をする。
  
## gas側でやること
### 1. Calendar APIを有効化する

スクリプトエディタの `リソース` -> `Googleの拡張サービス` からCalendar APIをONにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181001/9d07cab1-fa21-2a79-d9cf-f5255ec565e3.png)

gcpプロジェクトをまだ作ってない場合は、↑の画面のときに、 [any applicable services and APIs](https://console.cloud.google.com/terms) のリンクから作成できます。既にgcpプロジェクトを作ってる場合は、カレンダーAPIを有効化しておきましょう。
（gcpプロジェクトの作成とAPIの有効化手順を[gcp側でやること](#gcp側でやること) に記載しましたので、まだの人は良かったら参考にしてください :cat: ）

### 2. Cloud Platformプロジェクトを設定

`リソース` -> `Cloud Platform プロジェクト` でgcpの **プロジェクト番号** を設定します。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181001/d1ded8e4-5b91-c10b-3bc0-08e89ad0b5cf.png)


### 3. カレンダーIDをメモ
Googleカレンダーのマイカレンダーから対象のカレンダーの `設定と共有` を開くと **カレンダーID** が見れますので、メモしておきます。
デフォルトカレンダーの場合は、自分のgmailアドレスがカレンダーIDになります。


### 4. カレンダーの内容とスクリプトプロパティの内容を同期化する関数を作る
**スプレッドシートからカレンダーにイベントを登録した時** に、以下の関数を実行するようにしておきます。スクリプトプロパティの `syncToken` っていうプロパティにカレンダーのイベント内容を保存すると、スクリプト実行終了後も内容を保持してくれます。

```javascript
function initialSync() {
  var items = Calendar.Events.list("メモしたカレンダーID");
  var nextSyncToken = items.nextSyncToken;
  var properties = PropertiesService.getScriptProperties();
  properties.setProperty("syncToken", nextSyncToken);
}
```
### 5. トリガーに設定する関数を作る
**Googleカレンダー側でイベントを編集したとき**に実行する関数です。
手順4.の `initialSync()` を実行した時保存した`syncToken` プロパティとの差分をとってきて、ログ表示します。その後プロパティの内容を上書きしています。 
今回は差分をログ表示するだけですが、この部分をスプレッドシートへ反映する処理に書き換える予定です。

```javascript
function onCalendarEdit() {
  var properties = PropertiesService.getScriptProperties();
  var nextSyncToken = properties.getProperty("syncToken");
  var optionalArgs = {
    syncToken: nextSyncToken
  };
  var events = Calendar.Events.list("メモしたカレンダーID", optionalArgs);
  Logger.log(events); // 差分をログ表示
  var nextSyncToken = events["nextSyncToken"];
  properties.setProperty("syncToken", nextSyncToken);
}
```

### 6. トリガーを作成
`編集` -> `現在のプロジェクトのトリガー` と選択し、遷移先画面右下の `トリガーを追加` で作成します。
実行する関数は、さきほど作ったトリガーに設定する関数を選択します。
カレンダーのオーナーのメールアドレスには、カレンダーIDを入力します。初めて保存すると承認画面が表示されるので承認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181001/168d926b-3abe-182e-f8b4-7d4641678d48.png)

## カレンダーを編集してみる

スプレッドシートはこんなテンプレートです。カレンダーへイベント登録済みである前提
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181001/21fc03b4-68ad-fd8b-a3ec-5f3f36335663.png)


カレンダーからイベントを編集した時のログ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181001/a7d5dc06-8f0f-84d8-1769-5cacfbed7aab.png)

ログの内容から `items` の中にイベント内容が入っていることがわかります。
さきほどの `onCalendarEdit()` のログ表示の部分をこんな感じに書き換えるとイベントの内容を取得できます。

```javascript

 var events = Calendar.Events.list(calendarId, optionalArgs);
 var items = events.items;

 for (var i = 0; i < items.length; i++) {
    // イベントID
    var eventId = items[i].iCalUID;
    // イベントのタイトル
    var title = items[i].summary;
    // 場所
    var location = items[i].location;
    // 開始日
    var startDate = Utilities.formatDate(new Date(items[i].start.dateTime), "Asia/Tokyo", "yyyy/MM/dd");
    // 開始時間
    var startTime = Utilities.formatDate(new Date(items[i].start.dateTime), "Asia/Tokyo", "HH:mm");
    // 終了時間
    var endTime = Utilities.formatDate(new Date(items[i].end.dateTime), "Asia/Tokyo", "HH:mm");
    // ステータス
    var status = items[i].status;

    // TODO ここにスプレッドシートの内容を更新する処理を書こう
  }
```
カレンダーで以下の操作を試して **status** の中身を確認してみました。

 - 新規登録、編集… `confirmed`
 - 削除… `cancelled`
 - トリガーに設定したのと別のカレンダーへ移動… `cancelled`
 - 別のカレンダーからトリガーに設定したカレンダーへ移動… `confirmed`

自分のデフォルトカレンダー以外に、例えば仕事、プライベート用とか別のカレンダーを作っていて変更した場合は、いったん削除されて、別のカレンダーへ新規登録ってなるみたいです。

色々な処理に対応しようとすると上記のコードを色々工夫する必要がでてきますので、今日はタイトル通り、Googleカレンダーの編集をトリガーにして、スプレッドシートの予定も更新したい時の準備のみで終わりたいと思います。:cat: 

今回記事を書くときテストしたコードを[こちら](https://github.com/cazimayaa/repos05/tree/master/calendar-edit-test)に置きました。

（↓以降↓はgcpプロジェクトを作る手順です。）

## gcp側でやること
Google Cloud Platform でまだプロジェクトを作ってない場合、プロジェクトを作ってそちらでもAPIを有効化する必要があります。

### 1.Google Cloud Platformでプロジェクトを作成
https://console.cloud.google.com/terms からサービス規約の画面へ遷移します。`プロジェクトを選択` で表示された画面右上の `新しいプロジェクト` を選択してプロジェクト作ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181001/814f6833-5748-2d78-92e9-3e04a822a4fd.png)

### 2. Calendar APIを有効化
 プロジェクトを作成すると、作成したプロジェクトのダッシュボード画面へ遷移します。 左上のハンバーガメニューから `APIとサービス` -> `ライブラリ` と移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181001/84d96b31-b5f8-ae9f-7677-96e9993f6c4c.png)

遷移先の画面でCalendar APIを検索して、有効化します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181001/17648719-0860-f6f8-c498-f7a777fe876c.png)

### 3. アプリケーションの登録
ハンバーガーメニューの`APIとサービス` -> `OAuth同意画面` で以下の画面が表示されますので作成します。今回はプライベートのgmailアカウントでテストしたので、外部を選択しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181001/70cc6e95-714b-abfd-15cb-3208e11fe93c.png)


アプリケーション名を入力し、スコープを追加して保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181001/cfda5c62-a0a1-1c3f-14b4-1f49bca47563.png)

### 4. プロジェクト番号をメモ
Cloud Platformの画面右上から `プロジェクトの設定` を開き、**プロジェクト番号** をメモします。
このプロジェクト番号をgas側に設定すればOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/181001/ccf533fd-fd12-2dda-2de1-afc345c77e36.png)


## 参考にしたサイト
- [【GAS】Googleカレンダー編集時に発動するトリガーを使いこなす](https://google-apps-script.net/instructions/?p=645)
gas側の解説をとても詳しく書いてくださってます。

- 日付のフォーマット [Utilities.formatDate(date, timeZone, format)](https://developers.google.com/apps-script/reference/utilities/utilities?hl=ja#formatdatedate,-timezone,-format)