pjdavis-twitter 0.3.9 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,11 +1,24 @@
1
- 0.3.9 - December 19, 2008
2
- * Added page functionality for the rpp, letting you specify which page of a query to get back.
3
- * Fixed an error in the search specfile.
4
-
5
- 0.3.8 - October 03, 2008
6
- * added filter option to search, letting you specify options to filter by (i.e. links).
7
- * fixed issue where Fetch would overwrite the @query[:q] array with a string, raising an undefined method error.
8
1
 
2
+ 0.4.2 - February 10, 2009
3
+ * 1 minor enhancement
4
+ * Adding the Social Graph API methods (Josh Owens)
5
+
6
+ 0.4.1 - January 1, 2009
7
+ * 4 minor enhancements and 2 bug fixes:
8
+ * Added better exception handling (Billy Gray)
9
+ * Added page to search (Michael Ivey)
10
+ * Adding an option to display tweets on CLI in reverse order (oldest first). (Cameron Booth)
11
+ * Added in_reply_to_status_id option for replying to statuses (anthonycrumley)
12
+ * Fixed a bug where the @config was improperly set (K. Adam Christensen)
13
+ * Fix verify_credentials to include a format (breaks in laconica). (dustin)
14
+
15
+ 0.4.0 - December 23, 2008
16
+ * 3 major changes
17
+ * Removed active support as dependency
18
+ * Removed CLI dependencies from install dependency list
19
+ (they are now only installed by you manually)
20
+ * Switched to echoe for gem managment
21
+
9
22
  0.3.7 - August 26, 2008
10
23
  * Fixed source param not getting through
11
24
 
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,6 +24,9 @@ module Twitter
16
24
  class BadResponse < StandardError; end
17
25
  class UnknownTimeline < ArgumentError; end
18
26
  class RateExceeded < StandardError; end
27
+ class CantFindUsers < ArgumentError; end
28
+ class AlreadyFollowing < StandardError; end
29
+ class CantFollowUser < StandardError; end
19
30
 
20
31
  SourceName = 'twittergem'
21
32
  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.start(uri.host, 80) 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/
@@ -226,14 +250,23 @@ module Twitter
226
250
  raise Unavailable, response.message
227
251
  elsif response.code == '401'
228
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}"
229
258
  else
230
259
  raise CantConnect, "Twitter is returning a #{response.code}: #{response.message}"
231
260
  end
232
- end
261
+ end
233
262
 
234
263
  # Given a path and a hash, build a full path with the hash turned into a query string
235
264
  def build_path(path, options)
236
- path += "?#{options.to_query}" unless options.blank?
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
+
237
270
  path
238
271
  end
239
272
 
@@ -248,4 +281,4 @@ module Twitter
248
281
  Hpricot.XML(response || '')
249
282
  end
250
283
  end
251
- end
284
+ 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