gilesbowkett-gilesbowkett-twitter 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/History +126 -0
  2. data/License +19 -0
  3. data/Manifest +69 -0
  4. data/README +84 -0
  5. data/Rakefile +42 -0
  6. data/bin/twitter +14 -0
  7. data/examples/blocks.rb +15 -0
  8. data/examples/direct_messages.rb +29 -0
  9. data/examples/favorites.rb +20 -0
  10. data/examples/friends_followers.rb +25 -0
  11. data/examples/friendships.rb +13 -0
  12. data/examples/identica_timeline.rb +7 -0
  13. data/examples/location.rb +8 -0
  14. data/examples/posting.rb +9 -0
  15. data/examples/replies.rb +27 -0
  16. data/examples/search.rb +18 -0
  17. data/examples/sent_messages.rb +27 -0
  18. data/examples/timeline.rb +34 -0
  19. data/examples/twitter.rb +27 -0
  20. data/examples/verify_credentials.rb +13 -0
  21. data/gilesbowkett-twitter.gemspec +45 -0
  22. data/lib/twitter.rb +32 -0
  23. data/lib/twitter/base.rb +284 -0
  24. data/lib/twitter/cli.rb +334 -0
  25. data/lib/twitter/cli/config.rb +9 -0
  26. data/lib/twitter/cli/helpers.rb +109 -0
  27. data/lib/twitter/cli/migrations/20080722194500_create_accounts.rb +13 -0
  28. data/lib/twitter/cli/migrations/20080722194508_create_tweets.rb +16 -0
  29. data/lib/twitter/cli/migrations/20080722214605_add_account_id_to_tweets.rb +9 -0
  30. data/lib/twitter/cli/migrations/20080722214606_create_configurations.rb +13 -0
  31. data/lib/twitter/cli/models/account.rb +33 -0
  32. data/lib/twitter/cli/models/configuration.rb +13 -0
  33. data/lib/twitter/cli/models/tweet.rb +20 -0
  34. data/lib/twitter/direct_message.rb +30 -0
  35. data/lib/twitter/easy_class_maker.rb +43 -0
  36. data/lib/twitter/rate_limit_status.rb +19 -0
  37. data/lib/twitter/search.rb +101 -0
  38. data/lib/twitter/search_result.rb +83 -0
  39. data/lib/twitter/search_result_info.rb +82 -0
  40. data/lib/twitter/status.rb +22 -0
  41. data/lib/twitter/user.rb +37 -0
  42. data/lib/twitter/version.rb +3 -0
  43. data/spec/base_spec.rb +139 -0
  44. data/spec/cli/helper_spec.rb +49 -0
  45. data/spec/direct_message_spec.rb +40 -0
  46. data/spec/fixtures/follower_ids.xml +11 -0
  47. data/spec/fixtures/followers.xml +706 -0
  48. data/spec/fixtures/friend_ids.xml +12 -0
  49. data/spec/fixtures/friends.xml +609 -0
  50. data/spec/fixtures/friends_for.xml +584 -0
  51. data/spec/fixtures/friends_lite.xml +192 -0
  52. data/spec/fixtures/friends_timeline.xml +66 -0
  53. data/spec/fixtures/friendship_already_exists.xml +5 -0
  54. data/spec/fixtures/friendship_created.xml +12 -0
  55. data/spec/fixtures/public_timeline.xml +148 -0
  56. data/spec/fixtures/rate_limit_status.xml +7 -0
  57. data/spec/fixtures/search_result_info.yml +147 -0
  58. data/spec/fixtures/search_results.json +1 -0
  59. data/spec/fixtures/status.xml +25 -0
  60. data/spec/fixtures/user.xml +38 -0
  61. data/spec/fixtures/user_timeline.xml +465 -0
  62. data/spec/search_spec.rb +100 -0
  63. data/spec/spec.opts +1 -0
  64. data/spec/spec_helper.rb +23 -0
  65. data/spec/status_spec.rb +40 -0
  66. data/spec/user_spec.rb +42 -0
  67. data/website/css/common.css +47 -0
  68. data/website/images/terminal_output.png +0 -0
  69. data/website/index.html +147 -0
  70. metadata +188 -0
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
+
5
+ twitter = Twitter::Base.new(config['email'], config['password'])
6
+
7
+ puts twitter.create_friendship('orderedlist').name
8
+ puts twitter.follow('orderedlist').name
9
+ puts twitter.leave('orderedlist').name
10
+ puts twitter.destroy_friendship('orderedlist').name
11
+
12
+ puts twitter.friendship_exists?('jnunemaker', 'orderedlist').inspect
13
+ puts twitter.friendship_exists?('jnunemaker', 'ze').inspect
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
+
5
+ identica = Twitter::Base.new(config['email'], config['password'], :api_host => 'identi.ca/api')
6
+
7
+ identica.timeline(:public).each { |s| puts s.text, s.user.name, '' }
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
+
5
+ twitter = Twitter::Base.new(config['email'], config['password'])
6
+
7
+ puts twitter.update_location('Hollywood, CA').location
8
+ puts twitter.update_delivery_device('none')
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
+
5
+ twitter = Twitter::Base.new(config['email'], config['password'])
6
+ puts twitter.post("This is a test from the example file").inspect
7
+
8
+ # sending a direct message
9
+ # puts twitter.d('jnunemaker', 'this is a test').inspect
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ require 'activesupport'
3
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
4
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
5
+
6
+ twitter = Twitter::Base.new(config['email'], config['password'])
7
+
8
+ puts 'SINCE'
9
+ twitter.replies(:since => Time.now - 5.day).each do |s|
10
+ puts "- #{s.text}"
11
+ end
12
+ puts
13
+ puts
14
+
15
+ puts 'SINCE_ID'
16
+ twitter.replies(:since_id => 863081345).each do |s|
17
+ puts "- #{s.text}"
18
+ end
19
+ puts
20
+ puts
21
+
22
+ puts 'PAGE'
23
+ twitter.replies(:page => 1).each do |s|
24
+ puts "- #{s.text}"
25
+ end
26
+ puts
27
+ puts
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+
4
+ Twitter::Search.new('httparty').each { |r| puts r.inspect,'' }
5
+ Twitter::Search.new('httparty').page(2).each { |r| puts r.inspect, '' }
6
+
7
+ # search = Twitter::Search.new
8
+ # search.from('jnunemaker').to('oaknd1').each { |r| puts r.inspect, '' }
9
+ # pp search.result
10
+ # search.clear
11
+
12
+ # search.from('jnunemaker').to('oaknd1').since(814529437).containing('milk').each { |r| puts r.inspect, '' }
13
+ # search.clear
14
+ #
15
+ # search.geocode('40.757929', '-73.985506', '50mi').containing('holland').each { |r| puts r.inspect, '' }
16
+ # search.clear
17
+
18
+ # pp search.from('jnunemaker').fetch()
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ require 'activesupport'
3
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
4
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
5
+
6
+ twitter = Twitter::Base.new(config['email'], config['password'])
7
+
8
+ puts 'SINCE'
9
+ twitter.sent_messages(:since => Time.now - 5.day).each do |s|
10
+ puts "- #{s.text}"
11
+ end
12
+ puts
13
+ puts
14
+
15
+ puts 'SINCE_ID'
16
+ twitter.sent_messages(:since_id => 33505386).each do |s|
17
+ puts "- #{s.text}"
18
+ end
19
+ puts
20
+ puts
21
+
22
+ puts 'PAGE'
23
+ twitter.sent_messages(:page => 1).each do |s|
24
+ puts "- #{s.text}"
25
+ end
26
+ puts
27
+ puts
@@ -0,0 +1,34 @@
1
+ require 'rubygems'
2
+ require 'activesupport'
3
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
4
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
5
+
6
+ twitter = Twitter::Base.new(config['email'], config['password'])
7
+
8
+ puts 'SINCE'
9
+ twitter.timeline(:user, :since => Time.now - 1.day).each do |s|
10
+ puts "- #{s.text}"
11
+ end
12
+ puts
13
+ puts
14
+
15
+ puts 'SINCE_ID'
16
+ twitter.timeline(:user, :since_id => 865547074).each do |s|
17
+ puts "- #{s.text}"
18
+ end
19
+ puts
20
+ puts
21
+
22
+ puts 'COUNT'
23
+ twitter.timeline(:user, :count => 1).each do |s|
24
+ puts "- #{s.text}"
25
+ end
26
+ puts
27
+ puts
28
+
29
+ puts 'PAGE'
30
+ twitter.timeline(:user, :page => 1).each do |s|
31
+ puts "- #{s.text}"
32
+ end
33
+ puts
34
+ puts
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
+
5
+ puts "Public Timeline", "=" * 50
6
+ Twitter::Base.new(config['email'], config['password']).timeline(:public).each do |s|
7
+ puts s.text, s.user.name
8
+ puts
9
+ end
10
+
11
+ puts '', "Friends Timeline", "=" * 50
12
+ Twitter::Base.new(config['email'], config['password']).timeline.each do |s|
13
+ puts s.text, s.user.name
14
+ puts
15
+ end
16
+
17
+ puts '', "Friends", "=" * 50
18
+ Twitter::Base.new(config['email'], config['password']).friends.each do |u|
19
+ puts u.name, u.status.text
20
+ puts
21
+ end
22
+
23
+ puts '', "Followers", "=" * 50
24
+ Twitter::Base.new(config['email'], config['password']).followers.each do |u|
25
+ puts u.name, u.status.text
26
+ puts
27
+ end
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
+
5
+ twitter = Twitter::Base.new(config['email'], config['password'])
6
+
7
+ puts twitter.verify_credentials
8
+
9
+ begin
10
+ Twitter::Base.new('asdf', 'foobar').verify_credentials
11
+ rescue => error
12
+ puts error.message
13
+ end
@@ -0,0 +1,45 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{gilesbowkett-twitter}
5
+ s.version = "0.4.4"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["John Nunemaker", "Giles Bowkett"]
9
+ s.date = %q{2009-02-11}
10
+ s.default_executable = %q{twitter}
11
+ s.description = %q{a command line interface for twitter, also a library which wraps the twitter api}
12
+ s.email = %q{nunemaker@gmail.com}
13
+ s.executables = ["twitter"]
14
+ s.extra_rdoc_files = ["bin/twitter", "lib/twitter/base.rb", "lib/twitter/cli/config.rb", "lib/twitter/cli/helpers.rb", "lib/twitter/cli/migrations/20080722194500_create_accounts.rb", "lib/twitter/cli/migrations/20080722194508_create_tweets.rb", "lib/twitter/cli/migrations/20080722214605_add_account_id_to_tweets.rb", "lib/twitter/cli/migrations/20080722214606_create_configurations.rb", "lib/twitter/cli/models/account.rb", "lib/twitter/cli/models/configuration.rb", "lib/twitter/cli/models/tweet.rb", "lib/twitter/cli.rb", "lib/twitter/direct_message.rb", "lib/twitter/easy_class_maker.rb", "lib/twitter/rate_limit_status.rb", "lib/twitter/search.rb", "lib/twitter/search_result.rb", "lib/twitter/search_result_info.rb", "lib/twitter/status.rb", "lib/twitter/user.rb", "lib/twitter/version.rb", "lib/twitter.rb", "README"]
15
+ s.files = ["bin/twitter", "examples/blocks.rb", "examples/direct_messages.rb", "examples/favorites.rb", "examples/friends_followers.rb", "examples/friendships.rb", "examples/identica_timeline.rb", "examples/location.rb", "examples/posting.rb", "examples/replies.rb", "examples/search.rb", "examples/sent_messages.rb", "examples/timeline.rb", "examples/twitter.rb", "examples/verify_credentials.rb", "History", "lib/twitter/base.rb", "lib/twitter/cli/config.rb", "lib/twitter/cli/helpers.rb", "lib/twitter/cli/migrations/20080722194500_create_accounts.rb", "lib/twitter/cli/migrations/20080722194508_create_tweets.rb", "lib/twitter/cli/migrations/20080722214605_add_account_id_to_tweets.rb", "lib/twitter/cli/migrations/20080722214606_create_configurations.rb", "lib/twitter/cli/models/account.rb", "lib/twitter/cli/models/configuration.rb", "lib/twitter/cli/models/tweet.rb", "lib/twitter/cli.rb", "lib/twitter/direct_message.rb", "lib/twitter/easy_class_maker.rb", "lib/twitter/rate_limit_status.rb", "lib/twitter/search.rb", "lib/twitter/search_result.rb", "lib/twitter/search_result_info.rb", "lib/twitter/status.rb", "lib/twitter/user.rb", "lib/twitter/version.rb", "lib/twitter.rb", "License", "Manifest", "Rakefile", "README", "spec/base_spec.rb", "spec/cli/helper_spec.rb", "spec/direct_message_spec.rb", "spec/fixtures/follower_ids.xml", "spec/fixtures/followers.xml", "spec/fixtures/friend_ids.xml", "spec/fixtures/friends.xml", "spec/fixtures/friends_for.xml", "spec/fixtures/friends_lite.xml", "spec/fixtures/friends_timeline.xml", "spec/fixtures/friendship_already_exists.xml", "spec/fixtures/friendship_created.xml", "spec/fixtures/public_timeline.xml", "spec/fixtures/rate_limit_status.xml", "spec/fixtures/search_result_info.yml", "spec/fixtures/search_results.json", "spec/fixtures/status.xml", "spec/fixtures/user.xml", "spec/fixtures/user_timeline.xml", "spec/search_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "spec/status_spec.rb", "spec/user_spec.rb", "gilesbowkett-twitter.gemspec", "website/css/common.css", "website/images/terminal_output.png", "website/index.html"]
16
+ s.has_rdoc = true
17
+ s.homepage = %q{http://twitter.rubyforge.org}
18
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Twitter", "--main", "README"]
19
+ s.require_paths = ["lib"]
20
+ s.rubyforge_project = %q{twitter}
21
+ s.rubygems_version = %q{1.3.1}
22
+ s.summary = %q{a command line interface for twitter, also a library which wraps the twitter api}
23
+
24
+ if s.respond_to? :specification_version then
25
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
26
+ s.specification_version = 2
27
+
28
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
29
+ s.add_runtime_dependency(%q<hpricot>, [">= 0.6"])
30
+ s.add_runtime_dependency(%q<activesupport>, [">= 2.1"])
31
+ s.add_runtime_dependency(%q<httparty>, [">= 0.2.4"])
32
+ s.add_development_dependency(%q<echoe>, [">= 0"])
33
+ else
34
+ s.add_dependency(%q<hpricot>, [">= 0.6"])
35
+ s.add_dependency(%q<activesupport>, [">= 2.1"])
36
+ s.add_dependency(%q<httparty>, [">= 0.2.4"])
37
+ s.add_dependency(%q<echoe>, [">= 0"])
38
+ end
39
+ else
40
+ s.add_dependency(%q<hpricot>, [">= 0.6"])
41
+ s.add_dependency(%q<activesupport>, [">= 2.1"])
42
+ s.add_dependency(%q<httparty>, [">= 0.2.4"])
43
+ s.add_dependency(%q<echoe>, [">= 0"])
44
+ end
45
+ end
data/lib/twitter.rb ADDED
@@ -0,0 +1,32 @@
1
+ require 'uri'
2
+ require 'cgi'
3
+ require 'net/http'
4
+ require 'yaml'
5
+ require 'time'
6
+ require 'rubygems'
7
+ require 'hpricot'
8
+
9
+ $:.unshift(File.dirname(__FILE__))
10
+ require 'twitter/version'
11
+ require 'twitter/easy_class_maker'
12
+ require 'twitter/base'
13
+ require 'twitter/user'
14
+ require 'twitter/search'
15
+ require 'twitter/status'
16
+ require 'twitter/direct_message'
17
+ require 'twitter/rate_limit_status'
18
+ require 'twitter/search_result_info'
19
+ require 'twitter/search_result'
20
+
21
+ module Twitter
22
+ class Unavailable < StandardError; end
23
+ class CantConnect < StandardError; end
24
+ class BadResponse < StandardError; end
25
+ class UnknownTimeline < ArgumentError; end
26
+ class RateExceeded < StandardError; end
27
+ class CantFindUsers < ArgumentError; end
28
+ class AlreadyFollowing < StandardError; end
29
+ class CantFollowUser < StandardError; end
30
+
31
+ SourceName = 'twittergem'
32
+ end
@@ -0,0 +1,284 @@
1
+ # This is the base class for the twitter library. It makes all the requests
2
+ # to twitter, parses the xml (using hpricot) and returns ruby objects to play with.
3
+ #
4
+ # For complete documentation on the options, check out the twitter api docs.
5
+ # http://groups.google.com/group/twitter-development-talk/web/api-documentation
6
+ module Twitter
7
+ class Base
8
+ # Initializes the configuration for making requests to twitter
9
+ # Twitter example:
10
+ # Twitter.new('email/username', 'password')
11
+ #
12
+ # Identi.ca example:
13
+ # Twitter.new('email/username', 'password', :api_host => 'identi.ca/api')
14
+ def initialize(email, password, options={})
15
+ @api_host = options.delete(:api_host) || 'twitter.com'
16
+ @config, @config[:email], @config[:password] = options, email, password
17
+ @proxy_host = options[:proxy_host]
18
+ @proxy_port = options[:proxy_port]
19
+ end
20
+
21
+ # Returns an array of statuses for a timeline; Defaults to your friends timeline.
22
+ def timeline(which=:friends, options={})
23
+ raise UnknownTimeline unless [:friends, :public, :user].include?(which)
24
+ auth = which.to_s.include?('public') ? false : true
25
+ statuses(call("#{which}_timeline", :auth => auth, :since => options[:since], :args => parse_options(options)))
26
+ end
27
+
28
+ # Returns an array of users who are in your friends list
29
+ def friends(options={})
30
+ users(call(:friends, {:args => parse_options(options)}))
31
+ end
32
+
33
+ # Returns an array of users who are friends for the id or username passed in
34
+ def friends_for(id, options={})
35
+ friends(options.merge({:id => id}))
36
+ end
37
+
38
+ # Returns an array of user ids who are friends for the account or the option id/username passed in
39
+ def friend_ids(id_or_screenname = nil)
40
+ path = id_or_screenname ? "friends/ids/#{id_or_screenname}.xml" : "friends/ids.xml"
41
+ doc = request(path, :auth => true)
42
+ (doc/:id).inject([]) {|ids, id| ids << id.innerHTML; ids}
43
+ end
44
+
45
+ # Returns an array of users who are following you
46
+ def followers(options={})
47
+ users(call(:followers, {:args => parse_options(options)}))
48
+ end
49
+
50
+ def followers_for(id, options={})
51
+ followers(options.merge({:id => id}))
52
+ end
53
+
54
+ # Returns an array of user ids who are followers for the account or the option id/username passed in
55
+ def follower_ids(id_or_screenname = nil)
56
+ path = id_or_screenname ? "followers/ids/#{id_or_screenname}.xml" : "followers/ids.xml"
57
+ doc = request(path, :auth => true)
58
+ (doc/:id).inject([]) {|ids, id| ids << id.innerHTML; ids}
59
+ end
60
+
61
+ # Returns a single status for a given id
62
+ def status(id)
63
+ statuses(call("show/#{id}")).first
64
+ end
65
+
66
+ # returns all the profile information and the last status for a user
67
+ def user(id_or_screenname)
68
+ users(request("users/show/#{id_or_screenname}.xml", :auth => true)).first
69
+ end
70
+
71
+ # Returns an array of statuses that are replies
72
+ def replies(options={})
73
+ statuses(call(:replies, :since => options[:since], :args => parse_options(options)))
74
+ end
75
+
76
+ # Destroys a status by id
77
+ def destroy(id)
78
+ call("destroy/#{id}")
79
+ end
80
+
81
+ def rate_limit_status
82
+ RateLimitStatus.new_from_xml request("account/rate_limit_status.xml", :auth => true)
83
+ end
84
+
85
+ # waiting for twitter to correctly implement this in the api as it is documented
86
+ def featured
87
+ users(call(:featured))
88
+ end
89
+
90
+ # Returns an array of all the direct messages for the authenticated user
91
+ def direct_messages(options={})
92
+ doc = request(build_path('direct_messages.xml', parse_options(options)), {:auth => true, :since => options[:since]})
93
+ (doc/:direct_message).inject([]) { |dms, dm| dms << DirectMessage.new_from_xml(dm); dms }
94
+ end
95
+ alias :received_messages :direct_messages
96
+
97
+ # Returns direct messages sent by auth user
98
+ def sent_messages(options={})
99
+ doc = request(build_path('direct_messages/sent.xml', parse_options(options)), {:auth => true, :since => options[:since]})
100
+ (doc/:direct_message).inject([]) { |dms, dm| dms << DirectMessage.new_from_xml(dm); dms }
101
+ end
102
+
103
+ # destroys a give direct message by id if the auth user is a recipient
104
+ def destroy_direct_message(id)
105
+ DirectMessage.new_from_xml(request("direct_messages/destroy/#{id}.xml", :auth => true, :method => :post))
106
+ end
107
+
108
+ # Sends a direct message <code>text</code> to <code>user</code>
109
+ def d(user, text)
110
+ DirectMessage.new_from_xml(request('direct_messages/new.xml', :auth => true, :method => :post, :form_data => {'text' => text, 'user' => user}))
111
+ end
112
+
113
+ # Befriends id_or_screenname for the auth user
114
+ def create_friendship(id_or_screenname)
115
+ users(request("friendships/create/#{id_or_screenname}.xml", :auth => true, :method => :post)).first
116
+ end
117
+
118
+ # Defriends id_or_screenname for the auth user
119
+ def destroy_friendship(id_or_screenname)
120
+ users(request("friendships/destroy/#{id_or_screenname}.xml", :auth => true, :method => :post)).first
121
+ end
122
+
123
+ # Returns true if friendship exists, false if it doesn't.
124
+ def friendship_exists?(user_a, user_b)
125
+ doc = request(build_path("friendships/exists.xml", {:user_a => user_a, :user_b => user_b}), :auth => true)
126
+ doc.at('friends').innerHTML == 'true' ? true : false
127
+ end
128
+
129
+ # Updates your location and returns Twitter::User object
130
+ def update_location(location)
131
+ users(request(build_path('account/update_location.xml', {'location' => location}), :auth => true, :method => :post)).first
132
+ end
133
+
134
+ # Updates your deliver device and returns Twitter::User object
135
+ def update_delivery_device(device)
136
+ users(request(build_path('account/update_delivery_device.xml', {'device' => device}), :auth => true, :method => :post)).first
137
+ end
138
+
139
+ # Turns notifications by id_or_screenname on for auth user.
140
+ def follow(id_or_screenname)
141
+ users(request("notifications/follow/#{id_or_screenname}.xml", :auth => true, :method => :post)).first
142
+ end
143
+
144
+ # Turns notifications by id_or_screenname off for auth user.
145
+ def leave(id_or_screenname)
146
+ users(request("notifications/leave/#{id_or_screenname}.xml", :auth => true, :method => :post)).first
147
+ end
148
+
149
+ # Returns the most recent favorite statuses for the autenticating user
150
+ def favorites(options={})
151
+ statuses(request(build_path('favorites.xml', parse_options(options)), :auth => true))
152
+ end
153
+
154
+ # Favorites the status specified by id for the auth user
155
+ def create_favorite(id)
156
+ statuses(request("favorites/create/#{id}.xml", :auth => true, :method => :post)).first
157
+ end
158
+
159
+ # Un-favorites the status specified by id for the auth user
160
+ def destroy_favorite(id)
161
+ statuses(request("favorites/destroy/#{id}.xml", :auth => true, :method => :post)).first
162
+ end
163
+
164
+ # Blocks the user specified by id for the auth user
165
+ def block(id)
166
+ users(request("blocks/create/#{id}.xml", :auth => true, :method => :post)).first
167
+ end
168
+
169
+ # Unblocks the user specified by id for the auth user
170
+ def unblock(id)
171
+ users(request("blocks/destroy/#{id}.xml", :auth => true, :method => :post)).first
172
+ end
173
+
174
+ # Posts a new update to twitter for auth user.
175
+ def post(status, options={})
176
+ form_data = {'status' => status}
177
+ form_data.merge!({'source' => options[:source]}) if options[:source]
178
+ form_data.merge!({'in_reply_to_status_id' => options[:in_reply_to_status_id]}) if options[:in_reply_to_status_id]
179
+ Status.new_from_xml(request('statuses/update.xml', :auth => true, :method => :post, :form_data => form_data))
180
+ end
181
+ alias :update :post
182
+
183
+ # Verifies the credentials for the auth user.
184
+ # raises Twitter::CantConnect on failure.
185
+ def verify_credentials
186
+ request('account/verify_credentials.xml', :auth => true)
187
+ end
188
+
189
+ private
190
+ # Converts an hpricot doc to an array of statuses
191
+ def statuses(doc)
192
+ (doc/:status).inject([]) { |statuses, status| statuses << Status.new_from_xml(status); statuses }
193
+ end
194
+
195
+ # Converts an hpricot doc to an array of users
196
+ def users(doc)
197
+ (doc/:user).inject([]) { |users, user| users << User.new_from_xml(user); users }
198
+ end
199
+
200
+ # Calls whatever api method requested that deals with statuses
201
+ #
202
+ # ie: call(:public_timeline, :auth => false)
203
+ def call(method, options={})
204
+ options = { :auth => true, :args => {} }.merge(options)
205
+ # Following line needed as lite=false doesn't work in the API: http://tinyurl.com/yo3h5d
206
+ options[:args].delete(:lite) unless options[:args][:lite]
207
+ args = options.delete(:args)
208
+ request(build_path("statuses/#{method.to_s}.xml", args), options)
209
+ end
210
+
211
+ def response(path, options={})
212
+ uri = URI.parse("http://#{@api_host}")
213
+
214
+ begin
215
+ response = Net::HTTP::Proxy(@proxy_host, @proxy_port).start(uri.host, uri.port) do |http|
216
+ klass = Net::HTTP.const_get options[:method].to_s.downcase.capitalize
217
+ req = klass.new("#{uri.path}/#{path}", options[:headers])
218
+ req.basic_auth(@config[:email], @config[:password]) if options[:auth]
219
+ if options[:method].to_s == 'post' && options[:form_data]
220
+ req.set_form_data(options[:form_data])
221
+ end
222
+ http.request(req)
223
+ end
224
+ rescue => error
225
+ raise CantConnect, error.message
226
+ end
227
+ end
228
+
229
+ # Makes a request to twitter.
230
+ def request(path, options={})
231
+ options = {
232
+ :headers => { "User-Agent" => @config[:email] },
233
+ :method => :get,
234
+ }.merge(options)
235
+
236
+ unless options[:since].nil?
237
+ since = options[:since].kind_of?(Date) ? options[:since].strftime('%a, %d-%b-%y %T GMT') : options[:since].to_s
238
+ options[:headers]["If-Modified-Since"] = since
239
+ end
240
+
241
+ handle_response!(response(path, options))
242
+ end
243
+
244
+ def handle_response!(response)
245
+ if %w[200 304].include?(response.code)
246
+ response = parse(response.body)
247
+ raise RateExceeded if (response/:hash/:error).text =~ /Rate limit exceeded/
248
+ response
249
+ elsif response.code == '503'
250
+ raise Unavailable, response.message
251
+ elsif response.code == '401'
252
+ raise CantConnect, 'Authentication failed. Check your username and password'
253
+ elsif response.code == '403'
254
+ error_message = (parse(response.body)/:hash/:error).text
255
+ raise CantFindUsers, error_message if error_message =~ /Could not find both specified users/
256
+ raise AlreadyFollowing, error_message if error_message =~ /already on your list/
257
+ raise CantFollowUser, "Response code #{response.code}: #{response.message} #{error_message}"
258
+ else
259
+ raise CantConnect, "Twitter is returning a #{response.code}: #{response.message}"
260
+ end
261
+ end
262
+
263
+ # Given a path and a hash, build a full path with the hash turned into a query string
264
+ def build_path(path, options)
265
+ unless options.nil?
266
+ query = options.inject('') { |str, h| str += "#{CGI.escape(h[0].to_s)}=#{CGI.escape(h[1].to_s)}&"; str }
267
+ path += "?#{query}"
268
+ end
269
+
270
+ path
271
+ end
272
+
273
+ # Tries to get all the options in the correct format before making the request
274
+ def parse_options(options)
275
+ options[:since] = options[:since].kind_of?(Date) ? options[:since].strftime('%a, %d-%b-%y %T GMT') : options[:since].to_s if options[:since]
276
+ options
277
+ end
278
+
279
+ # Converts a string response into an Hpricot xml element.
280
+ def parse(response)
281
+ Hpricot.XML(response || '')
282
+ end
283
+ end
284
+ end