2011-06 << 2011-07 >> 2011-08

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の接続は切れてしまうので,切れたら再接続.

2011-06 << 2011-07 >> 2011-08