t 0.8.3 → 0.9.0

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.
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)