読者です 読者をやめる 読者になる 読者になる

すがブロ

sugamasaoのhatenablogだよ

ChirpUserStreams っていう Twitter API を使ってみた

なんだか自分の TL のイベント等がもりもり取れるらしい

Twitterの新しいStreaming API「ChirpUserStreams」がすごすぎる件 - すぎゃーんメモ が面白そうだったので、自分でも作ってみた。
自動返信とかはしてないのだけど、とりあえず自分のTLの状態をひたすらウォッチするというモノ。

require 'net/http'
require 'uri'
require 'pp'
require 'kconv'
require 'rubygems'
require 'pit'
require 'json'

#--------------------------
# 取得した Streamデータを文字列として出力する。
# Parser クラスには parse メソッドが実装されてればおk
#--------------------------
class Parser
  def parse(status)
    if(status["friends"])
      friends status
    elsif(status["user"])
      user status
    elsif(status["delete"])
      delete status
    elsif(status["event"] == "follow")
      follow status
    #elsif(status["target_object"])
      elsif(status["event"] == "retweet")
        retweet status
      elsif(status["event"] == "favorite")
        favorite status
      elsif(status["event"] == "unfavorite")
        unfavorite status
      #end
    else
      puts "unknown data."
      pp status
    end
  end

  :private
  def user(status)
    output "[Tweet]#{status['user']['screen_name']} : #{status['text']}"
  end

  def friends(status)
    output "[friends] friends(follow count) => #{status['friends'].size}"
  end

  def delete(status)
    output "[delete] userid:#{status['delete']['status']['user_id']} tweet_id:#{status['delete']['status']['id']}"
  end

  def follow(status)
    output "[follow] #{status['source']['screen_name']} => #{status['target']['screen_name']}"
  end

  def favorite(status)
    output "[fav] #{status['source']['screen_name']} => #{status['target']['screen_name']} / #{status['target_object']['user']['screen_name']} : #{status['target_object']['text']}"
  end

  def unfavorite(status)
    output "[unfav] #{status['source']['screen_name']} => #{status['target']['screen_name']} / #{status['target_object']['user']['screen_name']} : #{status['target_object']['text']}"
  end

  def retweet(status)
    output "[RT] #{status['source']['screen_name']} => #{status['target']['screen_name']} / #{status['target_object']['user']['screen_name']} : #{status['target_object']['text']}"
  end

  def output(str)
    puts str.toutf8
  end
end

#--------------------------
# TwitterStream 取得クラス
#--------------------------
class TwitterStream
  def initialize(user, pass)
    @user = user
    @pass = pass
  end

  def parser=(parser)
    @parser = parser.new
  end

  def parse(status)
    @parser ||= Parser.new
    @parser.parse(status)
  end

  def twitter_stream(uri)
    Net::HTTP.start(uri.host, uri.port) do |http|
      request = Net::HTTP::Get.new(uri.path)
      request.basic_auth(@user, @pass)
      http.request(request) do |res|
        raise Exception, (res.code + " " + res.message) if res.code !~ /2\d\d/
        data = ""
        res.read_body do |chunk|
        begin
          status = JSON.parse(chunk)
        rescue
          data << chunk
          status = JSON.parse(data) rescue next
          data = ""
        end
          yield status
        end
      end
    end
  end
end

#--------------------------------------
# Twitter Strean 取得開始
#--------------------------------------
URL = 'http://chirpstream.twitter.com/2b/user.json'
ts = TwitterStream.new(Pit.get("twitter")["username"], Pit.get("twitter")["password"])
# 出力を変更したかったら、以下のようにクラスを直接ぶっこんで。
#ts.parser = Parser
ts.twitter_stream(URI.parse(URL)) do |status|
  ts.parse(status)
end

解説するまでも無いですけど

TwitterStream のクラスがキモで、APIを叩いて chunk で来ているストリームをちまちま読んで、JSONでパースできたら1ターン終了、みたいな流れ。

参考文献

一番最後のサイトが戻り値の形式であるとか、そもそも仕様自体の是非等に言及されててわかりやすいです。

ちなみに

これを drb で実行して yield で別プロセスからとれれば WebAPPにできるなぁ*1、とか思ったのですが、イマイチ drb で yield のメソッドを実行できなくて挫折中。。。
っていうか、yieldを初めて実用的に使った気がする!

*1:このプロセスはAPI叩く用にして、アクセスがあったら取得してるプロセスにアクセス擦る感じで