一昨年の記事、テレビにTwitterの関連TLをオーバーレイ表示するAIRアプリを作ってみた - Tosshi Note で紹介したアプリを Twitter Streaming API に対応してみました。
f:id:tilfin:20110827190525j:image
AIRアプリ自体の公開は反応をみることにして、Twitter Streaming API を ActionScript で実装する例があまり見つからなかったので、とりあえず載せときます。
ちなみに URLStream を使って実装していますが、URLStream の Progress イベントは HTTP chunk レスポンスの受信とは別に起こります。そのため、流れの速いTLでないと TweetReceived イベントにタイムラグが発生するかもしれません。

URLStreamから取得できるデータ本体は、[ツイートJSON][CRLF][ツイートJSON][CRLF][ツイートJSON][CRLF] となります。
ツイートがないときに、Keep-Aliveのためのパケットも[CRLF]となるため、バッファに文字列をためつつ、[CRLF]を探しては切り出して、文字列長0でなければJSONをパースしてツイート受信イベント発生という流れです。

以下、ソースをべた貼りしておきます。

TwitterStreamLoader.as

コンストラクタ引数にBASIC認証のTwitterスクリーン名とパスワードを指定します。

package com.tilfin.tvtwlayer.twitter
{
import com.adobe.serialization.json.JSON;
import flash.events.EventDispatcher;
import flash.events.HTTPStatusEvent;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.net.URLRequest;
import flash.net.URLRequestHeader;
import flash.net.URLRequestMethod;
import flash.net.URLStream;
import flash.net.URLVariables;
import mx.utils.Base64Encoder;
/
* Tweet Received イベント
*/
[Event(name=”tweetReceived”, type=”com.tilfin.tvtwlayer.twitter.TwitterStreamEvent”)]
/

* Twitter Stream API Loader
/
public class TwitterStreamLoader extends EventDispatcher
{
private static const ROOT_URL:String = “http://stream.twitter.com/1/statuses/";
private var _authHeader:URLRequestHeader;
private var _stream:URLStream;
private var _streamBuffer:String = “”;
/**
\
コンストラクタ
*
* @param screenName スクリーン名
* @param password パスワード
/
public function TwitterStreamLoader(screenName:String , password:String) {
super();
var encoder:Base64Encoder = new Base64Encoder();
encoder.encode(screenName + “:” + password);
_authHeader = new URLRequestHeader(“Authorization”, “Basic “ + encoder.flush());
}
/**
\
各種フィルタをかけたストリームの受信を開始します。
*
* @param params クエリパラメータ
/
public function loadFilterStream(params:Object):void {
var variables:URLVariables = new URLVariables();
for (var key:String in params) {
variables[key] = params[key];
}
var request:URLRequest = new URLRequest(ROOT_URL + “filter.json”);
request.method = URLRequestMethod.POST;
request.requestHeaders = [ _authHeader ];
request.data = variables;
_stream = new URLStream();
_stream.addEventListener(IOErrorEvent.IO_ERROR, onIoError);
_stream.addEventListener(HTTPStatusEvent.HTTP_RESPONSE_STATUS, onHttpResponseStatus);
_stream.addEventListener(ProgressEvent.PROGRESS, onProgress);
_stream.load(request);
}
/**
\
ストリームを閉じます。
*/
public function close():void {
_stream.removeEventListener(IOErrorEvent.IO_ERROR, onIoError);
_stream.removeEventListener(ProgressEvent.PROGRESS, onProgress);
_stream.close();
_stream = null;
}
private function onProgress(event:ProgressEvent):void {
var buffer:String = _stream.readUTFBytes(_stream.bytesAvailable);
_streamBuffer += buffer;
var twstr:String;
var splitpos:int = _streamBuffer.indexOf(“\r\n”);
while (splitpos > -1) {
twstr = _streamBuffer.substr(0, splitpos);
if (twstr.length > 0) {
receivedTweet(twstr);
}
_streamBuffer = _streamBuffer.substr(splitpos + 2);
splitpos = _streamBuffer.indexOf(“\r\n”);
}
}
private function receivedTweet(str:String):void {
var tweet:Object = JSON.decode(str);
dispatchEvent(new TwitterStreamEvent(TwitterStreamEvent.TWEET_RECEIVED, tweet));
}
private function onIoError(event:IOErrorEvent):void {
trace(“IO Error.”);
}
}
}

TwitterStreamEvent.as

package com.tilfin.tvtwlayer.twitter
{
import flash.events.Event;
/
* Twitter Stream イベント
*/
public class TwitterStreamEvent extends Event
{
/

* イベントタイプ : ツイート受信
/
public static const TWEET_RECEIVED:String = “tweetReceived”;
private var _tweet:Object;
/**
\
@return ツイート
/
public function get tweet():Object {
return _tweet;
}
/**
\
コンストラクタ
*
* @param type イベントタイプ
* @param tweet ツイート
*/
public function TwitterStreamEvent(type:String, tweet:Object = null) {
super(type, false, false);
_tweet = tweet;
}
}
}

実行例

Public streams | Twitter Developers の statuses/filter の Parameters をマップかして loadFilterStream メソッドの引数に渡すと、ツイートを受信するたびに JSON を ActionScript Object化したものを受け取れます。

var streamLoader:TwitterStreamLoader = new TwitterStreamLoader(screenname, password);
streamLoader.addEventListener(TwitterStreamEvent.TWEET_RECEIVED, function(event:TwitterStreamEvent):void {
event.tweet; // ツイートの JSON を ActionScript Object化したもの
});
streamLoader.loadFilterStream({ “track”: “#f1jp,#f1” });

trackは「,」区切りで検索語を複数指定できる引数です。