Google Cloud Messaging for Chrome を使ってサーバからChrome拡張にメッセージを送る

(2013-07-09時点のものです)

すでに2ヶ月も前のことでアレですが、Chrome拡張等にプッシュ通知を送るGoogle Cloud MessagingがGoogleから発表されました。まりぽさんが早い段階で以下のような記事を書かれています。

[Google Cloud Messaging for Chromeを体験してみた](http://blog.maripo.org/2013/05/gcm-for-chrome/)

Chrome拡張からGAEへChannel IDを送信、送信されたIDへGAEからメッセージを送るデモを書いたので一応メモがてら書いときます。

* https://github.com/dmiyakawa/dmiyakawa-gcm-gae
* https://github.com/dmiyakawa/dmiyakawa-gcm-extension

この記事ではGCMを試した際に気づいたことを書きます。なお、筆者のWeb系の工作技術は低めなので、上のレポジトリでケチを付けられる点については教えていただけると助かります。

上記二つのプロジェクトについては最後に書きます。

# とりあえず拡張作るところから始める

以前もある件でChrome拡張を作ったことはあったのですが、本家のチュートリアルを元にもう一度やるところから始めました。

# Chrome拡張をChrome Web Storeに登録する。

拡張登録自体は一見GCMと関係ありませんが、channel ID を得るために使います。channel ID は各ユーザのChromeにインストールされているChrome拡張を指す宛先です。

[本家](http://developer.chrome.com/apps/cloudMessaging.html)からの引用はこちら:

> Similar to an email address, the channel ID is used to identify and send messages to a specific user of your app or extension. Your app or extension needs to send this value to its application server so that the server can trigger push messages back. 

GCMを実現するGoogle側のサーバに登録するのですが、この時に開発者登録のために一度だけ5ドルかかります。GCM以外のAPIを使う場合にも有効のはず。ここを読んでいる人のほとんどはこのステップは何年か前に終わっているのだとおもいますけどね。

＃ 本家のドキュメントではテストのための channel ID 取得方法として "Get the channel ID at install time using app.runtime.onLaunched." ともあり、上記の登録なしでも実験できるのかと思いましたが、ダメそう。この記述はそもそも Packaged Apps に関する記載に見えます。つまりChrome拡張からはアクセスできないような。

で、作った拡張を zip でアップロードします。もちろんダウンロード出来る人を制限できます。今回はTrustedテスターのみ == 事実上自分だけ、という形でアップロードしました。

https://chrome.google.com/webstore/developer/dashboard


ちょー基本的なことだと思いますが、Chrome拡張を Google Play にアップロードしても、その変更は即座には反映されません。具体的にはこう言われます:

> アイテムは公開処理中です。Chrome ウェブストアに表示されるようになるまで最大 60 分かかることがあります。

実際には表示されるまでに60分も普通はかからないようですが、さすがに1秒で更新、ともなりません。

開発する段階でこのラグをいつも経験するのは微妙に面倒なので、まりぽさんの記事の後半に記載された key の手動設定を行うのが良いです。keyを調べて手でmanifest.jsonに埋め込むわけです。 Linux でgoogle-chromeを使っている場合、~/.config/google-chrome/Default/Extensions/ 下に拡張があるようなので、keyをそこから引っこ抜いて開発中のものにぶちこめば良いです。

# Chrome拡張をGCM対応させる

メッセージを受け取る上でChrome拡張側でしなければならないことは以下の4つです。

* 拡張のパーミッションに "pushMessaging" を追加する
* 拡張の channel ID をGoogleのサーバ経由で準備する
* channel IDをサーバに知らせる
* メッセージを受け取るためのコールバックを実装する

パーミッションについてはmanifest.jsonに書くしかありませんのでそうします。

「channel IDを準備」はChrome Web Storeに登録した拡張であれば、後はしかるべきAPI (chrome.pushMessaging.getChannelId()) を叩けば出てきます。

「channel IDをサーバに伝える」部分は最終的に提供したいサービスの形態に応じて自分でつくる必要があります。popup を開いた時に送るような感じで今回は実装しました。

「メッセージを受け取るためのコールバックを実装する」は、チュートリアルにはないですが background の仕組みを使えば自然に出来ます。

この部分の動作する実装例がなかったので念の為書いてみたのですが、大したことはありませんでした。ただ、Chrome拡張固有のクセのようなものはあるのでそちらの勉強にはなりました。

# サーバ側からメッセージの要求を出せるように Google APIs Console でプロジェクトを登録

Google API Console によって最終的に以下の3つの情報を得ます。これらに有効期限はなく、ずっと使えるらしいです。

* client ID
* client secret
* refresh token

＃ よくわかっていないのが、なんでここで client ID/secret に追加して refresh token が必要なのか、です。OAuthの仕組みがわかっていないから分からないのかもしれません。

この3つのセットから、時間制限のある access token を得ます。この access token と channel ID (宛先)、payload (メッセージ本体) の3つの情報で実際にメッセージを特定の拡張に送ります。

本家からもいちど。

> You need two types of OAuth 2.0 tokens to authorize each call to the push messaging service: the refresh token and the access token. The access token authorizes each call to the service; however, this token expires after about an hour. The refresh token is used to 'refresh' the access token over time. These tokens are scoped to only send messages on behalf of your application or extension and nothing else.

最初の3つについてはあらかじめ Google APIs Console 経由で手動で取得しておき、access token は適宜サーバが取得し直す必要があります。

# サーバをGAEで実装する

curl コマンドで手でメッセージを送るのであれば、サーバはいりません。client ID, client secret, refresh token の3種類の情報から access token を得て、access token と channel ID を使ってメッセージを送ればできます。

Chrome拡張とGAE連携をさせてみたかったので適当にサーバサイドも書きました。

# 2つのプロジェクトについて

Chrome拡張をインストールするとボタンが配置されます。ボタンを押すとサーバにchannel IDが送信されます。サーバでは channel ID を覚えていて、簡易なメッセージを channel ID を送信したブラウザ拡張に送れます。

なお、遊ぶ場合、少なくとも以下の作業をしないとだめです。色々めんどいですね

* Google のアカウントが必要です。
* GAEの app ID 等は別途準備する必要があります
* GAEのコード中でアカウントをメアドで弾く設定がされてます。自分のアドレスに変えてください。
* client ID, client secret, refresh token を用意する必要があります
* Chrome拡張の登録もやる必要があります

セキュリティの配慮をほとんどしてないのでそれも注意です。