module Tweetsy class Client API_HOST = "http://twitter.com" def initialize(login, password, options = {}) @config = options.merge(:auth => [login, password].join(':')) end def authenticate_as(login, password) @config[:auth] = [login, password].join(':') end def authenticated? begin result = Curl::Easy.perform("#{API_HOST}/account/verify_credentials.xml") do |curl| default_curl_options(curl) curl.userpwd = @config[:auth] end rescue => e raise CantConnect, "#{e.message}" end case result.response_code when 200 return true when 401 return false else raise CantConnect, "Twitter is returning a: #{result.response_code}" end end def verify_credentials begin doc = request(:get, '/account/verify_credentials.xml', :auth => true) User.new(doc.at('user')) rescue Tweetsy::Unauthorized return false end end def create_friendship(id_or_screen_name) doc = request(:post, "/friendships/create/#{id_or_screen_name}.xml", :auth => true) User.new(doc.at('user')) end def destroy_friendship(id_or_screen_name) doc = request(:post, "/friendships/destroy/#{id_or_screen_name}.xml", :auth => true) User.new(doc.at('user')) end def update(status, options = {}) form_data = { :status => status } form_data[:source] = options[:source] if options[:source] form_data[:in_reply_to_status_id] = options[:in_reply_to_status_id] if options[:in_reply_to_status_id] request(:post, "/statuses/update.xml", :auth => true, :form_data => form_data) # Update returns a xml response but we don't need/care about that yet # Can assume if no exceptions it was successful true end def direct_message(recipient, status) form_data = { :user => recipient, :text => status } request(:post, "/direct_messages/new.xml", :auth => true, :form_data => form_data) end def friend_ids(id = nil) endpoint = id ? "/friends/ids/#{id}.xml" : "/friends/ids.xml" doc = request(:get, endpoint, :auth => id ? nil : true) # apparently don't need :auth (doc/:ids/:id).inject([]) { |friends_ids, id| friends_ids << id.inner_text.to_i } end def follower_ids(id = nil) endpoint = id ? "/followers/ids/#{id}.xml" : "/friends/ids.xml" doc = request(:get, endpoint, :auth => id ? nil : true) # apparently don't need :auth (doc/:ids/:id).inject([]) { |follower_ids, id| follower_ids << id.inner_text.to_i } end def user(id_or_screen_name) doc = request(:get, "/users/show/#{id_or_screen_name}.xml", :auth => true) User.new(doc.at('user')) end def friends_timeline(options = {}) if options[:since] && options[:since].kind_of?(Date) options[:since] = options[:since].strftime('%a, %d-%b-%y %T GMT') end doc = request(:get, build_path("/statuses/friends_timeline.xml", options), :auth => true) (doc/:status).inject([]) do |statuses, status| statuses << Status.new(status) end end # -------------------------------------------------------------- def build_path(path, params = {}) if params.any? path += "?" + params.inject('') { |str, h| str += "#{CGI.escape(h[0].to_s)}=#{CGI.escape(h[1].to_s)}&" } else path end end def request(method, endpoint, options = {}) raise ArgumentError, "#{method} is not a valid request method" unless [:get, :post].include?(method) begin result = case method when :get Curl::Easy.perform("#{API_HOST}/#{endpoint}") do |curl| default_curl_options(curl) curl.userpwd = @config[:auth] if options[:auth] end when :post options[:form_data] ||= {} post_fields = options[:form_data].inject([]) { |pfs, fd| pfs << Curl::PostField.content(fd[0].to_s, fd[1]) } Curl::Easy.http_post("#{API_HOST}/#{endpoint}", *post_fields) do |curl| default_curl_options(curl) curl.userpwd = @config[:auth] if options[:auth] end end rescue => e raise CantConnect, "#{e.message}" end if [200, 304].include?(result.response_code) Hpricot(result.body_str) elsif result.response_code == 401 raise Unauthorized elsif result.response_code == 503 raise CantConnect, "Service unavailable" else raise CantConnect, "#{result.response_code}" end end def default_curl_options(curl) curl.timeout = @config[:timeout] || 10 end end end