twitterのfollowingを増やすためのツールつくったー

twitterをはじめて少しはfollowしたけどなんとも誰を追加したらいいかわからない俺のためにfollow候補を見つけ出すツールを作ったよ。
このツールをぶん回すと、自分がfollowしている人のfollowしている人たちをがばっととってきて、多くfollowされている人を順に表示するよ。つまり、友達みんなが友達にしている人なら楽しいに違いないからfollowして僕と握手だ!というのを実現するのです!

使い方
./scriptname.rb USER_NAME PASSWORD [OUTPUT_FILENAME]

ちなみに、twiiterが重いのとかいろいろなことによって、お友達のお友達を集めてくるのに超絶時間がかかります!なにこれ!かかりすぎ!このままだととっても気の長い人しか使えないよ!
あと、自分のfollowingのページをダウンロードしてきてそれを雛形にfollow候補リストを作成するのですが、ローカルにある関係上か、followボタンを押してもfollowされません。名前をくりっこしてその人のページにアクセスしてfollowしてね!ローカルに出来上がったファイルからばしばしとfollowしていきたいところですが、Javascriptとかぜんぜんわからないからなんともできない!教えて偉い人!

require 'rubygems'
require 'hpricot'
require 'open-uri'
require "rexml/document" 
require 'net/http'
Net::HTTP.version_1_2

class TwitFriends < Array
	VERSION = '0.0.2'
	TW_HOME = 'http://twitter.com/'
	TW_FRIENDS = 'http://twitter.com/friends'
	TWAPI_FRIENDS = 'http://twitter.com/statuses/friends.xml'
	
	TIME_INTERVAL = 10
	MAX_PAGE = 1000
	TMP_DIR = './tmp/'

	class TwitterUser
		def initialize(user, html=nil)
			@user = user
			@html = html
			@friends = 
		end
		def size
			@friends.size
		end
		def friends_url
			TW_HOME + @user + '/friends'
		end
		def friends_path
			'/' + @user + '/friends'
		end
		attr_accessor(:user, :html, :friends)
	end
	
	class TwitterUsersList < Hash
		def add(user, friend)
			if self[user.user].nil? then
				self[user.user] = user
			end
			self[user.user].friends << friend
			self
		end
		def to_s
			str = ''
			self.each do |name, user|
				str << name + ': ' + user.friends.join(', ') + "\n"
			end
			str
		end
	end
	
	class WrongPasswordError < StandardError; end
	
	def initialize(user, pass)
		Dir.mkdir(TMP_DIR) if !FileTest.directory?(TMP_DIR)
		crypt_pass = 'Basic ' + [user + ':' + pass].pack('m')
		@tw_auth = {'Authorization' => crypt_pass}
		@template = nil
		@tw_login_name = user
		@tw_login_pass = pass
		open(TW_FRIENDS, @tw_auth){|f| @template = Hpricot(f) }
	end
	
	def get_my_users
		return @friends if @friends
		@friends = 
		xml = nil
		begin
			open(TWAPI_FRIENDS, @tw_auth){|f| xml = REXML::Document.new(f)}
		rescue OpenURI::HTTPError
			print $!, ". wait and retry ...\n"
			sleep(10)
			retry
		end
		xml.elements.each('/users/user') do |ele|
			@friends << TwitterUser.new(ele.elements['screen_name'].text)
		end
		@friends
	end
	
	def tmp_file_name(name, page)
		TMP_DIR + name + '_' + page.to_s + '.html'
	end
	
	def save_html(name, page, body)
		File.open(tmp_file_name(name, page), "w") do |f|
			f.print body
		end
	end
	
	def saved?(user, page)
		if FileTest.exist?(filename = tmp_file_name(user.user, page)) then
			html_body = ''
			File.open(filename) do |f|
				html_body << f.read
			end
			return html_body
		end
		nil
	end
	
	def get_page(http, user, page)
		if tmp = saved?(user, page) then
			return tmp
		end
		http_req = Net::HTTP::Get.new(user.friends_path  + "?page=" + page.to_s)
		http_req['Connection'] = 'Keep-Alive'
		http_req.basic_auth(@tw_login_name, @tw_login_pass)
		sleep(TIME_INTERVAL)
		begin
			res = http.request(http_req)
			http_body = res.body
			case res.code
			when "302" then
				raise WrongPasswordError
			end
		rescue
			case $!
			when WrongPasswordError then
				print $!, ". id or password is wrong.\n"
				exit
			else
				print $!, "\nrestart http...\n"
				http.finish
				sleep(10)
				http.start
				retry
			end
		end
		save_html(user.user, page, http_body)
		http_body
	end
	
	def get_friends
		list = TwitterUsersList.new
		uri = URI.parse(TW_HOME)
		Net::HTTP.start(uri.host) do |http|
			print "getting #{@tw_login_name}'s friends...\n"
			get_my_users.each do |user|
				counter = 0
				while(true) do
					counter += 1
					print ' ' * 78 + "\r"
					print "getting #{user.user}'s page #{counter} ..."
					page = Hpricot(get_page(http, user, counter))
					page.search("tr.vcard").each do |html|
						html = Hpricot.parse(html.inner_html)
						name = html.search("a.uid").inner_html.gsub(/<[^>]*>/, '')
						list.add(TwitterUser.new(name, html), user)
					end
					break if page.search("a[@rel='me next']").empty?
					break if counter > MAX_PAGE
					print "done. \r"
				end
				print "\n"
			end
		end
		self.replace(list.keys.sort{|a,b|
			list[b].friends.size <=> list[a].friends.size
		}.map{|name|
			[name, list[name], list[name].friends.size]
		})
	end
	
	def output_html(filename = 'out.html')
		get_friends
		@template.search("tr.vcard").remove
		str = ''
		self.each do |obj|
			str << ''
			str << obj[1].html.to_html
			str << ''
			str << '' + obj[2].to_s + ': '
			str << obj[1].friends.map{|friend|
				'' + friend.user + ''
			}.join(', ')
			str << ''
		end
		@template.search("table.doing").prepend(str)
		File.open(filename, "w") do |f|
			f.print @template.to_html
		end
	end
end

if $0 == __FILE__ then
	user = ARGV.shift; pass = ARGV.shift; output = ARGV.shift;
	if user.nil? or pass.nil? then
		print "use: ./#{$0} USER PASS [OUTPUT_FILE]\n"
		exit
	end
	TwitFriends.new(user, pass).output_html(output)
end

open-uri使ったりNet::HTTP使ったり「user」だったり「name」だったりして超汚いので俺の適当具合がよく分かる完成度となっております!