Twitter @Anywhere を斜め上に掘り下げる

Twitter @AnywhereというDeveloper向けの新サービスが出てた。このサービス、この自分的にはいろいろな理由によりちゃんとチェックしておく必要がある。ので久々にブログ書く。

Facebook Connect (JavaScript) を知ってる人は、それがTwitterでもできるようになった、と思ったらいい。つまりサーバレスでTwitterに接続してAPIJavaScriptから利用できるようになった。今でもJSONPである程度APIの利用はできたが、JSONPとは違い認証が必要な操作、例えば書き込み系の操作もOK(ただしversion 1のAPIでは許されている操作はそれほどない)。

詳しくはこちらの記事とかをどうぞ。

Twitter 新 API のドキュメント「Getting Started with @Anywhere」日本語訳 - WebOS Goodies


さて、上記の記事などでは触れられてないけど、プレビュー版のAPIもあるようで、これだとfollow以外にもいろいろなリクエストが使えるようだ。

http://platform.twitter.com/js-api.html
http://ongmap.com/blog/?p=519

Twitter @AnywhereのJavaScriptのインクルードに渡すパラメータで"v=1"となってるところを"v=chirp_preview"とするだけでよい。ただ、すべてのAPIが使えるかというとそうではなく、結構バグが有るみたい。ということでもうすこししたらちゃんとしたのが出るだろうからみんな待ちましょうね。おしまい。


でも待てないので、中身を見てみる。

通信の方法は比較的シンプル*1で、Twitter Anywhereのスクリプトが読み込まれると自動的にそのページの中に隠しIFRAMEが作成され、その中にメッセージのレシーバとなるHTMLファイルが読み込まれている*2。これは https://api.twitter.com/xd_receiver.html というURLで提供されており、このHTMLからならAPIのエンドポイントは同一オリジンなのでXMLHttpRequestで呼び出しができる、という仕掛け。

あとは、Twitter @Anywhereを呼び出した親フレームの間とでデータ通信を行えばよいわけだが、最近のブラウザはすでにHTML5 Cross Document Messagingを実装しているので、これは実は簡単。実際にTwitter Anywhereでもwindow.postMessageを使っているのはイベントリスナをつければすぐわかる。具体的にはFirebugで以下を実行する

cd(frames[0].frames[0]); // https://api.twitter.com/xd_receiver.html
window.addEventListener('message', function(message){ console.log(message.data) }, true);
cd(window.parent);
window.addEventListener('message', function(message){ console.log(message.data) }, true);

Dumpして得られたメッセージ内容

{"uuid":7,"method":"statuses/home_timeline","args":[{}],"token":"012345678-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN"}
{"uuid":7,"data":[{"in_reply_to_status_id":null,"created_at":"Sat Apr 17 11:12:35 +0000 2010", 
...
]}

つまり、JavaScriptAPIを使わずとも、この形式のメッセージをIFRAMEのターゲットにpostMessageで送信&帰ってくるイベントをListenすればよい。uuidというリクエストのつながりを管理するIDがついているので、これを見て自分のリクエストのレスポンスかどうかがわかる。リクエストの中にあるtokenは、Twitter Anywhereにコネクトした後、Cookieの中にtwttr_anywhereという名前で保存されているものが使われているようだ。


実際に作ってみた例がこれ。DMを送りつける。あやしい。

http://stomita.sakura.ne.jp/lab/anywhere/


なお、postMessageを実装してないブラウザには、さまざまなFallback方法があって異なるドメイン間でのフレーム間通信を実現できるが、ここではめんどくさいので省略。

実際に送られるAPIのHTTPリクエストの内容は、Firebugにでてくるのでよくわかる。

Host	api.twitter.com
User-Agent	Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; ja-JP-mac; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 Pathtraq/0.9
Accept	application/json, text/javascript, */*
Accept-Language	ja,en-us;q=0.7,en;q=0.3
Accept-Encoding	gzip,deflate
Accept-Charset	Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive	115
Connection	keep-alive
X-Requested-With	XMLHttpRequest
Authorization	OAuth oauth_access_token="012345678-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN"
....

AuthorizationヘッダにOAuthのトークンがくっついているが、OAuth WRAPに近い形で署名は省かれている。



ちなみに表題の「斜め上に掘り下げる」って、どうもおかしな日本語だが、本トピックは通常の反応としては斜め上方向だとおもうし、実際にIFRAMEの構造を下方向に掘り下げて解析したので実はえーじさんはおかしくはない

*1:主観

*2:実際にFirebugでDOMをみればわかるけど、正確にはもう1段別のIFRAMEが入っている