t 0.8.3 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -121,6 +121,9 @@ follows you:
121
121
  ### Output the last 200 tweets in your timeline to a CSV file
122
122
  t timeline -n 200 --csv > timeline.csv
123
123
 
124
+ ### Start streaming a real-time sample of all Tweets (Control-C to stop)
125
+ t stream all
126
+
124
127
  ### Count the number of employees who work for Twitter
125
128
  t list members twitter team | wc -l
126
129
 
@@ -155,7 +158,7 @@ follows you:
155
158
  like grep, cut, awk, bc, wc, and xargs for advanced text processing.
156
159
  * Generate spreadsheets: Convert the output of any command to CSV format simply
157
160
  by adding the `--csv` flag.
158
- * 98% C0 Code Coverage: Well tested, with a 3:1 test-to-code ratio.
161
+ * 95% C0 Code Coverage: Well tested, with a 2.5:1 test-to-code ratio.
159
162
 
160
163
  [search]: https://dev.twitter.com/docs/using-search
161
164
 
@@ -210,7 +213,7 @@ Here are some ways *you* can contribute:
210
213
  * by writing code (**no patch is too small**: fix typos, add comments, clean up
211
214
  inconsistent whitespace)
212
215
  * by refactoring code
213
- * by closing [issues][]
216
+ * by fixing [issues][]
214
217
  * by reviewing patches
215
218
 
216
219
  [issues]: https://github.com/sferik/t/issues
@@ -218,11 +221,10 @@ Here are some ways *you* can contribute:
218
221
  ## Submitting an Issue
219
222
  We use the [GitHub issue tracker][issues] to track bugs and features. Before
220
223
  submitting a bug report or feature request, check to make sure it hasn't
221
- already been submitted. You can indicate support for an existing issue by
222
- voting it up. When submitting a bug report, please include a [Gist][] that
223
- includes a stack trace and any details that may be necessary to reproduce the
224
- bug, including your gem version, Ruby version, and operating system. Ideally, a
225
- bug report should include a pull request with failing specs.
224
+ already been submitted. When submitting a bug report, please include a [Gist][]
225
+ that includes a stack trace and any details that may be necessary to reproduce
226
+ the bug, including your gem version, Ruby version, and operating system.
227
+ Ideally, a bug report should include a pull request with failing specs.
226
228
 
227
229
  [gist]: https://gist.github.com/
228
230
 
@@ -235,8 +237,8 @@ bug report should include a pull request with failing specs.
235
237
  6. Run `bundle exec rake spec`. If your specs fail, return to step 5.
236
238
  7. Run `open coverage/index.html`. If your changes are not completely covered
237
239
  by your tests, return to step 3.
238
- 6. Add, commit, and push your changes.
239
- 7. [Submit a pull request.][pr]
240
+ 8. Add, commit, and push your changes.
241
+ 9. [Submit a pull request.][pr]
240
242
 
241
243
  [fork]: http://help.github.com/fork-a-repo/
242
244
  [branch]: http://learn.github.com/p/branching.html
@@ -0,0 +1,27 @@
1
+ module T
2
+ module Authorizable
3
+
4
+ def consumer
5
+ OAuth::Consumer.new(
6
+ options['consumer-key'],
7
+ options['consumer-secret'],
8
+ :site => base_url
9
+ )
10
+ end
11
+
12
+ def generate_authorize_url(request_token)
13
+ request = consumer.create_signed_request(:get, consumer.authorize_path, request_token, pin_auth_parameters)
14
+ params = request['Authorization'].sub(/^OAuth\s+/, '').split(/,\s+/).map do |param|
15
+ key, value = param.split('=')
16
+ value =~ /"(.*?)"/
17
+ "#{key}=#{CGI::escape($1)}"
18
+ end.join('&')
19
+ "#{base_url}#{request.path}?#{params}"
20
+ end
21
+
22
+ def pin_auth_parameters
23
+ {:oauth_callback => 'oob'}
24
+ end
25
+
26
+ end
27
+ end
data/lib/t/cli.rb CHANGED
@@ -10,6 +10,7 @@ require 'geokit'
10
10
  require 'launchy'
11
11
  require 'oauth'
12
12
  require 'open-uri'
13
+ require 't/authorizable'
13
14
  require 't/collectable'
14
15
  require 't/core_ext/string'
15
16
  require 't/delete'
@@ -19,6 +20,7 @@ require 't/rcfile'
19
20
  require 't/requestable'
20
21
  require 't/search'
21
22
  require 't/set'
23
+ require 't/stream'
22
24
  require 't/version'
23
25
  require 'thor'
24
26
  require 'time'
@@ -35,6 +37,7 @@ module T
35
37
  include ActionView::Helpers::DateHelper
36
38
  include ActionView::Helpers::NumberHelper
37
39
  include ActionView::Helpers::TextHelper
40
+ include T::Authorizable
38
41
  include T::Collectable
39
42
  include T::Printable
40
43
  include T::Requestable
@@ -142,7 +145,7 @@ module T
142
145
  elsif options['long']
143
146
  array = direct_messages.map do |direct_message|
144
147
  created_at = direct_message.created_at > 6.months.ago ? direct_message.created_at.strftime("%b %e %H:%M") : direct_message.created_at.strftime("%b %e %Y")
145
- [direct_message.id, created_at, "@#{direct_message.sender.screen_name}", direct_message.text.gsub(/\n+/, ' ')]
148
+ [direct_message.id, created_at, "@#{direct_message.sender.screen_name}", HTMLEntities.new.decode(direct_message.text).gsub(/\n+/, ' ')]
146
149
  end
147
150
  if STDOUT.tty?
148
151
  headings = ["ID", "Posted at", "Screen name", "Text"]
@@ -176,7 +179,7 @@ module T
176
179
  elsif options['long']
177
180
  array = direct_messages.map do |direct_message|
178
181
  created_at = direct_message.created_at > 6.months.ago ? direct_message.created_at.strftime("%b %e %H:%M") : direct_message.created_at.strftime("%b %e %Y")
179
- [direct_message.id, created_at, "@#{direct_message.recipient.screen_name}", direct_message.text.gsub(/\n+/, ' ')]
182
+ [direct_message.id, created_at, "@#{direct_message.recipient.screen_name}", HTMLEntities.new.decode(direct_message.text).gsub(/\n+/, ' ')]
180
183
  end
181
184
  if STDOUT.tty?
182
185
  headings = ["ID", "Posted at", "Screen name", "Text"]
@@ -199,7 +202,7 @@ module T
199
202
  method_option "followers", :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
200
203
  method_option "friends", :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
201
204
  method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
202
- method_option "listed", :aliases => "-s", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
205
+ method_option "listed", :aliases => "-d", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
203
206
  method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
204
207
  method_option "posted", :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
205
208
  method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
@@ -361,7 +364,7 @@ module T
361
364
  method_option "followers", :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
362
365
  method_option "friends", :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
363
366
  method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
364
- method_option "listed", :aliases => "-s", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
367
+ method_option "listed", :aliases => "-d", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
365
368
  method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
366
369
  method_option "posted", :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
367
370
  method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
@@ -392,7 +395,7 @@ module T
392
395
  method_option "followers", :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
393
396
  method_option "friends", :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
394
397
  method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
395
- method_option "listed", :aliases => "-s", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
398
+ method_option "listed", :aliases => "-d", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
396
399
  method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
397
400
  method_option "posted", :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
398
401
  method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
@@ -423,7 +426,7 @@ module T
423
426
  method_option "followers", :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
424
427
  method_option "friends", :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
425
428
  method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
426
- method_option "listed", :aliases => "-s", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
429
+ method_option "listed", :aliases => "-d", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
427
430
  method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
428
431
  method_option "posted", :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
429
432
  method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
@@ -458,7 +461,7 @@ module T
458
461
  method_option "followers", :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
459
462
  method_option "friends", :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
460
463
  method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
461
- method_option "listed", :aliases => "-s", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
464
+ method_option "listed", :aliases => "-d", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
462
465
  method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
463
466
  method_option "posted", :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
464
467
  method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
@@ -636,11 +639,11 @@ module T
636
639
  end
637
640
  if options['csv']
638
641
  say ["ID", "Text", "Screen name", "Posted at", "Location", "Retweets", "Source", "URL"].to_csv
639
- say [status.id, status.text, status.user.screen_name, status.created_at.utc.strftime("%Y-%m-%d %H:%M:%S %z"), location, status.retweet_count, strip_tags(status.source), "https://twitter.com/#{status.user.screen_name}/status/#{status.id}"].to_csv
642
+ say [status.id, HTMLEntities.new.decode(status.text), status.user.screen_name, status.created_at.utc.strftime("%Y-%m-%d %H:%M:%S %z"), location, status.retweet_count, strip_tags(status.source), "https://twitter.com/#{status.user.screen_name}/status/#{status.id}"].to_csv
640
643
  else
641
644
  array = []
642
645
  array << ["ID", status.id.to_s]
643
- array << ["Text", status.text.gsub(/\n+/, ' ')]
646
+ array << ["Text", HTMLEntities.new.decode(status.text).gsub(/\n+/, ' ')]
644
647
  array << ["Screen name", "@#{status.user.screen_name}"]
645
648
  posted_at = status.created_at > 6.months.ago ? status.created_at.strftime("%b %e %H:%M") : status.created_at.strftime("%b %e %Y")
646
649
  array << ["Posted at", posted_at]
@@ -658,7 +661,7 @@ module T
658
661
  method_option "followers", :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
659
662
  method_option "friends", :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
660
663
  method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
661
- method_option "listed", :aliases => "-s", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
664
+ method_option "listed", :aliases => "-d", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
662
665
  method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
663
666
  method_option "number", :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS, :desc => "Limit the number of results."
664
667
  method_option "posted", :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
@@ -790,7 +793,7 @@ module T
790
793
  method_option "followers", :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
791
794
  method_option "friends", :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
792
795
  method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify input as Twitter user IDs instead of screen names."
793
- method_option "listed", :aliases => "-s", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
796
+ method_option "listed", :aliases => "-d", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
794
797
  method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
795
798
  method_option "posted", :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
796
799
  method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
@@ -826,7 +829,7 @@ module T
826
829
  user = client.user(user, :include_entities => false)
827
830
  if options['csv']
828
831
  say ["ID", "Verified", "Name", "Screen name", "Bio", "Location", "Following", "Last update", "Lasted updated at", "Since", "Tweets", "Favorites", "Listed", "Following", "Followers", "URL"].to_csv
829
- say [user.id, user.verified?, user.name, user.screen_name, user.description, user.location, user.following?, 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
832
+ 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
830
833
  else
831
834
  array = []
832
835
  name_label = user.verified ? "Name (Verified)" : "Name"
@@ -836,7 +839,7 @@ module T
836
839
  array << ["Location", user.location] unless user.location.nil?
837
840
  status = user.following ? "Following" : "Not following"
838
841
  array << ["Status", status]
839
- array << ["Last update", "#{user.status.text.gsub(/\n+/, ' ')} (#{time_ago_in_words(user.status.created_at)} ago)"] unless user.status.nil?
842
+ array << ["Last update", "#{HTMLEntities.new.decode(user.status.text).gsub(/\n+/, ' ')} (#{time_ago_in_words(user.status.created_at)} ago)"] unless user.status.nil?
840
843
  created_at = user.created_at > 6.months.ago ? user.created_at.strftime("%b %e %H:%M") : user.created_at.strftime("%b %e %Y")
841
844
  array << ["Since", created_at]
842
845
  array << ["Tweets", number_with_delimiter(user.statuses_count)]
@@ -862,25 +865,10 @@ module T
862
865
  desc "set SUBCOMMAND ...ARGS", "Change various account settings."
863
866
  subcommand 'set', T::Set
864
867
 
865
- private
866
-
867
- def consumer
868
- OAuth::Consumer.new(
869
- options['consumer-key'],
870
- options['consumer-secret'],
871
- :site => base_url
872
- )
873
- end
868
+ desc "stream SUBCOMMAND ...ARGS", "Commands for streaming Tweets."
869
+ subcommand 'stream', T::Stream
874
870
 
875
- def generate_authorize_url(request_token)
876
- request = consumer.create_signed_request(:get, consumer.authorize_path, request_token, pin_auth_parameters)
877
- params = request['Authorization'].sub(/^OAuth\s+/, '').split(/,\s+/).map do |param|
878
- key, value = param.split('=')
879
- value =~ /"(.*?)"/
880
- "#{key}=#{CGI::escape($1)}"
881
- end.join('&')
882
- "#{base_url}#{request.path}?#{params}"
883
- end
871
+ private
884
872
 
885
873
  def location
886
874
  return @location if @location
@@ -890,9 +878,5 @@ module T
890
878
  @location = Geokit::Geocoders::MultiGeocoder.geocode(ip_address)
891
879
  end
892
880
 
893
- def pin_auth_parameters
894
- {:oauth_callback => 'oob'}
895
- end
896
-
897
881
  end
898
882
  end
data/lib/t/list.rb CHANGED
@@ -107,7 +107,7 @@ module T
107
107
  method_option "followers", :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by total number of followers."
108
108
  method_option "friends", :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by total number of friends."
109
109
  method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
110
- method_option "listed", :aliases => "-s", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
110
+ method_option "listed", :aliases => "-d", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
111
111
  method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
112
112
  method_option "posted", :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
113
113
  method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
data/lib/t/printable.rb CHANGED
@@ -3,6 +3,7 @@ require 'csv'
3
3
  # 'fastercsv' required on Ruby versions < 1.9
4
4
  require 'fastercsv' unless Array.new.respond_to?(:to_csv)
5
5
  require 'highline'
6
+ require 'htmlentities'
6
7
  require 'thor/shell/color'
7
8
 
8
9
  module T
@@ -14,6 +15,36 @@ module T
14
15
 
15
16
  private
16
17
 
18
+ def build_long_list(list)
19
+ created_at = list.created_at > 6.months.ago ? list.created_at.strftime("%b %e %H:%M") : list.created_at.strftime("%b %e %Y")
20
+ [list.id, created_at, list.full_name, number_with_delimiter(list.member_count), number_with_delimiter(list.subscriber_count), list.mode, list.description]
21
+ end
22
+
23
+ def build_long_status(status)
24
+ created_at = status.created_at > 6.months.ago ? status.created_at.strftime("%b %e %H:%M") : status.created_at.strftime("%b %e %Y")
25
+ [status.id, created_at, "@#{status.user.screen_name}", HTMLEntities.new.decode(status.text).gsub(/\n+/, ' ')]
26
+ end
27
+
28
+ def build_long_user(user)
29
+ created_at = user.created_at > 6.months.ago ? user.created_at.strftime("%b %e %H:%M") : user.created_at.strftime("%b %e %Y")
30
+ [user.id, created_at, number_with_delimiter(user.statuses_count), number_with_delimiter(user.favourites_count), number_with_delimiter(user.listed_count), number_with_delimiter(user.friends_count), number_with_delimiter(user.followers_count), "@#{user.screen_name}", user.name]
31
+ end
32
+
33
+ def print_csv_list(list)
34
+ created_at = list.created_at.utc.strftime("%Y-%m-%d %H:%M:%S %z")
35
+ say [list.id, created_at, list.user.screen_name, list.slug, list.member_count, list.subscriber_count, list.mode, list.description].to_csv
36
+ end
37
+
38
+ def print_csv_status(status)
39
+ created_at = status.created_at.utc.strftime("%Y-%m-%d %H:%M:%S %z")
40
+ say [status.id, created_at, status.user.screen_name, HTMLEntities.new.decode(status.text)].to_csv
41
+ end
42
+
43
+ def print_csv_user(user)
44
+ created_at = user.created_at.utc.strftime("%Y-%m-%d %H:%M:%S %z")
45
+ say [user.id, created_at, user.statuses_count, user.favourites_count, user.listed_count, user.friends_count, user.followers_count, user.screen_name, user.name].to_csv
46
+ end
47
+
17
48
  def print_in_columns(array)
18
49
  cols = HighLine::SystemExtensions.terminal_size[0]
19
50
  width = (array.map{|el| el.to_s.size}.max || 0) + 2
@@ -39,12 +70,11 @@ module T
39
70
  if options['csv']
40
71
  say ["ID", "Created at", "Screen name", "Slug", "Members", "Subscribers", "Mode", "Description"].to_csv unless lists.empty?
41
72
  lists.each do |list|
42
- say [list.id, list.created_at.utc.strftime("%Y-%m-%d %H:%M:%S %z"), list.user.screen_name, list.slug, list.member_count, list.subscriber_count, list.mode, list.description].to_csv
73
+ print_csv_list(list)
43
74
  end
44
75
  elsif options['long']
45
76
  array = lists.map do |list|
46
- created_at = list.created_at > 6.months.ago ? list.created_at.strftime("%b %e %H:%M") : list.created_at.strftime("%b %e %Y")
47
- [list.id, created_at, list.full_name, number_with_delimiter(list.member_count), number_with_delimiter(list.subscriber_count), list.mode, list.description]
77
+ build_long_list(list)
48
78
  end
49
79
  if STDOUT.tty?
50
80
  headings = ["ID", "Created at", "Slug", "Members", "Subscribers", "Mode", "Description"]
@@ -64,17 +94,27 @@ module T
64
94
  end
65
95
  end
66
96
 
97
+ def print_status(status)
98
+ if STDOUT.tty? && !options['no-color']
99
+ say(" #{Thor::Shell::Color::BOLD}@#{status.user.screen_name}", :yellow)
100
+ print_wrapped(HTMLEntities.new.decode(status.text), :indent => 3)
101
+ else
102
+ say(" @#{status.user.screen_name}")
103
+ print_wrapped(HTMLEntities.new.decode(status.text), :indent => 3)
104
+ end
105
+ say
106
+ end
107
+
67
108
  def print_statuses(statuses)
68
- statuses.reverse! if options['reverse']
109
+ statuses.reverse! if options['reverse'] || options['stream']
69
110
  if options['csv']
70
111
  say ["ID", "Posted at", "Screen name", "Text"].to_csv unless statuses.empty?
71
112
  statuses.each do |status|
72
- say [status.id, status.created_at.utc.strftime("%Y-%m-%d %H:%M:%S %z"), status.user.screen_name, status.text].to_csv
113
+ print_csv_status(status)
73
114
  end
74
115
  elsif options['long']
75
116
  array = statuses.map do |status|
76
- created_at = status.created_at > 6.months.ago ? status.created_at.strftime("%b %e %H:%M") : status.created_at.strftime("%b %e %Y")
77
- [status.id, created_at, "@#{status.user.screen_name}", status.text.gsub(/\n+/, ' ')]
117
+ build_long_status(status)
78
118
  end
79
119
  if STDOUT.tty?
80
120
  headings = ["ID", "Posted at", "Screen name", "Text"]
@@ -84,22 +124,8 @@ module T
84
124
  print_table(array)
85
125
  end
86
126
  else
87
- if STDOUT.tty? && !options['no-color']
88
- say unless statuses.empty?
89
- statuses.each do |status|
90
- say(" #{Thor::Shell::Color::BOLD}@#{status.user.screen_name}", :yellow)
91
- print_wrapped(status.text, :indent => 3)
92
- say(" #{Thor::Shell::Color::BOLD}#{time_ago_in_words(status.created_at)} ago", :black)
93
- say
94
- end
95
- else
96
- say unless statuses.empty?
97
- statuses.each do |status|
98
- say(" @#{status.user.screen_name}")
99
- print_wrapped(status.text, :indent => 3)
100
- say(" #{time_ago_in_words(status.created_at)} ago")
101
- say
102
- end
127
+ statuses.each do |status|
128
+ print_status(status)
103
129
  end
104
130
  end
105
131
  end
@@ -123,12 +149,11 @@ module T
123
149
  if options['csv']
124
150
  say ["ID", "Since", "Tweets", "Favorites", "Listed", "Following", "Followers", "Screen name", "Name"].to_csv unless users.empty?
125
151
  users.each do |user|
126
- say [user.id, 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.screen_name, user.name].to_csv
152
+ print_csv_user(user)
127
153
  end
128
154
  elsif options['long']
129
155
  array = users.map do |user|
130
- created_at = user.created_at > 6.months.ago ? user.created_at.strftime("%b %e %H:%M") : user.created_at.strftime("%b %e %Y")
131
- [user.id, created_at, number_with_delimiter(user.statuses_count), number_with_delimiter(user.favourites_count), number_with_delimiter(user.listed_count), number_with_delimiter(user.friends_count), number_with_delimiter(user.followers_count), "@#{user.screen_name}", user.name]
156
+ build_long_user(user)
132
157
  end
133
158
  if STDOUT.tty?
134
159
  headings = ["ID", "Since", "Tweets", "Favorites", "Listed", "Following", "Followers", "Screen name", "Name"]
data/lib/t/search.rb CHANGED
@@ -2,6 +2,7 @@ require 'action_view'
2
2
  require 'csv'
3
3
  # 'fastercsv' required on Ruby versions < 1.9
4
4
  require 'fastercsv' unless Array.new.respond_to?(:to_csv)
5
+ require 'htmlentities'
5
6
  require 'retryable'
6
7
  require 't/collectable'
7
8
  require 't/printable'
@@ -37,12 +38,12 @@ module T
37
38
  if options['csv']
38
39
  say ["ID", "Posted at", "Screen name", "Text"].to_csv unless statuses.empty?
39
40
  statuses.each do |status|
40
- say [status.id, status.created_at.utc.strftime("%Y-%m-%d %H:%M:%S %z"), status.from_user, status.text].to_csv
41
+ say [status.id, status.created_at.utc.strftime("%Y-%m-%d %H:%M:%S %z"), status.from_user, HTMLEntities.new.decode(status.text)].to_csv
41
42
  end
42
43
  elsif options['long']
43
44
  array = statuses.map do |status|
44
45
  created_at = status.created_at > 6.months.ago ? status.created_at.strftime("%b %e %H:%M") : status.created_at.strftime("%b %e %Y")
45
- [status.id, created_at, "@#{status.from_user}", status.text.gsub(/\n+/, ' ')]
46
+ [status.id, created_at, "@#{status.from_user}", HTMLEntities.new.decode(status.text).gsub(/\n+/, ' ')]
46
47
  end
47
48
  if STDOUT.tty?
48
49
  headings = ["ID", "Posted at", "Screen name", "Text"]
@@ -52,13 +53,21 @@ module T
52
53
  print_table(array)
53
54
  end
54
55
  else
56
+ say unless statuses.empty?
55
57
  statuses.each do |status|
56
- say "#{status.from_user.rjust(MAX_SCREEN_NAME_SIZE)}: #{status.text.gsub(/\n+/, ' ')} (#{time_ago_in_words(status.created_at)} ago)"
58
+ if STDOUT.tty? && !options['no-color']
59
+ say(" #{Thor::Shell::Color::BOLD}@#{status.from_user}", :yellow)
60
+ print_wrapped(HTMLEntities.new.decode(status.text), :indent => 3)
61
+ else
62
+ say(" @#{status.from_user}")
63
+ print_wrapped(HTMLEntities.new.decode(status.text), :indent => 3)
64
+ end
65
+ say
57
66
  end
58
67
  end
59
68
  end
60
69
 
61
- desc "favorites QUERY", "Returns Tweets you've favorited that mach a specified query."
70
+ desc "favorites QUERY", "Returns Tweets you've favorited that match a specified query."
62
71
  method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
63
72
  method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
64
73
  def favorites(query)
@@ -76,7 +85,36 @@ module T
76
85
  end
77
86
  map %w(faves) => :favorites
78
87
 
79
- desc "mentions QUERY", "Returns Tweets mentioning you that mach a specified query."
88
+ desc "list [USER/]LIST QUERY", "Returns Tweets on a list that match specified query."
89
+ method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
90
+ method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
91
+ method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
92
+ def list(list, query)
93
+ owner, list = list.split('/')
94
+ if list.nil?
95
+ list = owner
96
+ owner = @rcfile.active_profile[0]
97
+ else
98
+ owner = if options['id']
99
+ owner.to_i
100
+ else
101
+ owner.strip_ats
102
+ end
103
+ end
104
+ opts = {:count => MAX_NUM_RESULTS}
105
+ statuses = collect_with_max_id do |max_id|
106
+ opts[:max_id] = max_id unless max_id.nil?
107
+ retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
108
+ client.list_timeline(owner, list, opts)
109
+ end
110
+ end.flatten.compact
111
+ statuses = statuses.select do |status|
112
+ /#{query}/i.match(status.text)
113
+ end
114
+ print_statuses(statuses)
115
+ end
116
+
117
+ desc "mentions QUERY", "Returns Tweets mentioning you that match a specified query."
80
118
  method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
81
119
  method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
82
120
  def mentions(query)
@@ -94,7 +132,7 @@ module T
94
132
  end
95
133
  map %w(replies) => :mentions
96
134
 
97
- desc "retweets QUERY", "Returns Tweets you've retweeted that mach a specified query."
135
+ desc "retweets QUERY", "Returns Tweets you've retweeted that match a specified query."
98
136
  method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
99
137
  method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
100
138
  def retweets(query)