TwitterizerでFilterStream

.NETでTwitterAPIを使うライブラリの一つにTwitterizerというものがあります。開発も継続されていていい感じなライブラリです。
そしてこのプロジェクトの片隅でTwitterizer.StreamingとしてStreamingAPIへの対応も進んでいるわけですが、まだ実用には至っていない様子。2.3のパッケージに含まれているものはAPIのURLが古いし、trunkのコードもいまいち動かないです。
OAuth部分だけ拝借して自分で書いてもいいのですが、せっかくなのでtrunkのコードを手直ししてみます。
「さしあたってFilterStreamが使いたい」
(要するに高専カンファの会場で#kosenconfのツイートを流し読みしたいのにクライアントなかったからその場で作ったお話)

やってみる

とりあえず最新のコード(この時点ではr435)を拝借して動かしてみます。

var token = new OAuthTokens
{
    ConsumerKey = "**",
    ConsumerSecret = "**",
    AccessToken = "**",
    AccessTokenSecret = "**"
};
var opts = new FilterStreamOptions();
opts.Track.Add("#kosenconf");
using(var stream = new TwitterStream(token, "StreamViewer"))
{
    bool disconnected = false;
    stream.OnStatusReceived += (stat) =>
    {
        Console.WriteLine(stat.Text);
    }
    stream.OnStreamingStopped += delegate
    {
        disconnected = true;
    }
    stream.StartFilterStream(opts);
    
    while(!disconnected) ;
}

動きません。デバッグ実行してみるとHTTP406が返ってきている模様。
すかさずドキュメントを読みます。

406
Not Acceptable. One or more of the parameters are not suitable for the resource.

http://dev.twitter.com/pages/streaming_api_response_codes

あれ、認証エラーとかじゃなくてパラメータエラー?
  
このコードではパラメータをURLに付加してAPIを呼び出しているのですが、そのURLに問題がありました。HttpWebRequestに渡されていたURLがこちら。

http://stream.twitter.com/1/statuses/filter.json?track=#kosenconf

・・・#がURLエンコードされてない。
試しにFilterStreamOptionsに指定するときに「%23kosenconf」とエンコードしてみる。

http://stream.twitter.com/1/statuses/filter.json?track=%2523kosenconf

%はエンコードするのか、なんていい子なんでしょう(#^ω^)ピキピキ

直す

HttpWebRequestの生成はTwitterizer.WebRequestBuilderでOAuth絡みごとラップされています。言うまでもなくTwitterizer.dllに含まれているのでこれを変更するのは無理。困った。
と、WebRequestBuilderにParametersなるプロパティを発見。もしやと思い、URLに付加していたパラメータをここに設定させてみる。

public void StartFilterStream(FilterStreamOptions options)
{
    //略
    
    var opts = new Dictionary<string, string>();
    
    //略
    
    if (options.Track != null && options.Track.Count > 0)
    {
        opts["track"] = string.Join(",", options.Track.ToArray());
    }
    
    //略
    
    StartStream("http://stream.twitter.com/1/statuses/filter.json", opts);
}

private void StartStream(string streamUri, Dictionary<string, string> opts)
{
    WebRequestBuilder builder = new WebRequestBuilder(new Uri(streamUri), HTTPVerb.GET, this.Tokens);
    
    foreach(var opt in opts)
        builder.Parameters[opt.Key] = opt.Value;
    
    HttpWebRequest request;
    request = builder.PrepareRequest();
    
    //略
}

あ、できた。#もエンコードされてますね。
これで無事にTwitter陣の様子が眺められました。コーディングに夢中で基調講演をろくに聴けなかったとかそんなこと(ry
  
さて、Parametersに設定しておくとHTTPVerbに応じて*1、OAuthにも配慮して、上手いことリクエストを作ってくれるようです。
というか・・・それがあるのになぜ自力でやったし。

*1:といいつつ、現時点ではGET限定のようです。将来的にはPOSTにも対応するようなことがコメントにあった気がする。