Twitterの特定キーワードを追跡し続けるスクリプト

TwitterのStream APIを使うと、簡単に特定ハッシュタグがリアルタイムに
追跡できるわけですが、その代わり、漢字をStream APIで追跡することはできません。
ハッシュタグが漢字であったり、ハッシュタグ以外も追跡したい場合は、通常の検索機能を使うと、追跡できますが、今度は逆にStreamではないので、動きが緩慢になります。
漢字は追えないとあきらめてしまえばかまいませんが、やはり汎用な物を持っておきたいので作っておきました。
即応性の要求される、2時間程度のコンファレンスの追跡はStream APIが良いわけですが、
複数日にわたるイベントの追跡は検索APIを使ってもかまわないといえます。
このような仕様なので使いどころがないと思っていましたが、ちょうどJavaOneが開催されていて、複数日ずーっとそれを追いたい訳なので、#javaonejp のハッシュタグで追跡し続けています。
記録性の為なのでテキスト出力の方が便利かなと思っています。

@Grab('net.homeip.yusuke:twitter4j:[2.0,)')
import groovy.xml.Namespace

// 発言番号を取得
String.metaClass.getNum = { ->
	if (delegate ==~/.*tag.*search.*/) {
		//println "発言番号取得 " + delegate.replaceAll(/.*:([0-9]+)/, '$1')
		return delegate.replaceAll(/.*:([0-9]+)/, '$1')
	} else {
		return delegate
	}
}
assert "tag:search.twitter.com,2005:16060673193".getNum() == "16060673193"

// 発言IDを取得
String.metaClass.getID = { ->
	if (delegate ==~/.*\(.*\)/) {
		//println "ID取得 " + delegate.replaceAll(/([^ ]+) \(.*/, '$1')
		return delegate.replaceAll(/([^ ]+) \(.*/, '$1')
	} else {
		return delegate
	}
}
assert "nemo_kaz (kazuo nemoto)".getID() == "nemo_kaz"

// 検索単語エンコード
String.metaClass.encodeIt = { ->
	return URLEncoder.encode(delegate,"UTF-8")
}
assert "ツイッター".encodeIt() == "%E3%83%84%E3%82%A4%E3%83%83%E3%82%BF%E3%83%BC"

// main
if (args.length == 0) {println "検索単語を指定してください"; return }
		
Long counter=1
for (;;){
	def feed=null
	def atom2 = ("http://search.twitter.com/search.atom?q="+args[0].encodeIt()).toURL().text
	feed = new XmlSlurper().parseText(atom2)
	feed.entry.collect{it}.reverse().each {
		if (counter < it.id.toString().getNum().toLong()) {
			counter = it.id.toString().getNum().toLong()
			println "■"+it.title +"  "+it.author[0].name.toString().getID() 
			//println it.link[0]
			//println "class="+it.published[0].toString()
		} else {
			Thread.sleep(1000*90)
		}
	}
}

動作イメージ


■スピーカーが来ない.... #javaonejp skrb
■[CodeGeneration] @Lazyをフィールドにつけると、初めてアクセスするときに初期化するようなアクセサを生成してくれる。 #javaonejp nobeans
■[CodeGeneration] @Lazyで展開される実装はdouble-checked-lockingっぽい。スレッドセーフか?というツッコミに、多分Yes、といってたけど、Java5以降はね、という条件が必要だと思った。 #javaonejp nobeans
■[CodeGeneration]後はアノテーションによるAST変換の例をいくつか紹介してから、DbCのGroovy実装であるGContractsの紹介。アノテーションクロージャで事前条件と事後条件を指定すると実行前後でチェックする実装をAST変換で生成。 #javaonejp nobeans
■[CodeGeneration]次は、FindBugsのGroovy版的なCodeNarc。発見できるバグの種類は前者が200以上なのに対して、60いくつと少なめだけど、CodeNarcはAST変換でバグチェックを実現してるんだよ、と。 #javaonejp nobeans
■[CodeGeneration]Groovy標準付属のツールで、ASTツリーをGUI表示できる。その構造の見方の説明と、AST変換をするためのASTTransformationの実装の仕方の説明。 #javaonejp nobeans
■[CodeGeneratin]AST変換といっても文法的には万能じゃなくて、Groovyとして正しい文法でなければいけない。文法さえ正しければ、その意味は自由に書き換えられる。文法NGならSyntaxErrorが発生してAST変換まで到達しない。 #javaonejp nobeans
■そろそろModular&Jigsawが始まりそう。 #javaonejp nobeans
■@skrb ですよねー!だからこそ貴重な機会を大事にしたいです。 #javaonejp kawamnv
■セッションがはじまるまでウトウトしていた。いつのまにか部屋がいっぱいになっていた。このセッションもffull #javaonejp skrb