t 0.9.4 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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