MS Exchange/Outlook のカレンダーとGoogle Calendarの同期 その1

Google Calendar Syncのサービスが2014-08-01に停止された。今まで使っていたので、困ってしまい、代替を調べたが認証proxy越えがうまくいかないので、自分で作ることにした。


仕様としては、

  • Exchange側をマスターとして、Google Calendarへ一方方向で反映
  • 前後30日分だけチェックして追加・変更・削除を反映
  • Exchange側から反映したのでないGoogle Calendar上で直接登録したイベントには触らない

ロジックとしては、これで良いはず。

  1. Exchangeの前後30日間のイベントを取得→リストE
  2. Google CalendarからExchange起因の前後30日間のイベントを取得→リストG
  3. リストGにあってリストEにないものを、Google Calendarから削除
  4. リストEにあってリストGにないものを、Google Calendarに追加


Exchangeのイベント取得は前にやったことがあるので、まずはGoogle CalendarAPIの確認。
ググって、下記ページを参考にした。
http://qiita.com/iron-breaker/items/2440c4ab41a482b1b096
http://qiita.com/mechamogera/items/bf2ed20e332dc31d2352

プロジェクト作成

目的のGoogleアカウントでログインしていることを確認した上で、管理コンソールページ https://cloud.google.com/console にアクセス*1

[Create Project]ボタンを押す。

PROJECT NAME と PROJECT ID を適当に入力する。
I have read and agree to all Terms of Service for the Google Cloud Platform products.
にチェックを入れて、[Create]ボタンをクリック。

ここで、PROJECT IDがGoogleシステム全体でユニークでないと、サーバーエラーになる*2

The Project ID specified is not available. Please select another.
These identifiers must be unique.
って、入力時に言ってくれよ!どうせ後で使わないようなので、おすすめに任せるのが吉。

PROJECT NAMEは、システム全体でユニークである必要はない。アカウント毎の管理。

APIの設定

プロジェクトが出来たら、左サイドバーの、[API&auth]の[APIs]をクリック。
Calendar API の Status が[OFF]になっているので、クリック。
Terms of Serviceの確認ダイアログが出るので、チェックして[Accept]をクリック。
1日に200,000リクエストまで使えるらしい。

クライアントIDの取得

サイドバーの[APIs]の下の[Credentials]をクリック。
"Compute Engine and App Engine" 用のClient IDが出来ているが、これは関係ないので、
OAuthの下の[Create new Client ID]をクリック。
APPLICATION TYPEは、"Installed application"を選択し、INSTALLED APPLICATION TYPEは、"Other"を選択。
[Create Client ID]をクリック。
すると、"Client ID for native application"のClient IDが出来ている。
IDをコピペする必要はなくて、[Download JSON]をクリックして、ダウンロードしておく。
ファイル名は、"client_secret.json" とかに縮めておく。
これで前準備は完了。

疎通確認

確認のため、Googleカレンダーに何か登録しておく。

google-api-clientのgemをインストール。

C> gem install google-api-client

イベントリストを取得して表示するだけのサンプルプログラム*3

require "google/api_client"
require "google/api_client/client_secrets"
require "google/api_client/auth/installed_app"
require "google/api_client/auth/file_storage"

ENV["http_proxy"] = "http://user:pass@proxyserver:port" # proxyの設定
client = Google::APIClient.new(application_name: "")

authfile = Google::APIClient::FileStorage.new("authfile.json") #このファイルはなくて良い。作られる
if authfile.authorization
  client.authorization = authfile.authorization
else
  client_secrets = Google::APIClient::ClientSecrets.load("client_secret.json") #ダウンロードしたJSONファイル

  flow = Google::APIClient::InstalledAppFlow.new(
    client_id: client_secrets.client_id,
    client_secret: client_secrets.client_secret,
    scope: ["https://www.googleapis.com/auth/calendar"]
  )
  client.authorization = flow.authorize(authfile)
end

service = client.discovered_api(:calendar, :v3)

now = Time.now
time_min = now - 3600*24*7 #前後一週間
time_max = now + 3600*24*7
params = {calendarId:   "xxxxxxxxxxxxxxxxx@gmail.com", #GOOGLEアカウントのメールアドレス
          orderBy:      "startTime",
          timeMax:      time_max.iso8601,
          timeMin:      time_min.iso8601,
          singleEvents: true}

result = client.execute(api_method: service.events.list,
                        parameters: params)
if result.error?
  puts "#{result.status} #{result.error_message}"
else
  result.data.items.each do |event|
    puts [event.start.date_time||event.start.date,
          event.end.date_time||event.end.date, event.summary].join(",")
  end
end

これを実行すると、ブラウザにOAuthの認証画面が出る。
「このアプリが次の許可をリクエストしています:」
と出るので、[承認]をクリック。

さっき登録したイベントの日時とタイトルが出ればOK。
Googleアカウントがまちがっていると、404 Not Found が出る。


一度OAuthの承認をしておくと、"authfile.json" に情報が保存され、以降はそれが使われる。

*1:左側の Account settings で Language を日本語にすることも出来るが、各種解説ページが英語ベースだったりするので、そのままにしておく

*2:Create Projectボタンが再び出なければ、左サイドバーの [Projects]というリンクをクリックする。あとは、[Create Project]ボタンからやり直し

*3:google-api-client は faraday というライブラリ経由でウェブアクセスしているが、faraday は https 接続でも http_proxy を使うようだ