Help us understand the problem. What is going on with this article?

Google Calendar上の予定の変更を監視する

More than 3 years have passed since last update.

Google Calendar API: Push Notifications

特定のCalendar上に予定が追加されたり、予定に変更(削除も含む)があった際に通知を受け取ることができる機能 Push Notification があります。

このAPIを使うと、予定に変更があった際にWebhookにて変更があったことを知ることができますが、Webhookにて通知された内容だけでは「何かの予定に対して変更があった」ことしかわからないので、実用には不十分です。このPush Notification(Events: watch)に加えて、Events: listという予定の一覧を取得するAPIを組み合わせます。Events: list APIにはsyncTokenというパラメータがあり、これが予定の変更を監視する肝となります。1

監視をする仕組み

詳細は長くなるので、ここではざっくりとしたフローを書いておきます。

Channel の作成

まずは特定のカレンダーを指定してEvents: watchを実行してカレンダーに対する通知を行うChannel(監視対象と通知先の組み合わせ) を作成します。

Webhook

Webhookが呼ばれるタイミングが二種類あり、ヘッダの値で区別します。

  • ヘッダ値: X-Goog-Resource-State=sync
    • Channelが作成されたという通知としてWebhookがキックされたので、対象カレンダーに対して一度 Events: listを実行して、レスポンスに含まれるnextSyncTokenと、Channelの期限(ヘッダ: X-Goog-Expiration)、監視対象カレンダーのID(ヘッダ:X-Goog-Resource-ID)をカレンダーのIDをキーにして保存します。
  • ヘッダ値: X-Goog-Resource-State=exsits
    • こちらが本筋の「予定に変更があった」という通知なので、保存されたnextSyncTokenを使ってEvents: listを実行して変更があった予定の一覧を取得し、nextSyncTokenを実行結果のものに書き換えます。2

定期実行処理

またWebhookドリブンなフローとは別に、Channelの期限への対応が必要です。

  • 定期的に、保存したカレンダーの期限をチェックし、期限が近いものはEvents: watchを実行して新しいChannelを作成してから、Channel: stopを使って古い方のChannelを削除します。

準備

この機能を利用するには以下の準備が必要です。

  • Webhookするための https でアクセスできるホスト
    • 自己証明書はダメ
  • Google Developer Console上のプロジェクト

各APIの詳細

Events: watch

予定を監視したいCalendarのidを指定して、Events: watchを実行すると、監視対象と通知先をセットにしたChannelが作成されます。4

パラメータは以下のとおりです。

  • id: リファレンスにはUUIDを指定するよう書かれていますが、実際には特定の記号文字が制限されているだけなので、人間に読みやすいIDでも構いません。5
  • token: 自由な値が設定でき、省略も可能です。何か設定しておくと、Webhookが呼ばれる場合にもヘッダに含まれます。カレンダーのオーナーアカウント等を入れておくと便利かもしれません。
  • type: "web_hook"固定です。他にどんな種類があるのかはわかりません。
  • expiration: 監視が有効な期間(日時)です。6
  • address: Webhookとしてリクエストを投げてもらうURLを設定します。Developer ConsoleのプロジェクトのPush先としてホワイトリストに追加されていないホストを設定した場合は、401 Unauthorized(詳細なメッセージなし)が返されます。

このAPIを実行したレスポンスの書式はリファレンスにて確認できますが、resourceId 以外は意味がありません。このAPIが成功した場合はWebhookとして登録したURLに対してリクエストが投げられますから、そちらで初期処理すればよいです。

Events: list + syncToken

Events: listはカレンダー内の予定を取得するAPIですが、実行するとレスポンスにnextSyncTokenが返されます。その値を次回のAPI実行時のsyncTokenとして指定することで、そのトークンを取得した時よりも後に変更された内容に絞って取得することができます。
変更内容として予定が削除された場合であっても、その予定のidstatus: canceledがレスポンスに含まれるので便利です。1

Channel: stop

作成したChannelを削除するAPIで、通知を受け取る必要がなくなったり、期限の更新のために新しいChannelを作成した後に実行します。

  • id: 削除対象のChannelのid7
  • resourceId: 削除対象のChannelに含まれるresouceId

watch対象について

watchできる監視対象としてEvents: watchを紹介しましたが、Google Calendarはひとつのアカウントに複数のカレンダー(CalendarList)を作成できます。それ自身の変更を監視したり、カレンダーの設定を変更を監視するwatchなども用意されています。

Google App Engineを使用する場合

*.appspot.com なドメインの所有者であることを証明できないため、SSL for Custom Domainを使用することになります。現在であれば、Google Appsへの登録が必要ということになります(2015/06)。→どうやらドメインの所有確認の対象として https://myappid.appspot.com と https をつければ所有確認ができ、appspot.comなドメインもそのまま利用できるようです。

  1. Developer Consoleの Compute - App Engine - Settingsで、Custom Domainとして追加する
  2. admin.google.com の Apps - Additional Google ServicesAdd servicesをクリックし、該当のGAEアプリケーションのIDを追加する
  3. admin.google.com のSecurity - SSL for Custom Domainsで証明書を登録し、URLを登録する(SNIで良い)

  1. 定期起動のポーリングで変更を検知するだけで良い場合は、Events: list + syncToken でも十分です。 

  2. 処理中に何かが失敗した場合は Webhook のレスポンスとして500などを返せば、Google Calendar側がexponential backoffを使ったリトライをしてくれます。 

  3. 認可を求めるアプリケーションのホワイトリストにWebhookとして使用するホストが登録されていなければならない、という制限のため、Events: watch APIは「API Explorer(というアプリケーション)に対する認可」の権限では正しく動作しません。 

  4. 現在、アプリケーションで作成したChannelの一覧を取得することはできません。Channelの一覧を管理する必要がある場合は、watch APIを実行した際のidを保存しておくか、通知が来た時のX-Goog-Channel-IDを参考にするか、意味のある値にするか、といった管理をしなければなりません。 

  5. ただし、有効なChannelで使用しているidと重複すると400 channelIdNotUniqueが返されます。 

  6. 省略した場合は1週間、最大だと24日程度が指定できます。大きすぎる値を設定使用すると400 pushInvalidTtlが返されます。 

  7. 存在しないidを指定した場合は404 notFoundが返されます。 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away