2011-07-17 (日)
ボットを書く日.
眠い.新宿でしゃぶしゃぶ食べた.kstmの現役の後輩に会った.
明日は休日らしいことを知った.
*[google] Google Channel APIに接続する(メモ)
Channel APIは,Googleのサーバに接続したクライアント間でリアルタイムにメッセージをやり取りするためのAPIです.
色々見てると,Google TalkやGoogle+とかのGoogleのサービスでも同じようなものが普通に使われているっぽい.自分で使いたい場合は,Google App Engine上で使うためのAPIが用意されているので,大量のクライアント同士でリアルタイムな通信が実現できます.自前でサーバ用意すると,コネクション数が多くなった時に大変なのですが,勝手にスケールしてくれるのが素晴らしいですね.
APIの使い方は,App Engineのドキュメント見れば分かります.JavaやGoから使えるようですね.
ここからが本題.
Webアプリケーションの作り方はわかったのですが,やっぱりクライアント自作して,ボット作りたいですよね.定期的にURLにアクセスしてポーリングとか前時代的なやり方から脱却しましょう.
APIの使い方は説明見れば分かるのですが,実際のプロトコルが分かりません….
まあ,実際に使われてるJavaScriptを読めばクライアント実装方法は分かるはずです.……Googleのサービスで使われているjsファイル読んだことある人なら,JavaScript読むのは真っ先に避けると思いますが.
とりあえず,GAEの某アプリケーションのChannelAPIに接続してみる.
トークン
まず,APIにアクセスするためのトークンが必要です.自分で作ったサービスなら,適当にtoken取得できるAPI用意しておけばよいです.
2時間で無効になるので定期的に取らないとダメみたいです.
Googleのサービスではtoken無しでチャンネルにアクセスしてたりするので,クライアントよりもGAEのアプリケーションを認証するのが目的かも?.
clid,gsessionidの取得
URL https://talkgadget.google.com/talkgadget/d
実際には,[0~999].talkgadget.google.com のホストが使われてます.
パラメータ
- token トークン
- xpc 接続の情報をjsonで渡す(詳細は不明…) cn,ppu,lpu cnはランダムな10文字
xpc={cn:ランダムな10文字,tp:null,ppu:appspot側のxpc_blankのURL,lpu:talkgadget側のxpc_blankのURL}
xpcは無くても接続できるようです(?).あとで適当なURL指定して挙動を調べたい.
レスポンスはJavaScriptですが,WcsDataClientのインスタンスを作っているところを見ると,
WcsDataClient(url,???,clid,gsessionid,???,???,token)
という引数になってるのが見えるのでこれを抜き出します.
test
bindする前に,接続のテストが必要みたいです.
URL: https://talkgadget.google.com/talkgadget/dch/test
パラメータ(共通):
- VER=8 ChannelAPIのバージョン?
- clid=XXXXXXXXXXX
- gsessionid=XXXXXXXXXXX
- prop=data プロパティ名
- token= トークン
- zx=xxxxxxx ランダム?
- t=1
1回目
- MODE=init
"[]" 空のjson配列が帰ってくる.
2回目
- TYPE=xmlhttp
"111112"みたいなレスポンスがきますが,最後の"2"を受信する前にコネクションを切る必要がある.
bind
メッセージを取得します.何かあったときにデータがくるので,別スレッドで受信しつつ,もう一つのスレッドでリクエストを送る感じに.
URL: https://talkgadget.google.com/talkgadget/dch/bind
パラメータ(常に送る):
- VER=8 ChannelAPIのバージョン?
- clid=XXXXXXXXXXX
- gsessionid=XXXXXXXXXXX
- prop='data' プロパティ名
- token トークン
- (追加分はこの間に入る…たんぶん順序は関係ない)
- zx=xxxxxxx ランダムな文字列
- t=1 不明
パラメータ(場合によって送る):
- RID リクエストID もしくは 'rpc'
- SID セッションID? 最初のbindで取得する
- AID 最後に受信したメッセージの番号
- CVER=1 何かのバージョン
POSTする場合
- count reqの数
- ofs=0
- req0_m 内容
- req0_c clid
- req0__sc=c clientのc?
レスポンス
size\n json\n size\n json\n . . .
1レスポンスに複数のメッセージ含まれます.
sizeはjsonのバイト数.jsonの最初の要素はメッセージの番号.json文字列は微妙にinvalidなjsonなので加工する必要があるかも.
データの送受信
まず,bindのURLに,RIDとCVERを指定してリクエストします.特にコマンドは送らず,"count=0"をPOST.
[[0,["c","XXXXXXSIDXXXXXXX",,8]],[1,["b"]]]
こんな感じのデータになってます.0番のメッセージにSIDが入っています.今はSID以外は捨ててOK.
次に,SID,RID,CI,AID,TYPEを指定してGET.よく分かりませんが,RID=rpc,CI=0,AID=1,TYPE=xmlhttpです.AIDはさっき受信した最後のメッセージのIDが1なのでそれを.TYPEは無くても良いみたいですが,一応.
このまま待っていれば,定期的にnoopというデータが届きます.一つの1データを受信しても,レスポンスが終わるわけではないので,受信が完了しなくても,順次データを取り出していきます.
ただ接続しただけでは,noopしか送られてこないようなので,["connect-add-client"]を送ってみると,色々流れ始めます.
RIDの値をインクリメントして,以下のようなデータをPOSTします.
'count'=>1, 'ofs'=>0, 'req0_m'=>'["connect-add-client"]', 'req0_c'=>clid, 'req0__sc'=>'c',
チャンネル制御 connect-add-client以外は分からず.
- connect-add-client チャンネルにクライアントを参加させる
メッセージ
[[0,["c","XXXXXSIDXXXXXXX",,8]],[1,["b"]]] SID受け取る
[[1,2,17]] connect-add-clientしたときに帰ってくる.意味は不明
[[15,["noop"]]] データ無し.15はメッセージの番号
[[16,["c"["XXXXclidXXXXX",["ae","hoge"]]]]] アプリケーションからのデータ ae=app engine??
4分弱でHTTPの接続は切れてしまうので,切れたら再接続.