anthonycrumley-twitter 0.3.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/History.txt +106 -0
  2. data/License.txt +19 -0
  3. data/Manifest.txt +71 -0
  4. data/README.txt +84 -0
  5. data/Rakefile +4 -0
  6. data/bin/twitter +15 -0
  7. data/config/hoe.rb +74 -0
  8. data/config/requirements.rb +17 -0
  9. data/examples/blocks.rb +15 -0
  10. data/examples/direct_messages.rb +28 -0
  11. data/examples/favorites.rb +20 -0
  12. data/examples/friends_followers.rb +25 -0
  13. data/examples/friendships.rb +13 -0
  14. data/examples/identica_timeline.rb +7 -0
  15. data/examples/location.rb +8 -0
  16. data/examples/posting.rb +9 -0
  17. data/examples/replies.rb +26 -0
  18. data/examples/search.rb +17 -0
  19. data/examples/sent_messages.rb +26 -0
  20. data/examples/timeline.rb +33 -0
  21. data/examples/twitter.rb +27 -0
  22. data/examples/verify_credentials.rb +13 -0
  23. data/lib/twitter.rb +21 -0
  24. data/lib/twitter/base.rb +252 -0
  25. data/lib/twitter/cli.rb +328 -0
  26. data/lib/twitter/cli/config.rb +9 -0
  27. data/lib/twitter/cli/helpers.rb +97 -0
  28. data/lib/twitter/cli/migrations/20080722194500_create_accounts.rb +13 -0
  29. data/lib/twitter/cli/migrations/20080722194508_create_tweets.rb +16 -0
  30. data/lib/twitter/cli/migrations/20080722214605_add_account_id_to_tweets.rb +9 -0
  31. data/lib/twitter/cli/migrations/20080722214606_create_configurations.rb +13 -0
  32. data/lib/twitter/cli/models/account.rb +33 -0
  33. data/lib/twitter/cli/models/configuration.rb +13 -0
  34. data/lib/twitter/cli/models/tweet.rb +20 -0
  35. data/lib/twitter/direct_message.rb +22 -0
  36. data/lib/twitter/easy_class_maker.rb +43 -0
  37. data/lib/twitter/rate_limit_status.rb +19 -0
  38. data/lib/twitter/search.rb +94 -0
  39. data/lib/twitter/status.rb +22 -0
  40. data/lib/twitter/user.rb +37 -0
  41. data/lib/twitter/version.rb +9 -0
  42. data/script/destroy +14 -0
  43. data/script/generate +14 -0
  44. data/script/txt2html +74 -0
  45. data/setup.rb +1585 -0
  46. data/spec/base_spec.rb +109 -0
  47. data/spec/cli/helper_spec.rb +35 -0
  48. data/spec/direct_message_spec.rb +35 -0
  49. data/spec/fixtures/followers.xml +706 -0
  50. data/spec/fixtures/friends.xml +609 -0
  51. data/spec/fixtures/friends_for.xml +584 -0
  52. data/spec/fixtures/friends_lite.xml +192 -0
  53. data/spec/fixtures/friends_timeline.xml +66 -0
  54. data/spec/fixtures/public_timeline.xml +148 -0
  55. data/spec/fixtures/rate_limit_status.xml +7 -0
  56. data/spec/fixtures/search_results.json +1 -0
  57. data/spec/fixtures/status.xml +25 -0
  58. data/spec/fixtures/user.xml +38 -0
  59. data/spec/fixtures/user_timeline.xml +465 -0
  60. data/spec/search_spec.rb +89 -0
  61. data/spec/spec.opts +1 -0
  62. data/spec/spec_helper.rb +12 -0
  63. data/spec/status_spec.rb +40 -0
  64. data/spec/user_spec.rb +42 -0
  65. data/tasks/deployment.rake +50 -0
  66. data/tasks/environment.rake +7 -0
  67. data/tasks/website.rake +17 -0
  68. data/twitter.gemspec +49 -0
  69. data/website/css/common.css +47 -0
  70. data/website/images/terminal_output.png +0 -0
  71. data/website/index.html +156 -0
  72. metadata +180 -0
@@ -0,0 +1,25 @@
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 "FRIENDS"
8
+ twitter.friends.each { |f| puts f.name }
9
+ puts
10
+ puts
11
+
12
+ puts "FRIENDS FOR"
13
+ twitter.friends_for('orderedlist', :lite => true).each { |f| puts f.name }
14
+ puts
15
+ puts
16
+
17
+ puts "FOLLOWERS"
18
+ twitter.followers(:lite => true).each { |f| puts f.name }
19
+ puts
20
+ puts
21
+
22
+ puts "FOLLOWERS FOR"
23
+ twitter.followers_for('orderedlist', :lite => true).each { |f| puts f.name }
24
+ puts
25
+ puts
@@ -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,26 @@
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 'SINCE'
8
+ twitter.replies(:since => Time.now - 5.day).each do |s|
9
+ puts "- #{s.text}"
10
+ end
11
+ puts
12
+ puts
13
+
14
+ puts 'SINCE_ID'
15
+ twitter.replies(:since_id => 863081345).each do |s|
16
+ puts "- #{s.text}"
17
+ end
18
+ puts
19
+ puts
20
+
21
+ puts 'PAGE'
22
+ twitter.replies(:page => 1).each do |s|
23
+ puts "- #{s.text}"
24
+ end
25
+ puts
26
+ puts
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+
4
+ Twitter::Search.new('httparty').each { |r| puts r.inspect,'' }
5
+
6
+ # search = Twitter::Search.new
7
+ # search.from('jnunemaker').to('oaknd1').each { |r| puts r.inspect, '' }
8
+ # pp search.result
9
+ # search.clear
10
+
11
+ # search.from('jnunemaker').to('oaknd1').since(814529437).containing('milk').each { |r| puts r.inspect, '' }
12
+ # search.clear
13
+ #
14
+ # search.geocode('40.757929', '-73.985506', '50mi').containing('holland').each { |r| puts r.inspect, '' }
15
+ # search.clear
16
+
17
+ # pp search.from('jnunemaker').fetch()
@@ -0,0 +1,26 @@
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 'SINCE'
8
+ twitter.sent_messages(:since => Time.now - 5.day).each do |s|
9
+ puts "- #{s.text}"
10
+ end
11
+ puts
12
+ puts
13
+
14
+ puts 'SINCE_ID'
15
+ twitter.sent_messages(:since_id => 33505386).each do |s|
16
+ puts "- #{s.text}"
17
+ end
18
+ puts
19
+ puts
20
+
21
+ puts 'PAGE'
22
+ twitter.sent_messages(:page => 1).each do |s|
23
+ puts "- #{s.text}"
24
+ end
25
+ puts
26
+ puts
@@ -0,0 +1,33 @@
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 'SINCE'
8
+ twitter.timeline(:user, :since => Time.now - 1.day).each do |s|
9
+ puts "- #{s.text}"
10
+ end
11
+ puts
12
+ puts
13
+
14
+ puts 'SINCE_ID'
15
+ twitter.timeline(:user, :since_id => 865547074).each do |s|
16
+ puts "- #{s.text}"
17
+ end
18
+ puts
19
+ puts
20
+
21
+ puts 'COUNT'
22
+ twitter.timeline(:user, :count => 1).each do |s|
23
+ puts "- #{s.text}"
24
+ end
25
+ puts
26
+ puts
27
+
28
+ puts 'PAGE'
29
+ twitter.timeline(:user, :page => 1).each do |s|
30
+ puts "- #{s.text}"
31
+ end
32
+ puts
33
+ 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,21 @@
1
+ %w(uri cgi net/http yaml rubygems hpricot active_support).each { |f| require f }
2
+
3
+ $:.unshift(File.join(File.dirname(__FILE__)))
4
+ require 'twitter/version'
5
+ require 'twitter/easy_class_maker'
6
+ require 'twitter/base'
7
+ require 'twitter/user'
8
+ require 'twitter/search'
9
+ require 'twitter/status'
10
+ require 'twitter/direct_message'
11
+ require 'twitter/rate_limit_status'
12
+
13
+ module Twitter
14
+ class Unavailable < StandardError; end
15
+ class CantConnect < StandardError; end
16
+ class BadResponse < StandardError; end
17
+ class UnknownTimeline < ArgumentError; end
18
+ class RateExceeded < StandardError; end
19
+
20
+ SourceName = 'twittergem'
21
+ end
@@ -0,0 +1,252 @@
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
+ @config, @config[:email], @config[:password] = {}, email, password
16
+ @api_host = options.delete(:api_host) || 'twitter.com'
17
+ end
18
+
19
+ # Returns an array of statuses for a timeline; Defaults to your friends timeline.
20
+ def timeline(which=:friends, options={})
21
+ raise UnknownTimeline unless [:friends, :public, :user].include?(which)
22
+ auth = which.to_s.include?('public') ? false : true
23
+ statuses(call("#{which}_timeline", :auth => auth, :since => options[:since], :args => parse_options(options)))
24
+ end
25
+
26
+ # Returns an array of users who are in your friends list
27
+ def friends(options={})
28
+ users(call(:friends, {:args => parse_options(options)}))
29
+ end
30
+
31
+ # Returns an array of users who are friends for the id or username passed in
32
+ def friends_for(id, options={})
33
+ friends(options.merge({:id => id}))
34
+ end
35
+
36
+ # Returns an array of users who are following you
37
+ def followers(options={})
38
+ users(call(:followers, {:args => parse_options(options)}))
39
+ end
40
+
41
+ def followers_for(id, options={})
42
+ followers(options.merge({:id => id}))
43
+ end
44
+
45
+ # Returns a single status for a given id
46
+ def status(id)
47
+ statuses(call("show/#{id}")).first
48
+ end
49
+
50
+ # returns all the profile information and the last status for a user
51
+ def user(id_or_screenname)
52
+ users(request("users/show/#{id_or_screenname}.xml", :auth => true)).first
53
+ end
54
+
55
+ # Returns an array of statuses that are replies
56
+ def replies(options={})
57
+ statuses(call(:replies, :since => options[:since], :args => parse_options(options)))
58
+ end
59
+
60
+ # Destroys a status by id
61
+ def destroy(id)
62
+ call("destroy/#{id}")
63
+ end
64
+
65
+ def rate_limit_status
66
+ RateLimitStatus.new_from_xml request("account/rate_limit_status.xml", :auth => true)
67
+ end
68
+
69
+ # waiting for twitter to correctly implement this in the api as it is documented
70
+ def featured
71
+ users(call(:featured))
72
+ end
73
+
74
+ # Returns an array of all the direct messages for the authenticated user
75
+ def direct_messages(options={})
76
+ doc = request(build_path('direct_messages.xml', parse_options(options)), {:auth => true, :since => options[:since]})
77
+ (doc/:direct_message).inject([]) { |dms, dm| dms << DirectMessage.new_from_xml(dm); dms }
78
+ end
79
+ alias :received_messages :direct_messages
80
+
81
+ # Returns direct messages sent by auth user
82
+ def sent_messages(options={})
83
+ doc = request(build_path('direct_messages/sent.xml', parse_options(options)), {:auth => true, :since => options[:since]})
84
+ (doc/:direct_message).inject([]) { |dms, dm| dms << DirectMessage.new_from_xml(dm); dms }
85
+ end
86
+
87
+ # destroys a give direct message by id if the auth user is a recipient
88
+ def destroy_direct_message(id)
89
+ DirectMessage.new_from_xml(request("direct_messages/destroy/#{id}.xml", :auth => true, :method => :post))
90
+ end
91
+
92
+ # Sends a direct message <code>text</code> to <code>user</code>
93
+ def d(user, text)
94
+ DirectMessage.new_from_xml(request('direct_messages/new.xml', :auth => true, :method => :post, :form_data => {'text' => text, 'user' => user}))
95
+ end
96
+
97
+ # Befriends id_or_screenname for the auth user
98
+ def create_friendship(id_or_screenname)
99
+ users(request("friendships/create/#{id_or_screenname}.xml", :auth => true, :method => :post)).first
100
+ end
101
+
102
+ # Defriends id_or_screenname for the auth user
103
+ def destroy_friendship(id_or_screenname)
104
+ users(request("friendships/destroy/#{id_or_screenname}.xml", :auth => true, :method => :post)).first
105
+ end
106
+
107
+ # Returns true if friendship exists, false if it doesn't.
108
+ def friendship_exists?(user_a, user_b)
109
+ doc = request(build_path("friendships/exists.xml", {:user_a => user_a, :user_b => user_b}), :auth => true)
110
+ doc.at('friends').innerHTML == 'true' ? true : false
111
+ end
112
+
113
+ # Updates your location and returns Twitter::User object
114
+ def update_location(location)
115
+ users(request(build_path('account/update_location.xml', {'location' => location}), :auth => true, :method => :post)).first
116
+ end
117
+
118
+ # Updates your deliver device and returns Twitter::User object
119
+ def update_delivery_device(device)
120
+ users(request(build_path('account/update_delivery_device.xml', {'device' => device}), :auth => true, :method => :post)).first
121
+ end
122
+
123
+ # Turns notifications by id_or_screenname on for auth user.
124
+ def follow(id_or_screenname)
125
+ users(request("notifications/follow/#{id_or_screenname}.xml", :auth => true, :method => :post)).first
126
+ end
127
+
128
+ # Turns notifications by id_or_screenname off for auth user.
129
+ def leave(id_or_screenname)
130
+ users(request("notifications/leave/#{id_or_screenname}.xml", :auth => true, :method => :post)).first
131
+ end
132
+
133
+ # Returns the most recent favorite statuses for the autenticating user
134
+ def favorites(options={})
135
+ statuses(request(build_path('favorites.xml', parse_options(options)), :auth => true))
136
+ end
137
+
138
+ # Favorites the status specified by id for the auth user
139
+ def create_favorite(id)
140
+ statuses(request("favorites/create/#{id}.xml", :auth => true, :method => :post)).first
141
+ end
142
+
143
+ # Un-favorites the status specified by id for the auth user
144
+ def destroy_favorite(id)
145
+ statuses(request("favorites/destroy/#{id}.xml", :auth => true, :method => :post)).first
146
+ end
147
+
148
+ # Blocks the user specified by id for the auth user
149
+ def block(id)
150
+ users(request("blocks/create/#{id}.xml", :auth => true, :method => :post)).first
151
+ end
152
+
153
+ # Unblocks the user specified by id for the auth user
154
+ def unblock(id)
155
+ users(request("blocks/destroy/#{id}.xml", :auth => true, :method => :post)).first
156
+ end
157
+
158
+ # Posts a new update to twitter for auth user.
159
+ def post(status, options={})
160
+ form_data = {'status' => status}
161
+ form_data.merge!({'source' => options[:source]}) if options[:source]
162
+ form_data.merge!({'in_reply_to_status_id' => options[:in_reply_to_status_id]}) if options[:in_reply_to_status_id]
163
+ Status.new_from_xml(request('statuses/update.xml', :auth => true, :method => :post, :form_data => form_data))
164
+ end
165
+ alias :update :post
166
+
167
+ # Verifies the credentials for the auth user.
168
+ # raises Twitter::CantConnect on failure.
169
+ def verify_credentials
170
+ request('account/verify_credentials', :auth => true)
171
+ end
172
+
173
+ private
174
+ # Converts an hpricot doc to an array of statuses
175
+ def statuses(doc)
176
+ (doc/:status).inject([]) { |statuses, status| statuses << Status.new_from_xml(status); statuses }
177
+ end
178
+
179
+ # Converts an hpricot doc to an array of users
180
+ def users(doc)
181
+ (doc/:user).inject([]) { |users, user| users << User.new_from_xml(user); users }
182
+ end
183
+
184
+ # Calls whatever api method requested that deals with statuses
185
+ #
186
+ # ie: call(:public_timeline, :auth => false)
187
+ def call(method, options={})
188
+ options.reverse_merge!({ :auth => true, :args => {} })
189
+ # Following line needed as lite=false doesn't work in the API: http://tinyurl.com/yo3h5d
190
+ options[:args].delete(:lite) unless options[:args][:lite]
191
+ args = options.delete(:args)
192
+ request(build_path("statuses/#{method.to_s}.xml", args), options)
193
+ end
194
+
195
+ # Makes a request to twitter.
196
+ def request(path, options={})
197
+ options.reverse_merge!({
198
+ :headers => { "User-Agent" => @config[:email] },
199
+ :method => :get
200
+ })
201
+ unless options[:since].blank?
202
+ since = options[:since].kind_of?(Date) ? options[:since].strftime('%a, %d-%b-%y %T GMT') : options[:since].to_s
203
+ options[:headers]["If-Modified-Since"] = since
204
+ end
205
+
206
+ uri = URI.parse("http://#{@api_host}")
207
+
208
+ begin
209
+ response = Net::HTTP.start(uri.host, 80) do |http|
210
+ klass = Net::HTTP.const_get options[:method].to_s.downcase.capitalize
211
+ req = klass.new("#{uri.path}/#{path}", options[:headers])
212
+ req.basic_auth(@config[:email], @config[:password]) if options[:auth]
213
+ if options[:method].to_s == 'post' && options[:form_data]
214
+ req.set_form_data(options[:form_data])
215
+ end
216
+ http.request(req)
217
+ end
218
+ rescue => error
219
+ raise CantConnect, error.message
220
+ end
221
+
222
+ if %w[200 304].include?(response.code)
223
+ response = parse(response.body)
224
+ raise RateExceeded if (response/:hash/:error).text =~ /Rate limit exceeded/
225
+ response
226
+ elsif response.code == '503'
227
+ raise Unavailable, response.message
228
+ elsif response.code == '401'
229
+ raise CantConnect, 'Authentication failed. Check your username and password'
230
+ else
231
+ raise CantConnect, "Twitter is returning a #{response.code}: #{response.message}"
232
+ end
233
+ end
234
+
235
+ # Given a path and a hash, build a full path with the hash turned into a query string
236
+ def build_path(path, options)
237
+ path += "?#{options.to_query}" unless options.blank?
238
+ path
239
+ end
240
+
241
+ # Tries to get all the options in the correct format before making the request
242
+ def parse_options(options)
243
+ options[:since] = options[:since].kind_of?(Date) ? options[:since].strftime('%a, %d-%b-%y %T GMT') : options[:since].to_s if options[:since]
244
+ options
245
+ end
246
+
247
+ # Converts a string response into an Hpricot xml element.
248
+ def parse(response)
249
+ Hpricot.XML(response || '')
250
+ end
251
+ end
252
+ end