さわっても熱くない花火

ちょっとした驚きを食べながら生きています

FlashでTwitterのUserStreamsを受信してみる ~完結編~

FlashでTwitterのUserStreamsを受信してみる - さわっても熱くない花火の続き

前振り

なんかUserStreams APIに対応したActionScriptライブラリがあった…
as3twitterlib

git clone http://code.google.com/p/as3twitterlib/

ただ、私の使い方が良くなかったのだろうけど、うまく使えなかったので、自分で書きました。

Streaming APIを受信するサンプルを弄って実装

Streaming APIにBasic認証で繋いで利用するサンプルがありました。それをOAuth化してUserStreams APIを受信したいと思います。
Twitter Streaming APIを使って関連TLをテレビにオーバーレイ表示するAIRアプリを作ってみた - Tosshi Note
ホント参考になりました。感謝感謝!

ソースコード

ほとんど弄って無いので、ソースはっつけて良いのか些か疑問です…問題ならば指摘していただければ幸いです。

TwitterStreamLoader.as

package 
{
	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;


	import org.iotashan.oauth.*;
	import org.iotashan.utils.*;
	import org.httpclient.*;
	import com.hurlant.crypto.Crypto;
	import org.flaircode.oauth.*;
	import org.httpclient.http.*;
	import org.httpclient.events.*;

	import flash.net.*;
	import flash.events.*;
	import com.adobe.net.URI;


	[Event(name = "tweetReceived",type = "TwitterStreamEvent")]


	public class TwitterStreamLoader extends EventDispatcher
	{
		private static const ROOT_URL:String = "https://userstream.twitter.com/2/";

		private var _authHeader:URLRequestHeader;
		private var _stream:URLStream;
		private var _streamBuffer:String = "";

		private var consumerKey = "こんしゅーまーきー";
		private var consumerSecret = "こんしゅーまーしーくれっと";



		public var accessToken:OAuthToken;
		private var consumer:OAuthConsumer;

		//myAccessTokenにはアクセストークンな文字列を指定。
		public function TwitterStreamLoader(myAccessToken)
		{
			super();

			accessToken = OAuthUtil.getTokenFromResponse(myAccessToken);

			consumer = new OAuthConsumer(consumerKey,consumerSecret);

		}

		
		public function loadFilterStream():void
		{
			var oauthReq:OAuthRequest = new OAuthRequest("POST",ROOT_URL + "user.json",{oauth_version:"1.0",replies:"all"},consumer,accessToken);
			var auth:String = oauthReq.buildRequest(new OAuthSignatureMethod_HMAC_SHA1  ,OAuthRequest.RESULT_TYPE_POST);
			auth = auth.split("&").join("\",");
			auth = auth.split("=").join("=\"");
			auth = auth.replace(/,replies=.*/,"");
			var authHeader:URLRequestHeader = new URLRequestHeader("Authorization","OAuth " + auth);

			var variables:URLVariables = new URLVariables();
			variables["replies"]="all";

			var request:URLRequest = new URLRequest(ROOT_URL + "user.json");
			request.requestHeaders = [authHeader];
			request.method = "POST";
			request.data = variables;

			_stream = new URLStream();
			_stream.addEventListener(IOErrorEvent.IO_ERROR, onIoError);
			_stream.addEventListener(ProgressEvent.PROGRESS, onProgress);
			_stream.addEventListener(flash.events.HTTPStatusEvent.HTTP_STATUS,function(e:flash.events.HTTPStatusEvent):void{
			 	trace(e.status);
			 });
			_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
		{
			dispatchEvent(event.clone());

			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(event.text);
			trace("IO Error.");
		}
	}
}


TwitterStreamEvent.as 名前空間以外は参考サイトのソースコードから変更点無し

package
{
  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;
    }
  }
}


使い方
とりあえず、DMだけを表示できるようにしてみました。

var streamLoader:TwitterStreamLoader = new TwitterStreamLoader("oauth_token=544281054-HLasyE6sqXJNHKOeoQD273Cb14WxBFNmiqaUpNU&oauth_token_secret=LHJ4NiGxcUcnZq09icSLMIzPKDE1tQg9TRm79t22E&user_id=544281054&screen_name=kwsnara");
streamLoader.addEventListener(TwitterStreamEvent.TWEET_RECEIVED, function(event:TwitterStreamEvent):void {
      if(event.tweet["direct_message"]!=null)
	  {
		  trace(event.tweet["direct_message"]["text"]);
	  }
    });
streamLoader.loadFilterStream();

余談

UserStreams APIを叩くときは、repliesステータスを指定した方が良さそうです。
ずっと401エラーで悩まされていたのですが、repliesステータスを指定することでなぜかエラーが出なくなりました。

UserStreams APIの戻り値達

Twitter の User Streams で流れてくるやつ — Gist
こんなスバラシイまとめがありました。多謝。