lib/twitter/base.rb in pjdavis-twitter-0.3.9 vs lib/twitter/base.rb in pjdavis-twitter-0.4.2

- old
+ new

@@ -10,12 +10,14 @@ # Twitter.new('email/username', 'password') # # Identi.ca example: # Twitter.new('email/username', 'password', :api_host => 'identi.ca/api') def initialize(email, password, options={}) - @config, @config[:email], @config[:password] = {}, email, password @api_host = options.delete(:api_host) || 'twitter.com' + @config, @config[:email], @config[:password] = options, email, password + @proxy_host = options[:proxy_host] + @proxy_port = options[:proxy_port] end # Returns an array of statuses for a timeline; Defaults to your friends timeline. def timeline(which=:friends, options={}) raise UnknownTimeline unless [:friends, :public, :user].include?(which) @@ -31,19 +33,33 @@ # Returns an array of users who are friends for the id or username passed in def friends_for(id, options={}) friends(options.merge({:id => id})) end + # Returns an array of user ids who are friends for the account or the option id/username passed in + def friend_ids(id_or_screenname = nil) + path = id_or_screenname ? "friends/ids/#{id_or_screenname}.xml" : "friends/ids.xml" + doc = request(path, :auth => true) + (doc/:id).inject([]) {|ids, id| ids << id.innerHTML; ids} + end + # Returns an array of users who are following you def followers(options={}) users(call(:followers, {:args => parse_options(options)})) end def followers_for(id, options={}) followers(options.merge({:id => id})) end + # Returns an array of user ids who are followers for the account or the option id/username passed in + def follower_ids(id_or_screenname = nil) + path = id_or_screenname ? "followers/ids/#{id_or_screenname}.xml" : "followers/ids.xml" + doc = request(path, :auth => true) + (doc/:id).inject([]) {|ids, id| ids << id.innerHTML; ids} + end + # Returns a single status for a given id def status(id) statuses(call("show/#{id}")).first end @@ -157,18 +173,19 @@ # Posts a new update to twitter for auth user. def post(status, options={}) form_data = {'status' => status} form_data.merge!({'source' => options[:source]}) if options[:source] + form_data.merge!({'in_reply_to_status_id' => options[:in_reply_to_status_id]}) if options[:in_reply_to_status_id] Status.new_from_xml(request('statuses/update.xml', :auth => true, :method => :post, :form_data => form_data)) end alias :update :post # Verifies the credentials for the auth user. # raises Twitter::CantConnect on failure. def verify_credentials - request('account/verify_credentials', :auth => true) + request('account/verify_credentials.xml', :auth => true) end private # Converts an hpricot doc to an array of statuses def statuses(doc) @@ -182,32 +199,22 @@ # Calls whatever api method requested that deals with statuses # # ie: call(:public_timeline, :auth => false) def call(method, options={}) - options.reverse_merge!({ :auth => true, :args => {} }) + options = { :auth => true, :args => {} }.merge(options) # Following line needed as lite=false doesn't work in the API: http://tinyurl.com/yo3h5d options[:args].delete(:lite) unless options[:args][:lite] args = options.delete(:args) request(build_path("statuses/#{method.to_s}.xml", args), options) end - # Makes a request to twitter. - def request(path, options={}) - options.reverse_merge!({ - :headers => { "User-Agent" => @config[:email] }, - :method => :get - }) - unless options[:since].blank? - since = options[:since].kind_of?(Date) ? options[:since].strftime('%a, %d-%b-%y %T GMT') : options[:since].to_s - options[:headers]["If-Modified-Since"] = since - end - + def response(path, options={}) uri = URI.parse("http://#{@api_host}") begin - response = Net::HTTP.start(uri.host, 80) do |http| + response = Net::HTTP::Proxy(@proxy_host, @proxy_port).start(uri.host, uri.port) do |http| klass = Net::HTTP.const_get options[:method].to_s.downcase.capitalize req = klass.new("#{uri.path}/#{path}", options[:headers]) req.basic_auth(@config[:email], @config[:password]) if options[:auth] if options[:method].to_s == 'post' && options[:form_data] req.set_form_data(options[:form_data]) @@ -215,27 +222,53 @@ http.request(req) end rescue => error raise CantConnect, error.message end + end + + # Makes a request to twitter. + def request(path, options={}) + options = { + :headers => { "User-Agent" => @config[:email] }, + :method => :get, + }.merge(options) + unless options[:since].nil? + since = options[:since].kind_of?(Date) ? options[:since].strftime('%a, %d-%b-%y %T GMT') : options[:since].to_s + options[:headers]["If-Modified-Since"] = since + end + + handle_response!(response(path, options)) + end + + def handle_response!(response) if %w[200 304].include?(response.code) response = parse(response.body) raise RateExceeded if (response/:hash/:error).text =~ /Rate limit exceeded/ response elsif response.code == '503' raise Unavailable, response.message elsif response.code == '401' raise CantConnect, 'Authentication failed. Check your username and password' + elsif response.code == '403' + error_message = (parse(response.body)/:hash/:error).text + raise CantFindUsers, error_message if error_message =~ /Could not find both specified users/ + raise AlreadyFollowing, error_message if error_message =~ /already on your list/ + raise CantFollowUser, "Response code #{response.code}: #{response.message} #{error_message}" else raise CantConnect, "Twitter is returning a #{response.code}: #{response.message}" end - end + end # Given a path and a hash, build a full path with the hash turned into a query string def build_path(path, options) - path += "?#{options.to_query}" unless options.blank? + unless options.nil? + query = options.inject('') { |str, h| str += "#{CGI.escape(h[0].to_s)}=#{CGI.escape(h[1].to_s)}&"; str } + path += "?#{query}" + end + path end # Tries to get all the options in the correct format before making the request def parse_options(options) @@ -246,6 +279,6 @@ # Converts a string response into an Hpricot xml element. def parse(response) Hpricot.XML(response || '') end end -end \ No newline at end of file +end