billymeltdown-twitter 0.3.8.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,23 @@
1
+ 0.4.2 - February 10, 2009
2
+ * 1 minor enhancement
3
+ * Adding the Social Graph API methods (Josh Owens)
4
+
5
+ 0.4.1 - January 1, 2009
6
+ * 4 minor enhancements and 2 bug fixes:
7
+ * Added better exception handling (Billy Gray)
8
+ * Added page to search (Michael Ivey)
9
+ * Adding an option to display tweets on CLI in reverse order (oldest first). (Cameron Booth)
10
+ * Added in_reply_to_status_id option for replying to statuses (anthonycrumley)
11
+ * Fixed a bug where the @config was improperly set (K. Adam Christensen)
12
+ * Fix verify_credentials to include a format (breaks in laconica). (dustin)
13
+
14
+ 0.4.0 - December 23, 2008
15
+ * 3 major changes
16
+ * Removed active support as dependency
17
+ * Removed CLI dependencies from install dependency list
18
+ (they are now only installed by you manually)
19
+ * Switched to echoe for gem managment
20
+
1
21
  0.3.7 - August 26, 2008
2
22
  * Fixed source param not getting through
3
23
 
File without changes
@@ -1,11 +1,4 @@
1
- History.txt
2
- License.txt
3
- Manifest.txt
4
- README.txt
5
- Rakefile
6
1
  bin/twitter
7
- config/hoe.rb
8
- config/requirements.rb
9
2
  examples/blocks.rb
10
3
  examples/direct_messages.rb
11
4
  examples/favorites.rb
@@ -20,9 +13,8 @@ examples/sent_messages.rb
20
13
  examples/timeline.rb
21
14
  examples/twitter.rb
22
15
  examples/verify_credentials.rb
23
- lib/twitter.rb
16
+ History
24
17
  lib/twitter/base.rb
25
- lib/twitter/cli.rb
26
18
  lib/twitter/cli/config.rb
27
19
  lib/twitter/cli/helpers.rb
28
20
  lib/twitter/cli/migrations/20080722194500_create_accounts.rb
@@ -32,27 +24,36 @@ lib/twitter/cli/migrations/20080722214606_create_configurations.rb
32
24
  lib/twitter/cli/models/account.rb
33
25
  lib/twitter/cli/models/configuration.rb
34
26
  lib/twitter/cli/models/tweet.rb
27
+ lib/twitter/cli.rb
35
28
  lib/twitter/direct_message.rb
36
29
  lib/twitter/easy_class_maker.rb
37
30
  lib/twitter/rate_limit_status.rb
38
31
  lib/twitter/search.rb
32
+ lib/twitter/search_result.rb
33
+ lib/twitter/search_result_info.rb
39
34
  lib/twitter/status.rb
40
35
  lib/twitter/user.rb
41
36
  lib/twitter/version.rb
42
- script/destroy
43
- script/generate
44
- script/txt2html
45
- setup.rb
37
+ lib/twitter.rb
38
+ License
39
+ Manifest
40
+ Rakefile
41
+ README
46
42
  spec/base_spec.rb
47
43
  spec/cli/helper_spec.rb
48
44
  spec/direct_message_spec.rb
45
+ spec/fixtures/follower_ids.xml
49
46
  spec/fixtures/followers.xml
47
+ spec/fixtures/friend_ids.xml
50
48
  spec/fixtures/friends.xml
51
49
  spec/fixtures/friends_for.xml
52
50
  spec/fixtures/friends_lite.xml
53
51
  spec/fixtures/friends_timeline.xml
52
+ spec/fixtures/friendship_already_exists.xml
53
+ spec/fixtures/friendship_created.xml
54
54
  spec/fixtures/public_timeline.xml
55
55
  spec/fixtures/rate_limit_status.xml
56
+ spec/fixtures/search_result_info.yml
56
57
  spec/fixtures/search_results.json
57
58
  spec/fixtures/status.xml
58
59
  spec/fixtures/user.xml
@@ -62,9 +63,6 @@ spec/spec.opts
62
63
  spec/spec_helper.rb
63
64
  spec/status_spec.rb
64
65
  spec/user_spec.rb
65
- tasks/deployment.rake
66
- tasks/environment.rake
67
- tasks/website.rake
68
66
  twitter.gemspec
69
67
  website/css/common.css
70
68
  website/images/terminal_output.png
File without changes
data/Rakefile CHANGED
@@ -1,4 +1,42 @@
1
- require 'config/requirements'
2
- require 'config/hoe' # setup Hoe + all gem configuration
1
+ ProjectName = 'twitter'
2
+ WebsitePath = "jnunemaker@rubyforge.org:/var/www/gforge-projects/#{ProjectName}"
3
3
 
4
- Dir['tasks/**/*.rake'].each { |rake| load rake }
4
+ require 'rubygems'
5
+ require 'rake'
6
+ require 'echoe'
7
+ require 'spec/rake/spectask'
8
+ require "lib/#{ProjectName}/version"
9
+
10
+ Echoe.new(ProjectName, Twitter::Version) do |p|
11
+ p.description = "a command line interface for twitter, also a library which wraps the twitter api"
12
+ p.url = "http://#{ProjectName}.rubyforge.org"
13
+ p.author = "John Nunemaker"
14
+ p.email = "nunemaker@gmail.com"
15
+ p.extra_deps = [['hpricot', '>= 0.6'], ['activesupport', '>= 2.1'], ['httparty', '>= 0.2.4']]
16
+ p.need_tar_gz = false
17
+ p.docs_host = WebsitePath
18
+ end
19
+
20
+ desc 'Upload website files to rubyforge'
21
+ task :website do
22
+ sh %{rsync -av website/ #{WebsitePath}}
23
+ Rake::Task['website_docs'].invoke
24
+ end
25
+
26
+ task :website_docs do
27
+ Rake::Task['redocs'].invoke
28
+ sh %{rsync -av doc/ #{WebsitePath}/docs}
29
+ end
30
+
31
+ desc 'Preps the gem for a new release'
32
+ task :prepare do
33
+ %w[manifest build_gemspec].each do |task|
34
+ Rake::Task[task].invoke
35
+ end
36
+ end
37
+
38
+ Rake::Task[:default].prerequisites.clear
39
+ task :default => :spec
40
+ Spec::Rake::SpecTask.new do |t|
41
+ t.spec_files = FileList["spec/**/*_spec.rb"]
42
+ end
data/bin/twitter CHANGED
@@ -10,6 +10,5 @@ if ARGV[0] && ARGV[0] == 'd' && !STDIN.tty?
10
10
  ARGV[2] = "#{STDIN.read}#{ARGV[2]}"
11
11
  end
12
12
 
13
- $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
14
- require 'twitter'
13
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'twitter'))
15
14
  require 'twitter/cli'
@@ -1,4 +1,5 @@
1
1
  require 'rubygems'
2
+ require 'activesupport'
2
3
  require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
4
  config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
5
 
@@ -4,13 +4,13 @@ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
4
 
5
5
  twitter = Twitter::Base.new(config['email'], config['password'])
6
6
 
7
- puts 'FAVORITES'
8
- twitter.favorites.each { |f| puts f.text }
7
+ puts 'CREATE'
8
+ puts twitter.create_favorite(865416114).text
9
9
  puts
10
10
  puts
11
11
 
12
- puts 'CREATE'
13
- puts twitter.create_favorite(865416114).text
12
+ puts 'FAVORITES'
13
+ twitter.favorites.each { |f| puts f.text }
14
14
  puts
15
15
  puts
16
16
 
data/examples/replies.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'rubygems'
2
+ require 'activesupport'
2
3
  require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
4
  config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
5
 
data/examples/search.rb CHANGED
@@ -2,6 +2,7 @@ require 'rubygems'
2
2
  require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
3
 
4
4
  Twitter::Search.new('httparty').each { |r| puts r.inspect,'' }
5
+ Twitter::Search.new('httparty').page(2).each { |r| puts r.inspect, '' }
5
6
 
6
7
  # search = Twitter::Search.new
7
8
  # search.from('jnunemaker').to('oaknd1').each { |r| puts r.inspect, '' }
@@ -1,4 +1,5 @@
1
1
  require 'rubygems'
2
+ require 'activesupport'
2
3
  require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
4
  config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
5
 
data/examples/timeline.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'rubygems'
2
+ require 'activesupport'
2
3
  require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
4
  config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
5
 
data/lib/twitter.rb CHANGED
@@ -1,6 +1,12 @@
1
- %w(uri cgi net/http yaml rubygems hpricot active_support).each { |f| require f }
1
+ require 'uri'
2
+ require 'cgi'
3
+ require 'net/http'
4
+ require 'yaml'
5
+ require 'time'
6
+ require 'rubygems'
7
+ require 'hpricot'
2
8
 
3
- $:.unshift(File.join(File.dirname(__FILE__)))
9
+ $:.unshift(File.dirname(__FILE__))
4
10
  require 'twitter/version'
5
11
  require 'twitter/easy_class_maker'
6
12
  require 'twitter/base'
@@ -9,6 +15,8 @@ require 'twitter/search'
9
15
  require 'twitter/status'
10
16
  require 'twitter/direct_message'
11
17
  require 'twitter/rate_limit_status'
18
+ require 'twitter/search_result_info'
19
+ require 'twitter/search_result'
12
20
 
13
21
  module Twitter
14
22
  class Unavailable < StandardError; end
@@ -16,10 +24,10 @@ module Twitter
16
24
  class BadResponse < StandardError; end
17
25
  class UnknownTimeline < ArgumentError; end
18
26
  class RateExceeded < StandardError; end
19
- class CantFindUsers < ArgumentError; end
20
- class AlreadyFollowing < StandardError; end
21
27
  class RequestRefused < ArgumentError; end
22
- class YouAreNotFriends < StandardError; end
28
+ class CantFindUsers < RequestRefused; end
29
+ class AlreadyFollowing < RequestRefused; end
30
+ class YouAreNotFriends < RequestRefused; end
23
31
 
24
32
  SourceName = 'twittergem'
25
33
  end
data/lib/twitter/base.rb CHANGED
@@ -12,8 +12,10 @@ module Twitter
12
12
  # Identi.ca example:
13
13
  # Twitter.new('email/username', 'password', :api_host => 'identi.ca/api')
14
14
  def initialize(email, password, options={})
15
- @config, @config[:email], @config[:password] = {}, email, password
16
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]
17
19
  end
18
20
 
19
21
  # Returns an array of statuses for a timeline; Defaults to your friends timeline.
@@ -33,6 +35,13 @@ module Twitter
33
35
  friends(options.merge({:id => id}))
34
36
  end
35
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
+
36
45
  # Returns an array of users who are following you
37
46
  def followers(options={})
38
47
  users(call(:followers, {:args => parse_options(options)}))
@@ -42,6 +51,13 @@ module Twitter
42
51
  followers(options.merge({:id => id}))
43
52
  end
44
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
+
45
61
  # Returns a single status for a given id
46
62
  def status(id)
47
63
  statuses(call("show/#{id}")).first
@@ -159,6 +175,7 @@ module Twitter
159
175
  def post(status, options={})
160
176
  form_data = {'status' => status}
161
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]
162
179
  Status.new_from_xml(request('statuses/update.xml', :auth => true, :method => :post, :form_data => form_data))
163
180
  end
164
181
  alias :update :post
@@ -166,7 +183,7 @@ module Twitter
166
183
  # Verifies the credentials for the auth user.
167
184
  # raises Twitter::CantConnect on failure.
168
185
  def verify_credentials
169
- request('account/verify_credentials', :auth => true)
186
+ request('account/verify_credentials.xml', :auth => true)
170
187
  end
171
188
 
172
189
  private
@@ -184,28 +201,18 @@ module Twitter
184
201
  #
185
202
  # ie: call(:public_timeline, :auth => false)
186
203
  def call(method, options={})
187
- options.reverse_merge!({ :auth => true, :args => {} })
204
+ options = { :auth => true, :args => {} }.merge(options)
188
205
  # Following line needed as lite=false doesn't work in the API: http://tinyurl.com/yo3h5d
189
206
  options[:args].delete(:lite) unless options[:args][:lite]
190
207
  args = options.delete(:args)
191
208
  request(build_path("statuses/#{method.to_s}.xml", args), options)
192
209
  end
193
210
 
194
- # Makes a request to twitter.
195
- def request(path, options={})
196
- options.reverse_merge!({
197
- :headers => { "User-Agent" => @config[:email] },
198
- :method => :get
199
- })
200
- unless options[:since].blank?
201
- since = options[:since].kind_of?(Date) ? options[:since].strftime('%a, %d-%b-%y %T GMT') : options[:since].to_s
202
- options[:headers]["If-Modified-Since"] = since
203
- end
204
-
211
+ def response(path, options={})
205
212
  uri = URI.parse("http://#{@api_host}")
206
213
 
207
214
  begin
208
- response = Net::HTTP::Proxy(options[:proxy_host], options[:proxy_port]).start(uri.host, uri.port) do |http|
215
+ response = Net::HTTP::Proxy(@proxy_host, @proxy_port).start(uri.host, uri.port) do |http|
209
216
  klass = Net::HTTP.const_get options[:method].to_s.downcase.capitalize
210
217
  req = klass.new("#{uri.path}/#{path}", options[:headers])
211
218
  req.basic_auth(@config[:email], @config[:password]) if options[:auth]
@@ -217,7 +224,24 @@ module Twitter
217
224
  rescue => error
218
225
  raise CantConnect, error.message
219
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
220
240
 
241
+ handle_response!(response(path, options))
242
+ end
243
+
244
+ def handle_response!(response)
221
245
  if %w[200 304].include?(response.code)
222
246
  response = parse(response.body)
223
247
  raise RateExceeded if (response/:hash/:error).text =~ /Rate limit exceeded/
@@ -227,20 +251,23 @@ module Twitter
227
251
  elsif response.code == '401'
228
252
  raise CantConnect, 'Authentication failed. Check your username and password'
229
253
  elsif response.code == '403'
230
- # get an Hpricot document
231
- doc = parse(response.body)
232
- raise CantFindUsers, (doc/:hash/:error).text if (doc/:hash/:error).text =~ /Could not find both specified users/
233
- raise AlreadyFollowing, (doc/:hash/:error).text if (doc/:hash/:error).text =~ /already on your list/
234
- raise YouAreNotFriends, (doc/:hash/:error).text if (doc/:hash/:error).text =~ /You are not friends/
235
- raise RequestRefused, "Response code #{response.code}: #{response.message}"
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 YouAreNotFriends, error_message if error_message =~ /You are not friends/
258
+ raise RequestRefused, "Response code #{response.code}: #{response.message} #{error_message}"
236
259
  else
237
- raise CantConnect, "Twitter is returning a #{response.code}: #{response.message}"
260
+ raise CantConnect, "Twitter is returning a #{response.code}: #{response.message} #{(doc/:hash/:error).text}"
238
261
  end
239
- end
262
+ end
240
263
 
241
264
  # Given a path and a hash, build a full path with the hash turned into a query string
242
265
  def build_path(path, options)
243
- path += "?#{options.to_query}" unless options.blank?
266
+ unless options.nil?
267
+ query = options.inject('') { |str, h| str += "#{CGI.escape(h[0].to_s)}=#{CGI.escape(h[1].to_s)}&"; str }
268
+ path += "?#{query}"
269
+ end
270
+
244
271
  path
245
272
  end
246
273
 
@@ -255,4 +282,4 @@ module Twitter
255
282
  Hpricot.XML(response || '')
256
283
  end
257
284
  end
258
- end
285
+ end
data/lib/twitter/cli.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  require 'rubygems'
2
+
2
3
  gem 'main', '>= 2.8.2'
3
4
  gem 'highline', '>= 1.4.0'
4
- gem 'activerecord', '>= 2.1'
5
+ gem 'activerecord', '= 2.2.2'
5
6
  gem 'sqlite3-ruby', '>= 1.2.1'
7
+
6
8
  require 'main'
7
9
  require 'highline/import'
8
10
  require 'activerecord'
@@ -177,7 +179,7 @@ Main {
177
179
  end
178
180
  post_thread.join
179
181
  progress_thread.join
180
- say "Got it! New twitter created at: #{status.created_at}\n"
182
+ say "Got it! New tweet created at: #{status.created_at}\n"
181
183
  end
182
184
  end
183
185
  end
@@ -215,7 +217,7 @@ Main {
215
217
  end
216
218
 
217
219
  mode 'follow' do
218
- description "Allows you to turn on notifications for a user"
220
+ description "Allows you to add notifications for a user (aka Follow Them)"
219
221
  argument('username') {
220
222
  required
221
223
  description 'username or id of twitterrer to follow'
@@ -275,21 +277,25 @@ Main {
275
277
  option('force', 'f') {
276
278
  description "Ignore since_id and show first page of results even if there aren't new ones"
277
279
  }
280
+ option('reverse', 'r') {
281
+ description 'Reverse the output so the oldest tweets are at the top'
282
+ }
278
283
 
279
284
  def run
280
285
  do_work do
281
286
  timeline = params['timeline'].value == 'me' ? 'user' : params['timeline'].value
282
287
  options, since_id = {}, Configuration["#{timeline}_since_id"]
283
- options[:since_id] = since_id if !since_id.blank? && !params['force'].given?
288
+ options[:since_id] = since_id if !since_id.nil? && !params['force'].given?
289
+ reverse = params['reverse'].given? ? true : false
284
290
  cache = [:friends, :user].include?(timeline)
285
291
  collection = base.timeline(timeline.to_sym, options)
286
- output_tweets(collection, {:cache => cache, :since_prefix => timeline})
292
+ output_tweets(collection, {:cache => cache, :since_prefix => timeline, :reverse => reverse})
287
293
  end
288
294
  end
289
295
  end
290
296
 
291
297
  mode 'replies' do
292
- description 'Allows you to view all @replies at you'
298
+ description 'Allows you to view all @replies sent to you'
293
299
  option('force', 'f') {
294
300
  description "Ignore since_id and show first page of replies even if there aren't new ones"
295
301
  }
@@ -297,7 +303,7 @@ Main {
297
303
  def run
298
304
  do_work do
299
305
  options, since_id = {}, Configuration["replies_since_id"]
300
- options[:since_id] = since_id if !since_id.blank? && !params['force'].given?
306
+ options[:since_id] = since_id if !since_id.nil? && !params['force'].given?
301
307
  collection = base.replies(options)
302
308
  output_tweets(collection, {:since_prefix => 'replies'})
303
309
  end