t 0.9.4 → 0.9.5

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,5 @@
1
+ require 'oauth'
2
+
1
3
  module T
2
4
  module Authorizable
3
5
 
@@ -6,9 +6,6 @@ require 'active_support/core_ext/numeric/time'
6
6
  require 'csv'
7
7
  # 'fastercsv' required on Ruby versions < 1.9
8
8
  require 'fastercsv' unless Array.new.respond_to?(:to_csv)
9
- require 'geokit'
10
- require 'launchy'
11
- require 'oauth'
12
9
  require 'open-uri'
13
10
  require 't/authorizable'
14
11
  require 't/collectable'
@@ -83,6 +80,7 @@ module T
83
80
  ask "Press [Enter] to open the Twitter app authorization page."
84
81
  say
85
82
  end
83
+ require 'launchy'
86
84
  Launchy.open(url, :dry_run => options['display-url'])
87
85
  pin = ask "Paste in the supplied PIN:"
88
86
  access_token = request_token.get_access_token(:oauth_verifier => pin.chomp)
@@ -113,7 +111,7 @@ module T
113
111
  end
114
112
  users = users.threaded_map do |user|
115
113
  retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
116
- client.block(user, :include_entities => false)
114
+ client.block(user)
117
115
  end
118
116
  end
119
117
  number = users.length
@@ -129,7 +127,9 @@ module T
129
127
  method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
130
128
  def direct_messages
131
129
  count = options['number'] || DEFAULT_NUM_RESULTS
132
- direct_messages = client.direct_messages(:count => count, :include_entities => false)
130
+ direct_messages = collect_with_count(count) do |opts|
131
+ client.direct_messages(opts)
132
+ end
133
133
  direct_messages.reverse! if options['reverse']
134
134
  if options['csv']
135
135
  say ["ID", "Posted at", "Screen name", "Text"].to_csv unless direct_messages.empty?
@@ -163,7 +163,9 @@ module T
163
163
  method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
164
164
  def direct_messages_sent
165
165
  count = options['number'] || DEFAULT_NUM_RESULTS
166
- direct_messages = client.direct_messages_sent(:count => count, :include_entities => false)
166
+ direct_messages = collect_with_count(count) do |opts|
167
+ client.direct_messages_sent(opts)
168
+ end
167
169
  direct_messages.reverse! if options['reverse']
168
170
  if options['csv']
169
171
  say ["ID", "Posted at", "Screen name", "Text"].to_csv unless direct_messages.empty?
@@ -219,7 +221,7 @@ module T
219
221
  disciple_ids = (follower_ids - following_ids)
220
222
  users = disciple_ids.in_groups_of(MAX_USERS_PER_REQUEST, false).threaded_map do |disciple_id_group|
221
223
  retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
222
- client.users(disciple_id_group, :include_entities => false)
224
+ client.users(disciple_id_group)
223
225
  end
224
226
  end.flatten
225
227
  print_users(users)
@@ -233,7 +235,7 @@ module T
233
235
  else
234
236
  user.strip_ats
235
237
  end
236
- direct_message = client.direct_message_create(user, message, :include_entities => false)
238
+ direct_message = client.direct_message_create(user, message)
237
239
  say "Direct Message sent from @#{@rcfile.active_profile[0]} to @#{direct_message.recipient.screen_name} (#{time_ago_in_words(direct_message.created_at)} ago)."
238
240
  end
239
241
  map %w(d m) => :dm
@@ -247,7 +249,7 @@ module T
247
249
  owner = @rcfile.active_profile[0]
248
250
  else
249
251
  owner = if options['id']
250
- client.user(owner.to_i, :include_entities => false).screen_name
252
+ client.user(owner.to_i).screen_name
251
253
  else
252
254
  owner.strip_ats
253
255
  end
@@ -256,7 +258,7 @@ module T
256
258
  user = @rcfile.active_profile[0]
257
259
  else
258
260
  user = if options['id']
259
- user = client.user(user.to_i, :include_entities => false).screen_name
261
+ user = client.user(user.to_i).screen_name
260
262
  else
261
263
  user.strip_ats
262
264
  end
@@ -274,7 +276,7 @@ module T
274
276
  method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
275
277
  def does_follow(user1, user2=nil)
276
278
  user1 = if options['id']
277
- client.user(user1.to_i, :include_entities => false).screen_name
279
+ client.user(user1.to_i).screen_name
278
280
  else
279
281
  user1.strip_ats
280
282
  end
@@ -282,7 +284,7 @@ module T
282
284
  user2 = @rcfile.active_profile[0]
283
285
  else
284
286
  user2 = if options['id']
285
- client.user(user2.to_i, :include_entities => false).screen_name
287
+ client.user(user2.to_i).screen_name
286
288
  else
287
289
  user2.strip_ats
288
290
  end
@@ -302,7 +304,7 @@ module T
302
304
  status_ids.map!(&:strip_commas)
303
305
  favorites = status_ids.threaded_map do |status_id|
304
306
  retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
305
- client.favorite(status_id.to_i, :include_entities => false)
307
+ client.favorite(status_id.to_i)
306
308
  end
307
309
  end
308
310
  number = favorites.length
@@ -327,7 +329,9 @@ module T
327
329
  end
328
330
  end
329
331
  count = options['number'] || DEFAULT_NUM_RESULTS
330
- statuses = client.favorites(user, :count => count, :include_entities => false)
332
+ statuses = collect_with_count(count) do |opts|
333
+ client.favorites(user, opts)
334
+ end
331
335
  print_statuses(statuses)
332
336
  end
333
337
  map %w(faves favourites) => :favorites
@@ -343,7 +347,7 @@ module T
343
347
  end
344
348
  users = users.threaded_map do |user|
345
349
  retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
346
- client.follow(user, :include_entities => false)
350
+ client.follow(user)
347
351
  end
348
352
  end
349
353
  number = users.length
@@ -377,7 +381,7 @@ module T
377
381
  end
378
382
  users = following_ids.in_groups_of(MAX_USERS_PER_REQUEST, false).threaded_map do |following_id_group|
379
383
  retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
380
- client.users(following_id_group, :include_entities => false)
384
+ client.users(following_id_group)
381
385
  end
382
386
  end.flatten
383
387
  print_users(users)
@@ -408,7 +412,7 @@ module T
408
412
  end
409
413
  users = follower_ids.in_groups_of(MAX_USERS_PER_REQUEST, false).threaded_map do |follower_id_group|
410
414
  retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
411
- client.users(follower_id_group, :include_entities => false)
415
+ client.users(follower_id_group)
412
416
  end
413
417
  end.flatten
414
418
  print_users(users)
@@ -443,7 +447,7 @@ module T
443
447
  friend_ids = (following_ids & follower_ids)
444
448
  users = friend_ids.in_groups_of(MAX_USERS_PER_REQUEST, false).threaded_map do |friend_id_group|
445
449
  retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
446
- client.users(friend_id_group, :include_entities => false)
450
+ client.users(friend_id_group)
447
451
  end
448
452
  end.flatten
449
453
  print_users(users)
@@ -478,7 +482,7 @@ module T
478
482
  leader_ids = (following_ids - follower_ids)
479
483
  users = leader_ids.in_groups_of(MAX_USERS_PER_REQUEST, false).threaded_map do |leader_id_group|
480
484
  retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
481
- client.users(leader_id_group, :include_entities => false)
485
+ client.users(leader_id_group)
482
486
  end
483
487
  end.flatten
484
488
  print_users(users)
@@ -515,7 +519,9 @@ module T
515
519
  method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
516
520
  def mentions
517
521
  count = options['number'] || DEFAULT_NUM_RESULTS
518
- statuses = client.mentions(:count => count, :include_entities => false)
522
+ statuses = collect_with_count(count) do |opts|
523
+ client.mentions(opts)
524
+ end
519
525
  print_statuses(statuses)
520
526
  end
521
527
  map %w(replies) => :mentions
@@ -525,11 +531,12 @@ module T
525
531
  method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
526
532
  method_option "status", :aliases => "-s", :type => :boolean, :default => false, :desc => "Specify input as a Twitter status ID instead of a screen name."
527
533
  def open(user)
534
+ require 'launchy'
528
535
  if options['id']
529
- user = client.user(user.to_i, :include_entities => false)
536
+ user = client.user(user.to_i)
530
537
  Launchy.open("https://twitter.com/#{user.screen_name}", :dry_run => options['display-url'])
531
538
  elsif options['status']
532
- status = client.status(user.to_i, :include_entities => false, :include_my_retweet => false)
539
+ status = client.status(user.to_i, :include_my_retweet => false)
533
540
  Launchy.open("https://twitter.com/#{status.user.screen_name}/status/#{status.id}", :dry_run => options['display-url'])
534
541
  else
535
542
  Launchy.open("https://twitter.com/#{user.strip_ats}", :dry_run => options['display-url'])
@@ -541,7 +548,7 @@ module T
541
548
  method_option "location", :aliases => "-l", :type => :boolean, :default => false
542
549
  def reply(status_id, message)
543
550
  status_id = status_id.strip_commas
544
- status = client.status(status_id.to_i, :include_entities => false, :include_my_retweet => false)
551
+ status = client.status(status_id.to_i, :include_my_retweet => false)
545
552
  users = Array(status.user.screen_name)
546
553
  if options['all']
547
554
  # twitter-text requires $KCODE to be set to UTF8 on Ruby versions < 1.8
@@ -552,7 +559,7 @@ module T
552
559
  users.uniq!
553
560
  end
554
561
  users.map!(&:prepend_at)
555
- opts = {:in_reply_to_status_id => status.id, :include_entities => false, :trim_user => true}
562
+ opts = {:in_reply_to_status_id => status.id, :trim_user => true}
556
563
  opts.merge!(:lat => location.lat, :long => location.lng) if options['location']
557
564
  reply = client.update("#{users.join(' ')} #{message}", opts)
558
565
  say "Reply created by @#{@rcfile.active_profile[0]} to #{users.join(' ')} (#{time_ago_in_words(reply.created_at)} ago)."
@@ -571,7 +578,7 @@ module T
571
578
  end
572
579
  users = users.threaded_map do |user|
573
580
  retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
574
- client.report_spam(user, :include_entities => false)
581
+ client.report_spam(user)
575
582
  end
576
583
  end
577
584
  number = users.length
@@ -585,7 +592,7 @@ module T
585
592
  status_ids.map!(&:strip_commas)
586
593
  retweets = status_ids.threaded_map do |status_id|
587
594
  retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
588
- client.retweet(status_id.to_i, :include_entities => false, :trim_user => true)
595
+ client.retweet(status_id.to_i, :trim_user => true)
589
596
  end
590
597
  end
591
598
  number = retweets.length
@@ -610,7 +617,9 @@ module T
610
617
  end
611
618
  end
612
619
  count = options['number'] || DEFAULT_NUM_RESULTS
613
- statuses = client.retweeted_by(user, :count => count, :include_entities => false)
620
+ statuses = collect_with_count(count) do |opts|
621
+ client.retweeted_by(user, opts)
622
+ end
614
623
  print_statuses(statuses)
615
624
  end
616
625
  map %w(rts) => :retweets
@@ -624,18 +633,23 @@ module T
624
633
  method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
625
634
  def status(status_id)
626
635
  status_id = status_id.strip_commas
627
- status = client.status(status_id.to_i, :include_entities => false, :include_my_retweet => false)
628
- if status.geo
629
- geoloc = Geokit::Geocoders::MultiGeocoder.reverse_geocode(status.geo.coordinates)
630
- location = if geoloc.city && geoloc.state && geoloc.country
631
- [geoloc.city, geoloc.state, geoloc.country].join(", ")
632
- elsif geoloc.state && geoloc.country
633
- [geoloc.state, geoloc.country].join(", ")
636
+ status = client.status(status_id.to_i, :include_my_retweet => false)
637
+ location = if status.place
638
+ if status.place.name && status.place.attributes && status.place.attributes['street_address'] && status.place.attributes['locality'] && status.place.attributes['region'] && status.place.country
639
+ [status.place.name, status.place.attributes['street_address'], status.place.attributes['locality'], status.place.attributes['region'], status.place.country].join(", ")
640
+ elsif status.place.name && status.place.attributes && status.place.attributes['locality'] && status.place.attributes['region'] && status.place.country
641
+ [status.place.name, status.place.attributes['locality'], status.place.attributes['region'], status.place.country].join(", ")
642
+ elsif status.place.full_name && status.place.attributes && status.place.attributes['region'] && status.place.country
643
+ [status.place.full_name, status.place.attributes['region'], status.place.country].join(", ")
644
+ elsif status.place.full_name && status.place.country
645
+ [status.place.full_name, status.place.country].join(", ")
646
+ elsif status.place.full_name
647
+ status.place.full_name
634
648
  else
635
- geoloc.country
649
+ status.place.name
636
650
  end
637
- else
638
- location = nil
651
+ elsif status.geo
652
+ reverse_geocode(status.geo)
639
653
  end
640
654
  if options['csv']
641
655
  say ["ID", "Text", "Screen name", "Posted at", "Location", "Retweets", "Source", "URL"].to_csv
@@ -677,7 +691,7 @@ module T
677
691
  end
678
692
  end
679
693
  limit = options['number'] || DEFAULT_NUM_RESULTS
680
- users = client.recommendations(user, :limit => limit, :include_entities => false)
694
+ users = client.recommendations(user, :limit => limit)
681
695
  print_users(users)
682
696
  end
683
697
 
@@ -695,9 +709,13 @@ module T
695
709
  else
696
710
  user.strip_ats
697
711
  end
698
- statuses = client.user_timeline(user, :count => count, :include_entities => false)
712
+ statuses = collect_with_count(count) do |opts|
713
+ client.user_timeline(user, opts)
714
+ end
699
715
  else
700
- statuses = client.home_timeline(:count => count, :include_entities => false)
716
+ statuses = collect_with_count(count) do |opts|
717
+ client.home_timeline(opts)
718
+ end
701
719
  end
702
720
  print_statuses(statuses)
703
721
  end
@@ -766,7 +784,7 @@ module T
766
784
  end
767
785
  users = users.threaded_map do |user|
768
786
  retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
769
- client.unfollow(user, :include_entities => false)
787
+ client.unfollow(user)
770
788
  end
771
789
  end
772
790
  number = users.length
@@ -778,7 +796,7 @@ module T
778
796
  desc "update MESSAGE", "Post a Tweet."
779
797
  method_option "location", :aliases => "-l", :type => :boolean, :default => false
780
798
  def update(message)
781
- opts = {:include_entities => false, :trim_user => true}
799
+ opts = {:trim_user => true}
782
800
  opts.merge!(:lat => location.lat, :long => location.lng) if options['location']
783
801
  status = client.update(message, opts)
784
802
  say "Tweet created by @#{@rcfile.active_profile[0]} (#{time_ago_in_words(status.created_at)} ago)."
@@ -806,7 +824,7 @@ module T
806
824
  else
807
825
  users.map!(&:strip_ats)
808
826
  end
809
- users = client.users(users, :include_entities => false)
827
+ users = client.users(users)
810
828
  print_users(users)
811
829
  end
812
830
  map %w(stats) => :users
@@ -826,7 +844,7 @@ module T
826
844
  else
827
845
  user.strip_ats
828
846
  end
829
- user = client.user(user, :include_entities => false)
847
+ user = client.user(user)
830
848
  if options['csv']
831
849
  say ["ID", "Verified", "Name", "Screen name", "Bio", "Location", "Following", "Last update", "Lasted updated at", "Since", "Tweets", "Favorites", "Listed", "Following", "Followers", "URL"].to_csv
832
850
  say [user.id, user.verified?, user.name, user.screen_name, user.description, user.location, user.following?, HTMLEntities.new.decode(user.status.text), user.status.created_at.utc.strftime("%Y-%m-%d %H:%M:%S %z"), user.created_at.utc.strftime("%Y-%m-%d %H:%M:%S %z"), user.statuses_count, user.favourites_count, user.listed_count, user.friends_count, user.followers_count, user.url].to_csv
@@ -872,11 +890,24 @@ module T
872
890
 
873
891
  def location
874
892
  return @location if @location
893
+ require 'geokit'
875
894
  ip_address = Kernel::open("http://checkip.dyndns.org/") do |body|
876
895
  /(?:\d{1,3}\.){3}\d{1,3}/.match(body.read)[0]
877
896
  end
878
897
  @location = Geokit::Geocoders::MultiGeocoder.geocode(ip_address)
879
898
  end
880
899
 
900
+ def reverse_geocode(geo)
901
+ require 'geokit'
902
+ geoloc = Geokit::Geocoders::MultiGeocoder.reverse_geocode(geo.coordinates)
903
+ if geoloc.city && geoloc.state && geoloc.country
904
+ [geoloc.city, geoloc.state, geoloc.country].join(", ")
905
+ elsif geoloc.state && geoloc.country
906
+ [geoloc.state, geoloc.country].join(", ")
907
+ else
908
+ geoloc.country
909
+ end
910
+ end
911
+
881
912
  end
882
913
  end
@@ -1,6 +1,12 @@
1
1
  module T
2
2
  module Collectable
3
3
 
4
+ MAX_NUM_RESULTS = 200
5
+
6
+ def collect_with_count(count, &block)
7
+ collect_with_number(count, :count, &block)
8
+ end
9
+
4
10
  def collect_with_cursor(collection=[], cursor=-1, &block)
5
11
  object = yield cursor
6
12
  collection += object.collection
@@ -9,9 +15,33 @@ module T
9
15
 
10
16
  def collect_with_max_id(collection=[], max_id=nil, &block)
11
17
  array = yield max_id
18
+ return collection unless !array.nil?
12
19
  collection += array
13
20
  array.empty? ? collection : collect_with_max_id(collection, array.last.id - 1, &block)
14
21
  end
15
22
 
23
+ def collect_with_number(number, key, &block)
24
+ opts = {}
25
+ opts[key] = MAX_NUM_RESULTS
26
+ statuses = collect_with_max_id do |max_id|
27
+ opts[:max_id] = max_id unless max_id.nil?
28
+ opts[key] = number unless number >= MAX_NUM_RESULTS
29
+ if number > 0
30
+ number -= MAX_NUM_RESULTS
31
+ retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
32
+ yield opts
33
+ end
34
+ end
35
+ end.flatten.compact
36
+ end
37
+
38
+ def collect_with_per_page(per_page, &block)
39
+ collect_with_number(per_page, :per_page, &block)
40
+ end
41
+
42
+ def collect_with_rpp(rpp, &block)
43
+ collect_with_number(rpp, :rpp, &block)
44
+ end
45
+
16
46
  end
17
47
  end
@@ -27,7 +27,7 @@ module T
27
27
  end
28
28
  users = users.threaded_map do |user|
29
29
  retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
30
- client.unblock(user, :include_entities => false)
30
+ client.unblock(user)
31
31
  end
32
32
  end
33
33
  number = users.length
@@ -43,10 +43,10 @@ module T
43
43
  direct_message_ids.map!(&:strip_commas)
44
44
  direct_message_ids.each do |direct_message_id|
45
45
  unless options['force']
46
- direct_message = client.direct_message(direct_message_id.to_i, :include_entities => false)
46
+ direct_message = client.direct_message(direct_message_id.to_i)
47
47
  return unless yes? "Are you sure you want to permanently delete the direct message to @#{direct_message.recipient.screen_name}: \"#{direct_message.text}\"? [y/N]"
48
48
  end
49
- direct_message = client.direct_message_destroy(direct_message_id.to_i, :include_entities => false)
49
+ direct_message = client.direct_message_destroy(direct_message_id.to_i)
50
50
  say "@#{@rcfile.active_profile[0]} deleted the direct message sent to @#{direct_message.recipient.screen_name}: \"#{direct_message.text}\""
51
51
  end
52
52
  end
@@ -59,10 +59,10 @@ module T
59
59
  status_ids.map!(&:strip_commas)
60
60
  status_ids.each do |status_id|
61
61
  unless options['force']
62
- status = client.status(status_id.to_i, :include_entities => false, :include_my_retweet => false, :trim_user => true)
62
+ status = client.status(status_id.to_i, :include_my_retweet => false, :trim_user => true)
63
63
  return unless yes? "Are you sure you want to remove @#{status.user.screen_name}'s status: \"#{status.text}\" from your favorites? [y/N]"
64
64
  end
65
- status = client.unfavorite(status_id.to_i, :include_entities => false)
65
+ status = client.unfavorite(status_id.to_i)
66
66
  say "@#{@rcfile.active_profile[0]} unfavorited @#{status.user.screen_name}'s status: \"#{status.text}\""
67
67
  end
68
68
  end
@@ -88,10 +88,10 @@ module T
88
88
  status_ids.map!(&:strip_commas)
89
89
  status_ids.each do |status_id|
90
90
  unless options['force']
91
- status = client.status(status_id.to_i, :include_entities => false, :include_my_retweet => false, :trim_user => true)
91
+ status = client.status(status_id.to_i, :include_my_retweet => false, :trim_user => true)
92
92
  return unless yes? "Are you sure you want to permanently delete @#{status.user.screen_name}'s status: \"#{status.text}\"? [y/N]"
93
93
  end
94
- status = client.status_destroy(status_id.to_i, :include_entities => false, :trim_user => true)
94
+ status = client.status_destroy(status_id.to_i, :trim_user => true)
95
95
  say "@#{@rcfile.active_profile[0]} deleted the status: \"#{status.text}\""
96
96
  end
97
97
  end
@@ -126,7 +126,7 @@ module T
126
126
  end
127
127
  end
128
128
  users = collect_with_cursor do |cursor|
129
- client.list_members(owner, list, :cursor => cursor, :include_entities => false, :skip_status => true)
129
+ client.list_members(owner, list, :cursor => cursor, :skip_status => true)
130
130
  end
131
131
  print_users(users)
132
132
  end
@@ -174,7 +174,9 @@ module T
174
174
  end
175
175
  end
176
176
  per_page = options['number'] || DEFAULT_NUM_RESULTS
177
- statuses = client.list_timeline(owner, list, :include_entities => false, :per_page => per_page)
177
+ statuses = collect_with_per_page(per_page) do |opts|
178
+ client.list_timeline(owner, list, opts)
179
+ end
178
180
  print_statuses(statuses)
179
181
  end
180
182
  map %w(tl) => :timeline