t 2.2.0 → 2.2.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a30a59d381d4a4e6815086c4e45d1541fb372855
4
+ data.tar.gz: 300d0fedac30548d0fea628774e4b9a632716017
5
+ SHA512:
6
+ metadata.gz: 09c862bc9a765df7f652ea0820631c8d284266df0a87af3a9840694c9a1ce75cdae8e143c8312eba5cae686194185602ffe2b90e9f7d0633c80c3c43b0c8bab7
7
+ data.tar.gz: 15970f41bc2c623d6040eeb3fb7cc721ac67742b4697ec4826e6d1d8123525b4e9e039a6485299b545c625482cd77c1cde917146a6540a1217d2205def19aa71
checksums.yaml.gz.sig ADDED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/README.md CHANGED
@@ -280,6 +280,7 @@ implementations:
280
280
  * Ruby 1.9.2
281
281
  * Ruby 1.9.3
282
282
  * Ruby 2.0.0
283
+ * Ruby 2.1.0
283
284
 
284
285
  If something doesn't work on one of these Ruby versions, it's a bug.
285
286
 
data/bin/t CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Trap interrupts to quit cleanly. See
4
4
  # https://twitter.com/mitchellh/status/283014103189053442
5
- Signal.trap('INT') { exit 1 }
5
+ Signal.trap('INT') { abort }
6
6
 
7
7
  require 'oauth'
8
8
  require 't'
@@ -13,28 +13,23 @@ def pute(*args)
13
13
  first = args.shift.dup
14
14
  first.insert(0, "#{$PROGRAM_NAME}: ")
15
15
  args.unshift(first)
16
- $stderr.puts(*args)
16
+ abort(*args)
17
17
  end
18
18
 
19
19
  begin
20
20
  T::CLI.start(ARGV)
21
21
  rescue Interrupt
22
22
  pute 'Quitting...'
23
- exit 1
24
23
  rescue OAuth::Unauthorized
25
24
  pute 'Authorization failed'
26
- exit 1
27
25
  rescue Twitter::Error::TooManyRequests => error
28
26
  pute error.message,
29
27
  "The rate limit for this request will reset in #{error.rate_limit.reset_in} seconds.",
30
28
  'While you wait, consider making a polite request for Twitter to increase the API rate limit at https://dev.twitter.com/discussions/10644'
31
- exit 1
32
29
  rescue Twitter::Error::BadRequest => error
33
30
  pute error.message,
34
31
  'Run `t authorize` to authorize.'
35
- exit 1
36
32
  rescue Twitter::Error::Forbidden, Twitter::Error::Unauthorized => error
37
- pute error.message
38
33
  if error.message == 'Error processing your OAuth request: Read-only application cannot POST' ||
39
34
  error.message == 'This application is not allowed to access or delete your direct messages'
40
35
  $stderr.puts(%q(Make sure to set your Twitter application's Access Level to "Read, Write and Access direct messages".))
@@ -42,9 +37,9 @@ rescue Twitter::Error::Forbidden, Twitter::Error::Unauthorized => error
42
37
  Thor::Shell::Basic.new.ask 'Press [Enter] to open the Twitter Developer site.'
43
38
  require 'launchy'
44
39
  Launchy.open('https://dev.twitter.com/apps') { |u, o, e| $stderr.puts "Manually open #{u}" }
40
+ else
41
+ pute error.message
45
42
  end
46
- exit 1
47
43
  rescue Twitter::Error => error
48
44
  pute error.message
49
- exit 1
50
45
  end
data/lib/t.rb CHANGED
@@ -14,13 +14,13 @@ module T
14
14
 
15
15
  def utc_offset=(offset)
16
16
  @utc_offset = case offset
17
- when String
18
- Time.zone_offset(offset)
19
- when NilClass
20
- nil
21
- else
22
- offset.to_i
23
- end
17
+ when String
18
+ Time.zone_offset(offset)
19
+ when NilClass
20
+ nil
21
+ else
22
+ offset.to_i
23
+ end
24
24
  end
25
25
  end
26
26
  end
data/lib/t/cli.rb CHANGED
@@ -29,7 +29,7 @@ module T
29
29
 
30
30
  check_unknown_options!
31
31
 
32
- class_option 'color', :aliases => '-C', :type => :string, :enum => %w(auto never), :default => 'auto', :desc => 'Control how color is used in output'
32
+ class_option 'color', :aliases => '-C', :type => :string, :enum => %w[auto never], :default => 'auto', :desc => 'Control how color is used in output'
33
33
  class_option 'profile', :aliases => '-P', :type => :string, :default => File.join(File.expand_path('~'), T::RCFile::FILE_NAME), :desc => 'Path to RC file', :banner => 'FILE'
34
34
 
35
35
  def initialize(*)
@@ -124,26 +124,28 @@ module T
124
124
 
125
125
  desc 'direct_messages', "Returns the #{DEFAULT_NUM_RESULTS} most recent Direct Messages sent to you."
126
126
  method_option 'csv', :aliases => '-c', :type => :boolean, :default => false, :desc => 'Output in CSV format.'
127
+ method_option 'decode_uris', :aliases => '-d', :type => :boolean, :default => false, :desc => 'Decodes t.co URLs into their original form.'
127
128
  method_option 'long', :aliases => '-l', :type => :boolean, :default => false, :desc => 'Output in long format.'
128
129
  method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
129
130
  method_option 'number', :aliases => '-n', :type => :numeric, :default => DEFAULT_NUM_RESULTS, :desc => 'Limit the number of results.'
130
131
  method_option 'reverse', :aliases => '-r', :type => :boolean, :default => false, :desc => 'Reverse the order of the sort.'
131
132
  def direct_messages
132
133
  count = options['number'] || DEFAULT_NUM_RESULTS
134
+ opts = {}
135
+ opts[:include_entities] = !!options['decode_uris']
133
136
  direct_messages = collect_with_count(count) do |count_opts|
134
- client.direct_messages(count_opts)
137
+ client.direct_messages(count_opts.merge(opts))
135
138
  end
136
139
  direct_messages.reverse! if options['reverse']
137
- require 'htmlentities'
138
140
  if options['csv']
139
141
  require 'csv'
140
142
  say DIRECT_MESSAGE_HEADINGS.to_csv unless direct_messages.empty?
141
143
  direct_messages.each do |direct_message|
142
- say [direct_message.id, csv_formatted_time(direct_message), direct_message.sender.screen_name, HTMLEntities.new.decode(direct_message.text)].to_csv
144
+ say [direct_message.id, csv_formatted_time(direct_message), direct_message.sender.screen_name, decode_full_text(direct_message, options['decode_uris'])].to_csv
143
145
  end
144
146
  elsif options['long']
145
147
  array = direct_messages.map do |direct_message|
146
- [direct_message.id, ls_formatted_time(direct_message), "@#{direct_message.sender.screen_name}", HTMLEntities.new.decode(direct_message.text).gsub(/\n+/, ' ')]
148
+ [direct_message.id, ls_formatted_time(direct_message), "@#{direct_message.sender.screen_name}", decode_full_text(direct_message, options['decode_uris']).gsub(/\n+/, ' ')]
147
149
  end
148
150
  format = options['format'] || DIRECT_MESSAGE_HEADINGS.size.times.map { '%s' }
149
151
  print_table_with_headings(array, DIRECT_MESSAGE_HEADINGS, format)
@@ -153,30 +155,32 @@ module T
153
155
  end
154
156
  end
155
157
  end
156
- map %w(directmessages dms) => :direct_messages
158
+ map %w[directmessages dms] => :direct_messages
157
159
 
158
160
  desc 'direct_messages_sent', "Returns the #{DEFAULT_NUM_RESULTS} most recent Direct Messages you've sent."
159
161
  method_option 'csv', :aliases => '-c', :type => :boolean, :default => false, :desc => 'Output in CSV format.'
162
+ method_option 'decode_uris', :aliases => '-d', :type => :boolean, :default => false, :desc => 'Decodes t.co URLs into their original form.'
160
163
  method_option 'long', :aliases => '-l', :type => :boolean, :default => false, :desc => 'Output in long format.'
161
- method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
162
164
  method_option 'number', :aliases => '-n', :type => :numeric, :default => DEFAULT_NUM_RESULTS, :desc => 'Limit the number of results.'
165
+ method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
163
166
  method_option 'reverse', :aliases => '-r', :type => :boolean, :default => false, :desc => 'Reverse the order of the sort.'
164
167
  def direct_messages_sent
165
168
  count = options['number'] || DEFAULT_NUM_RESULTS
169
+ opts = {}
170
+ opts[:include_entities] = !!options['decode_uris']
166
171
  direct_messages = collect_with_count(count) do |count_opts|
167
- client.direct_messages_sent(count_opts)
172
+ client.direct_messages_sent(count_opts.merge(opts))
168
173
  end
169
174
  direct_messages.reverse! if options['reverse']
170
- require 'htmlentities'
171
175
  if options['csv']
172
176
  require 'csv'
173
177
  say DIRECT_MESSAGE_HEADINGS.to_csv unless direct_messages.empty?
174
178
  direct_messages.each do |direct_message|
175
- say [direct_message.id, csv_formatted_time(direct_message), direct_message.recipient.screen_name, HTMLEntities.new.decode(direct_message.text)].to_csv
179
+ say [direct_message.id, csv_formatted_time(direct_message), direct_message.recipient.screen_name, decode_full_text(direct_message, options['decode_uris'])].to_csv
176
180
  end
177
181
  elsif options['long']
178
182
  array = direct_messages.map do |direct_message|
179
- [direct_message.id, ls_formatted_time(direct_message), "@#{direct_message.recipient.screen_name}", HTMLEntities.new.decode(direct_message.text).gsub(/\n+/, ' ')]
183
+ [direct_message.id, ls_formatted_time(direct_message), "@#{direct_message.recipient.screen_name}", decode_full_text(direct_message, options['decode_uris']).gsub(/\n+/, ' ')]
180
184
  end
181
185
  format = options['format'] || DIRECT_MESSAGE_HEADINGS.size.times.map { '%s' }
182
186
  print_table_with_headings(array, DIRECT_MESSAGE_HEADINGS, format)
@@ -186,7 +190,7 @@ module T
186
190
  end
187
191
  end
188
192
  end
189
- map %w(directmessagessent sent_messages sentmessages sms) => :direct_messages_sent
193
+ map %w[directmessagessent sent_messages sentmessages sms] => :direct_messages_sent
190
194
 
191
195
  desc 'groupies [USER]', "Returns the list of people who follow you but you don't follow back."
192
196
  method_option 'csv', :aliases => '-c', :type => :boolean, :default => false, :desc => 'Output in CSV format.'
@@ -194,21 +198,17 @@ module T
194
198
  method_option 'long', :aliases => '-l', :type => :boolean, :default => false, :desc => 'Output in long format.'
195
199
  method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
196
200
  method_option 'reverse', :aliases => '-r', :type => :boolean, :default => false, :desc => 'Reverse the order of the sort.'
197
- method_option 'sort', :aliases => '-s', :type => :string, :enum => %w(favorites followers friends listed screen_name since tweets tweeted), :default => 'screen_name', :desc => 'Specify the order of the results.', :banner => 'ORDER'
201
+ method_option 'sort', :aliases => '-s', :type => :string, :enum => %w[favorites followers friends listed screen_name since tweets tweeted], :default => 'screen_name', :desc => 'Specify the order of the results.', :banner => 'ORDER'
198
202
  method_option 'unsorted', :aliases => '-u', :type => :boolean, :default => false, :desc => 'Output is not sorted.'
199
203
  def groupies(user = nil)
200
204
  user = if user
201
- require 't/core_ext/string'
202
- options['id'] ? user.to_i : user.strip_ats
203
- else
204
- client.verify_credentials.screen_name
205
- end
206
- follower_ids = Thread.new do
207
- client.follower_ids(user).to_a
208
- end
209
- following_ids = Thread.new do
210
- client.friend_ids(user).to_a
205
+ require 't/core_ext/string'
206
+ options['id'] ? user.to_i : user.strip_ats
207
+ else
208
+ client.verify_credentials.screen_name
211
209
  end
210
+ follower_ids = Thread.new { client.follower_ids(user).to_a }
211
+ following_ids = Thread.new { client.friend_ids(user).to_a }
212
212
  disciple_ids = (follower_ids.value - following_ids.value)
213
213
  require 'retryable'
214
214
  users = retryable(:tries => 3, :on => Twitter::Error, :sleep => 0) do
@@ -216,7 +216,7 @@ module T
216
216
  end
217
217
  print_users(users)
218
218
  end
219
- map %w(disciples) => :groupies
219
+ map %w[disciples] => :groupies
220
220
 
221
221
  desc 'dm USER MESSAGE', 'Sends that person a Direct Message.'
222
222
  method_option 'id', :aliases => '-i', :type => :boolean, :default => false, :desc => 'Specify user via ID instead of screen name.'
@@ -226,7 +226,7 @@ module T
226
226
  direct_message = client.create_direct_message(user, message)
227
227
  say "Direct Message sent from @#{@rcfile.active_profile[0]} to @#{direct_message.recipient.screen_name}."
228
228
  end
229
- map %w(d m) => :dm
229
+ map %w[d m] => :dm
230
230
 
231
231
  desc 'does_contain [USER/]LIST USER', 'Find out whether a list contains a user.'
232
232
  method_option 'id', :aliases => '-i', :type => :boolean, :default => false, :desc => 'Specify user via ID instead of screen name.'
@@ -241,30 +241,37 @@ module T
241
241
  if client.list_member?(owner, list, user)
242
242
  say "Yes, #{list} contains @#{user}."
243
243
  else
244
- say "No, #{list} does not contain @#{user}."
245
- exit 1
244
+ abort "No, #{list} does not contain @#{user}."
246
245
  end
247
246
  end
248
- map %w(dc doescontain) => :does_contain
247
+ map %w[dc doescontain] => :does_contain
249
248
 
250
249
  desc 'does_follow USER [USER]', 'Find out whether one user follows another.'
251
250
  method_option 'id', :aliases => '-i', :type => :boolean, :default => false, :desc => 'Specify user via ID instead of screen name.'
252
251
  def does_follow(user1, user2 = nil)
253
252
  require 't/core_ext/string'
254
- user1 = options['id'] ? client.user(user1.to_i).screen_name : user1.strip_ats
255
- if user2.nil?
256
- user2 = @rcfile.active_profile[0]
253
+ thread1 = if options['id']
254
+ Thread.new { client.user(user1.to_i).screen_name }
257
255
  else
258
- user2 = options['id'] ? client.user(user2.to_i).screen_name : user2.strip_ats
256
+ Thread.new { user1.strip_ats }
259
257
  end
258
+ thread2 = if user2.nil?
259
+ Thread.new { @rcfile.active_profile[0] }
260
+ else
261
+ if options['id']
262
+ Thread.new { client.user(user2.to_i).screen_name }
263
+ else
264
+ Thread.new { user2.strip_ats }
265
+ end
266
+ end
267
+ user1, user2 = thread1.value, thread2.value
260
268
  if client.friendship?(user1, user2)
261
269
  say "Yes, @#{user1} follows @#{user2}."
262
270
  else
263
- say "No, @#{user1} does not follow @#{user2}."
264
- exit 1
271
+ abort "No, @#{user1} does not follow @#{user2}."
265
272
  end
266
273
  end
267
- map %w(df doesfollow) => :does_follow
274
+ map %w[df doesfollow] => :does_follow
268
275
 
269
276
  desc 'favorite TWEET_ID [TWEET_ID...]', 'Marks Tweets as favorites.'
270
277
  def favorite(status_id, *status_ids)
@@ -279,21 +286,23 @@ module T
279
286
  say
280
287
  say "Run `#{File.basename($PROGRAM_NAME)} delete favorite #{status_ids.join(' ')}` to unfavorite."
281
288
  end
282
- map %w(fave favourite) => :favorite
289
+ map %w[fave favourite] => :favorite
283
290
 
284
291
  desc 'favorites [USER]', "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets you favorited."
285
292
  method_option 'csv', :aliases => '-c', :type => :boolean, :default => false, :desc => 'Output in CSV format.'
293
+ method_option 'decode_uris', :aliases => '-d', :type => :boolean, :default => false, :desc => 'Decodes t.co URLs into their original form.'
286
294
  method_option 'id', :aliases => '-i', :type => :boolean, :default => false, :desc => 'Specify user via ID instead of screen name.'
287
295
  method_option 'long', :aliases => '-l', :type => :boolean, :default => false, :desc => 'Output in long format.'
288
- method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
289
296
  method_option 'max_id', :aliases => '-m', :type => :numeric, :desc => 'Returns only the results with an ID less than the specified ID.'
290
297
  method_option 'number', :aliases => '-n', :type => :numeric, :default => DEFAULT_NUM_RESULTS, :desc => 'Limit the number of results.'
298
+ method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
291
299
  method_option 'reverse', :aliases => '-r', :type => :boolean, :default => false, :desc => 'Reverse the order of the sort.'
292
300
  method_option 'since_id', :aliases => '-s', :type => :numeric, :desc => 'Returns only the results with an ID greater than the specified ID.'
293
301
  def favorites(user = nil)
294
302
  count = options['number'] || DEFAULT_NUM_RESULTS
295
303
  opts = {}
296
304
  opts[:exclude_replies] = true if options['exclude'] == 'replies'
305
+ opts[:include_entities] = !!options['decode_uris']
297
306
  opts[:include_rts] = false if options['exclude'] == 'retweets'
298
307
  opts[:max_id] = options['max_id'] if options['max_id']
299
308
  opts[:since_id] = options['since_id'] if options['since_id']
@@ -306,7 +315,7 @@ module T
306
315
  end
307
316
  print_tweets(tweets)
308
317
  end
309
- map %w(faves favourites) => :favorites
318
+ map %w[faves favourites] => :favorites
310
319
 
311
320
  desc 'follow USER [USER...]', 'Allows you to start following users.'
312
321
  method_option 'id', :aliases => '-i', :type => :boolean, :default => false, :desc => 'Specify input as Twitter user IDs instead of screen names.'
@@ -325,7 +334,7 @@ module T
325
334
  method_option 'long', :aliases => '-l', :type => :boolean, :default => false, :desc => 'Output in long format.'
326
335
  method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
327
336
  method_option 'reverse', :aliases => '-r', :type => :boolean, :default => false, :desc => 'Reverse the order of the sort.'
328
- method_option 'sort', :aliases => '-s', :type => :string, :enum => %w(favorites followers friends listed screen_name since tweets tweeted), :default => 'screen_name', :desc => 'Specify the order of the results.', :banner => 'ORDER'
337
+ method_option 'sort', :aliases => '-s', :type => :string, :enum => %w[favorites followers friends listed screen_name since tweets tweeted], :default => 'screen_name', :desc => 'Specify the order of the results.', :banner => 'ORDER'
329
338
  method_option 'unsorted', :aliases => '-u', :type => :boolean, :default => false, :desc => 'Output is not sorted.'
330
339
  def followings(user = nil)
331
340
  if user
@@ -346,7 +355,7 @@ module T
346
355
  method_option 'long', :aliases => '-l', :type => :boolean, :default => false, :desc => 'Output in long format.'
347
356
  method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
348
357
  method_option 'reverse', :aliases => '-r', :type => :boolean, :default => false, :desc => 'Reverse the order of the sort.'
349
- method_option 'sort', :aliases => '-s', :type => :string, :enum => %w(favorites followers friends listed screen_name since tweets tweeted), :default => 'screen_name', :desc => 'Specify the order of the results.', :banner => 'ORDER'
358
+ method_option 'sort', :aliases => '-s', :type => :string, :enum => %w[favorites followers friends listed screen_name since tweets tweeted], :default => 'screen_name', :desc => 'Specify the order of the results.', :banner => 'ORDER'
350
359
  method_option 'unsorted', :aliases => '-u', :type => :boolean, :default => false, :desc => 'Output is not sorted.'
351
360
  def followers(user = nil)
352
361
  if user
@@ -367,21 +376,17 @@ module T
367
376
  method_option 'long', :aliases => '-l', :type => :boolean, :default => false, :desc => 'Output in long format.'
368
377
  method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
369
378
  method_option 'reverse', :aliases => '-r', :type => :boolean, :default => false, :desc => 'Reverse the order of the sort.'
370
- method_option 'sort', :aliases => '-s', :type => :string, :enum => %w(favorites followers friends listed screen_name since tweets tweeted), :default => 'screen_name', :desc => 'Specify the order of the results.', :banner => 'ORDER'
379
+ method_option 'sort', :aliases => '-s', :type => :string, :enum => %w[favorites followers friends listed screen_name since tweets tweeted], :default => 'screen_name', :desc => 'Specify the order of the results.', :banner => 'ORDER'
371
380
  method_option 'unsorted', :aliases => '-u', :type => :boolean, :default => false, :desc => 'Output is not sorted.'
372
381
  def friends(user = nil)
373
382
  user = if user
374
- require 't/core_ext/string'
375
- options['id'] ? user.to_i : user.strip_ats
376
- else
377
- client.verify_credentials.screen_name
378
- end
379
- following_ids = Thread.new do
380
- client.friend_ids(user).to_a
381
- end
382
- follower_ids = Thread.new do
383
- client.follower_ids(user).to_a
383
+ require 't/core_ext/string'
384
+ options['id'] ? user.to_i : user.strip_ats
385
+ else
386
+ client.verify_credentials.screen_name
384
387
  end
388
+ following_ids = Thread.new { client.friend_ids(user).to_a }
389
+ follower_ids = Thread.new { client.follower_ids(user).to_a }
385
390
  friend_ids = (following_ids.value & follower_ids.value)
386
391
  require 'retryable'
387
392
  users = retryable(:tries => 3, :on => Twitter::Error, :sleep => 0) do
@@ -396,21 +401,17 @@ module T
396
401
  method_option 'long', :aliases => '-l', :type => :boolean, :default => false, :desc => 'Output in long format.'
397
402
  method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
398
403
  method_option 'reverse', :aliases => '-r', :type => :boolean, :default => false, :desc => 'Reverse the order of the sort.'
399
- method_option 'sort', :aliases => '-s', :type => :string, :enum => %w(favorites followers friends listed screen_name since tweets tweeted), :default => 'screen_name', :desc => 'Specify the order of the results.', :banner => 'ORDER'
404
+ method_option 'sort', :aliases => '-s', :type => :string, :enum => %w[favorites followers friends listed screen_name since tweets tweeted], :default => 'screen_name', :desc => 'Specify the order of the results.', :banner => 'ORDER'
400
405
  method_option 'unsorted', :aliases => '-u', :type => :boolean, :default => false, :desc => 'Output is not sorted.'
401
406
  def leaders(user = nil)
402
407
  user = if user
403
- require 't/core_ext/string'
404
- options['id'] ? user.to_i : user.strip_ats
405
- else
406
- client.verify_credentials.screen_name
407
- end
408
- following_ids = Thread.new do
409
- client.friend_ids(user).to_a
410
- end
411
- follower_ids = Thread.new do
412
- client.follower_ids(user).to_a
408
+ require 't/core_ext/string'
409
+ options['id'] ? user.to_i : user.strip_ats
410
+ else
411
+ client.verify_credentials.screen_name
413
412
  end
413
+ following_ids = Thread.new { client.friend_ids(user).to_a }
414
+ follower_ids = Thread.new { client.follower_ids(user).to_a }
414
415
  leader_ids = (following_ids.value - follower_ids.value)
415
416
  require 'retryable'
416
417
  users = retryable(:tries => 3, :on => Twitter::Error, :sleep => 0) do
@@ -425,16 +426,16 @@ module T
425
426
  method_option 'long', :aliases => '-l', :type => :boolean, :default => false, :desc => 'Output in long format.'
426
427
  method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
427
428
  method_option 'reverse', :aliases => '-r', :type => :boolean, :default => false, :desc => 'Reverse the order of the sort.'
428
- method_option 'sort', :aliases => '-s', :type => :string, :enum => %w(members mode since slug subscribers), :default => 'slug', :desc => 'Specify the order of the results.', :banner => 'ORDER'
429
+ method_option 'sort', :aliases => '-s', :type => :string, :enum => %w[members mode since slug subscribers], :default => 'slug', :desc => 'Specify the order of the results.', :banner => 'ORDER'
429
430
  method_option 'unsorted', :aliases => '-u', :type => :boolean, :default => false, :desc => 'Output is not sorted.'
430
431
  def lists(user = nil)
431
432
  lists = if user
432
- require 't/core_ext/string'
433
- user = options['id'] ? user.to_i : user.strip_ats
434
- client.lists(user)
435
- else
436
- client.lists
437
- end
433
+ require 't/core_ext/string'
434
+ user = options['id'] ? user.to_i : user.strip_ats
435
+ client.lists(user)
436
+ else
437
+ client.lists
438
+ end
438
439
  print_lists(lists)
439
440
  end
440
441
 
@@ -443,18 +444,21 @@ module T
443
444
 
444
445
  desc 'mentions', "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets mentioning you."
445
446
  method_option 'csv', :aliases => '-c', :type => :boolean, :default => false, :desc => 'Output in CSV format.'
447
+ method_option 'decode_uris', :aliases => '-d', :type => :boolean, :default => false, :desc => 'Decodes t.co URLs into their original form.'
446
448
  method_option 'long', :aliases => '-l', :type => :boolean, :default => false, :desc => 'Output in long format.'
447
- method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
448
449
  method_option 'number', :aliases => '-n', :type => :numeric, :default => DEFAULT_NUM_RESULTS, :desc => 'Limit the number of results.'
450
+ method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
449
451
  method_option 'reverse', :aliases => '-r', :type => :boolean, :default => false, :desc => 'Reverse the order of the sort.'
450
452
  def mentions
451
453
  count = options['number'] || DEFAULT_NUM_RESULTS
454
+ opts = {}
455
+ opts[:include_entities] = !!options['decode_uris']
452
456
  tweets = collect_with_count(count) do |count_opts|
453
- client.mentions(count_opts)
457
+ client.mentions(count_opts.merge(opts))
454
458
  end
455
459
  print_tweets(tweets)
456
460
  end
457
- map %w(replies) => :mentions
461
+ map %w[replies] => :mentions
458
462
 
459
463
  desc 'open USER', "Opens that user's profile in a web browser."
460
464
  method_option 'display-uri', :aliases => '-d', :type => :boolean, :default => false, :desc => 'Display the requested URL instead of attempting to open it.'
@@ -502,7 +506,7 @@ module T
502
506
  end
503
507
  say "@#{@rcfile.active_profile[0]} reported #{pluralize(number, 'user')}."
504
508
  end
505
- map %w(report reportspam spam) => :report_spam
509
+ map %w[report reportspam spam] => :report_spam
506
510
 
507
511
  desc 'retweet TWEET_ID [TWEET_ID...]', 'Sends Tweets to your followers.'
508
512
  def retweet(status_id, *status_ids)
@@ -517,31 +521,34 @@ module T
517
521
  say
518
522
  say "Run `#{File.basename($PROGRAM_NAME)} delete status #{retweets.map { |tweet| tweet.retweeted_status.id }.join(' ')}` to undo."
519
523
  end
520
- map %w(rt) => :retweet
524
+ map %w[rt] => :retweet
521
525
 
522
526
  desc 'retweets [USER]', "Returns the #{DEFAULT_NUM_RESULTS} most recent Retweets by a user."
523
527
  method_option 'csv', :aliases => '-c', :type => :boolean, :default => false, :desc => 'Output in CSV format.'
528
+ method_option 'decode_uris', :aliases => '-d', :type => :boolean, :default => false, :desc => 'Decodes t.co URLs into their original form.'
524
529
  method_option 'id', :aliases => '-i', :type => :boolean, :default => false, :desc => 'Specify user via ID instead of screen name.'
525
530
  method_option 'long', :aliases => '-l', :type => :boolean, :default => false, :desc => 'Output in long format.'
526
- method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
527
531
  method_option 'number', :aliases => '-n', :type => :numeric, :default => DEFAULT_NUM_RESULTS, :desc => 'Limit the number of results.'
532
+ method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
528
533
  method_option 'reverse', :aliases => '-r', :type => :boolean, :default => false, :desc => 'Reverse the order of the sort.'
529
534
  def retweets(user = nil)
530
535
  count = options['number'] || DEFAULT_NUM_RESULTS
536
+ opts = {}
537
+ opts[:include_entities] = !!options['decode_uris']
531
538
  tweets = if user
532
- require 't/core_ext/string'
533
- user = options['id'] ? user.to_i : user.strip_ats
534
- collect_with_count(count) do |count_opts|
535
- client.retweeted_by_user(user, count_opts)
536
- end
537
- else
538
- collect_with_count(count) do |count_opts|
539
- client.retweeted_by_me(count_opts)
540
- end
541
- end
539
+ require 't/core_ext/string'
540
+ user = options['id'] ? user.to_i : user.strip_ats
541
+ collect_with_count(count) do |count_opts|
542
+ client.retweeted_by_user(user, count_opts.merge(opts))
543
+ end
544
+ else
545
+ collect_with_count(count) do |count_opts|
546
+ client.retweeted_by_me(count_opts.merge(opts))
547
+ end
548
+ end
542
549
  print_tweets(tweets)
543
550
  end
544
- map %w(rts) => :retweets
551
+ map %w[rts] => :retweets
545
552
 
546
553
  desc 'ruler', 'Prints a 140-character ruler'
547
554
  method_option 'indent', :aliases => '-i', :type => :numeric, :default => 0, :desc => 'The number of space to print before the ruler.'
@@ -552,40 +559,43 @@ module T
552
559
 
553
560
  desc 'status TWEET_ID', 'Retrieves detailed information about a Tweet.'
554
561
  method_option 'csv', :aliases => '-c', :type => :boolean, :default => false, :desc => 'Output in CSV format.'
562
+ method_option 'decode_uris', :aliases => '-d', :type => :boolean, :default => false, :desc => 'Decodes t.co URLs into their original form.'
555
563
  method_option 'long', :aliases => '-l', :type => :boolean, :default => false, :desc => 'Output in long format.'
556
564
  method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
557
565
  def status(status_id) # rubocop:disable CyclomaticComplexity
558
- status = client.status(status_id.to_i, :include_my_retweet => false)
566
+ opts = {:include_my_retweet => false}
567
+ opts[:include_entities] = !!options['decode_uris']
568
+ status = client.status(status_id.to_i, opts)
559
569
  location = if status.place?
560
- if status.place.name && status.place.attributes && status.place.attributes[:street_address] && status.place.attributes[:locality] && status.place.attributes[:region] && status.place.country
561
- [status.place.name, status.place.attributes[:street_address], status.place.attributes[:locality], status.place.attributes[:region], status.place.country].join(', ')
562
- elsif status.place.name && status.place.attributes && status.place.attributes[:locality] && status.place.attributes[:region] && status.place.country
563
- [status.place.name, status.place.attributes[:locality], status.place.attributes[:region], status.place.country].join(', ')
564
- elsif status.place.full_name && status.place.attributes && status.place.attributes[:region] && status.place.country
565
- [status.place.full_name, status.place.attributes[:region], status.place.country].join(', ')
566
- elsif status.place.full_name && status.place.country
567
- [status.place.full_name, status.place.country].join(', ')
568
- elsif status.place.full_name
569
- status.place.full_name
570
- else
571
- status.place.name
572
- end
573
- elsif status.geo?
574
- reverse_geocode(status.geo)
575
- end
570
+ if status.place.name && status.place.attributes && status.place.attributes[:street_address] && status.place.attributes[:locality] && status.place.attributes[:region] && status.place.country
571
+ [status.place.name, status.place.attributes[:street_address], status.place.attributes[:locality], status.place.attributes[:region], status.place.country].join(', ')
572
+ elsif status.place.name && status.place.attributes && status.place.attributes[:locality] && status.place.attributes[:region] && status.place.country
573
+ [status.place.name, status.place.attributes[:locality], status.place.attributes[:region], status.place.country].join(', ')
574
+ elsif status.place.full_name && status.place.attributes && status.place.attributes[:region] && status.place.country
575
+ [status.place.full_name, status.place.attributes[:region], status.place.country].join(', ')
576
+ elsif status.place.full_name && status.place.country
577
+ [status.place.full_name, status.place.country].join(', ')
578
+ elsif status.place.full_name
579
+ status.place.full_name
580
+ else
581
+ status.place.name
582
+ end
583
+ elsif status.geo?
584
+ reverse_geocode(status.geo)
585
+ end
576
586
  status_headings = ['ID', 'Posted at', 'Screen name', 'Text', 'Retweets', 'Favorites', 'Source', 'Location']
577
587
  if options['csv']
578
588
  require 'csv'
579
589
  say status_headings.to_csv
580
- say [status.id, csv_formatted_time(status), status.user.screen_name, decode_full_text(status), status.retweet_count, status.favorite_count, strip_tags(status.source), location].to_csv
590
+ say [status.id, csv_formatted_time(status), status.user.screen_name, decode_full_text(status, options['decode_uris']), status.retweet_count, status.favorite_count, strip_tags(status.source), location].to_csv
581
591
  elsif options['long']
582
- array = [status.id, ls_formatted_time(status), "@#{status.user.screen_name}", decode_full_text(status).gsub(/\n+/, ' '), status.retweet_count, status.favorite_count, strip_tags(status.source), location]
592
+ array = [status.id, ls_formatted_time(status), "@#{status.user.screen_name}", decode_full_text(status, options['decode_uris']).gsub(/\n+/, ' '), status.retweet_count, status.favorite_count, strip_tags(status.source), location]
583
593
  format = options['format'] || status_headings.size.times.map { '%s' }
584
594
  print_table_with_headings([array], status_headings, format)
585
595
  else
586
596
  array = []
587
597
  array << ['ID', status.id.to_s]
588
- array << ['Text', decode_full_text(status).gsub(/\n+/, ' ')]
598
+ array << ['Text', decode_full_text(status, options['decode_uris']).gsub(/\n+/, ' ')]
589
599
  array << ['Screen name', "@#{status.user.screen_name}"]
590
600
  array << ['Posted at', "#{ls_formatted_time(status, :created_at, false)} (#{time_ago_in_words(status.created_at)} ago)"]
591
601
  array << ['Retweets', number_with_delimiter(status.retweet_count)]
@@ -598,18 +608,20 @@ module T
598
608
 
599
609
  desc 'timeline [USER]', "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets posted by a user."
600
610
  method_option 'csv', :aliases => '-c', :type => :boolean, :default => false, :desc => 'Output in CSV format.'
601
- method_option 'exclude', :aliases => '-e', :type => :string, :enum => %w(replies retweets), :desc => 'Exclude certain types of Tweets from the results.', :banner => 'TYPE'
611
+ method_option 'decode_uris', :aliases => '-d', :type => :boolean, :default => false, :desc => 'Decodes t.co URLs into their original form.'
612
+ method_option 'exclude', :aliases => '-e', :type => :string, :enum => %w[replies retweets], :desc => 'Exclude certain types of Tweets from the results.', :banner => 'TYPE'
602
613
  method_option 'id', :aliases => '-i', :type => :boolean, :default => false, :desc => 'Specify user via ID instead of screen name.'
603
614
  method_option 'long', :aliases => '-l', :type => :boolean, :default => false, :desc => 'Output in long format.'
604
- method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
605
615
  method_option 'max_id', :aliases => '-m', :type => :numeric, :desc => 'Returns only the results with an ID less than the specified ID.'
606
616
  method_option 'number', :aliases => '-n', :type => :numeric, :default => DEFAULT_NUM_RESULTS, :desc => 'Limit the number of results.'
617
+ method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
607
618
  method_option 'reverse', :aliases => '-r', :type => :boolean, :default => false, :desc => 'Reverse the order of the sort.'
608
619
  method_option 'since_id', :aliases => '-s', :type => :numeric, :desc => 'Returns only the results with an ID greater than the specified ID.'
609
620
  def timeline(user = nil)
610
621
  count = options['number'] || DEFAULT_NUM_RESULTS
611
622
  opts = {}
612
623
  opts[:exclude_replies] = true if options['exclude'] == 'replies'
624
+ opts[:include_entities] = !!options['decode_uris']
613
625
  opts[:include_rts] = false if options['exclude'] == 'retweets'
614
626
  opts[:max_id] = options['max_id'] if options['max_id']
615
627
  opts[:since_id] = options['since_id'] if options['since_id']
@@ -626,7 +638,7 @@ module T
626
638
  end
627
639
  print_tweets(tweets)
628
640
  end
629
- map %w(tl) => :timeline
641
+ map %w[tl] => :timeline
630
642
 
631
643
  desc 'trends [WOEID]', 'Returns the top 10 trending topics.'
632
644
  method_option 'exclude-hashtags', :aliases => '-x', :type => :boolean, :default => false, :desc => 'Remove all hashtags from the trends list.'
@@ -642,22 +654,22 @@ module T
642
654
  method_option 'long', :aliases => '-l', :type => :boolean, :default => false, :desc => 'Output in long format.'
643
655
  method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
644
656
  method_option 'reverse', :aliases => '-r', :type => :boolean, :default => false, :desc => 'Reverse the order of the sort.'
645
- method_option 'sort', :aliases => '-s', :type => :string, :enum => %w(country name parent type woeid), :default => 'name', :desc => 'Specify the order of the results.', :banner => 'ORDER'
657
+ method_option 'sort', :aliases => '-s', :type => :string, :enum => %w[country name parent type woeid], :default => 'name', :desc => 'Specify the order of the results.', :banner => 'ORDER'
646
658
  method_option 'unsorted', :aliases => '-u', :type => :boolean, :default => false, :desc => 'Output is not sorted.'
647
659
  def trend_locations
648
660
  places = client.trend_locations
649
661
  places = case options['sort']
650
- when 'country'
651
- places.sort_by { |place| place.country.downcase }
652
- when 'parent'
653
- places.sort_by { |place| place.parent_id.to_i }
654
- when 'type'
655
- places.sort_by { |place| place.place_type.downcase }
656
- when 'woeid'
657
- places.sort_by { |place| place.woeid.to_i }
658
- else
659
- places.sort_by { |place| place.name.downcase }
660
- end unless options['unsorted']
662
+ when 'country'
663
+ places.sort_by { |place| place.country.downcase }
664
+ when 'parent'
665
+ places.sort_by { |place| place.parent_id.to_i }
666
+ when 'type'
667
+ places.sort_by { |place| place.place_type.downcase }
668
+ when 'woeid'
669
+ places.sort_by { |place| place.woeid.to_i }
670
+ else
671
+ places.sort_by { |place| place.name.downcase }
672
+ end unless options['unsorted']
661
673
  places.reverse! if options['reverse']
662
674
  if options['csv']
663
675
  require 'csv'
@@ -675,7 +687,7 @@ module T
675
687
  print_attribute(places, :name)
676
688
  end
677
689
  end
678
- map %w(locations trendlocations) => :trend_locations
690
+ map %w[locations trendlocations] => :trend_locations
679
691
 
680
692
  desc 'unfollow USER [USER...]', 'Allows you to stop following users.'
681
693
  method_option 'id', :aliases => '-i', :type => :boolean, :default => false, :desc => 'Specify input as Twitter user IDs instead of screen names.'
@@ -695,17 +707,16 @@ module T
695
707
  message = T::Editor.gets if message.nil? || message.empty?
696
708
  opts = {:trim_user => true}
697
709
  add_location!(options, opts)
698
-
699
710
  status = if options['file']
700
- client.update_with_media(message, File.new(File.expand_path(options['file'])), opts)
701
- else
702
- client.update(message, opts)
703
- end
711
+ client.update_with_media(message, File.new(File.expand_path(options['file'])), opts)
712
+ else
713
+ client.update(message, opts)
714
+ end
704
715
  say "Tweet posted by @#{@rcfile.active_profile[0]}."
705
716
  say
706
717
  say "Run `#{File.basename($PROGRAM_NAME)} delete status #{status.id}` to delete."
707
718
  end
708
- map %w(post tweet) => :update
719
+ map %w[post tweet] => :update
709
720
 
710
721
  desc 'users USER [USER...]', 'Returns a list of users you specify.'
711
722
  method_option 'csv', :aliases => '-c', :type => :boolean, :default => false, :desc => 'Output in CSV format.'
@@ -713,7 +724,7 @@ module T
713
724
  method_option 'long', :aliases => '-l', :type => :boolean, :default => false, :desc => 'Output in long format.'
714
725
  method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
715
726
  method_option 'reverse', :aliases => '-r', :type => :boolean, :default => false, :desc => 'Reverse the order of the sort.'
716
- method_option 'sort', :aliases => '-s', :type => :string, :enum => %w(favorites followers friends listed screen_name since tweets tweeted), :default => 'screen_name', :desc => 'Specify the order of the results.', :banner => 'ORDER'
727
+ method_option 'sort', :aliases => '-s', :type => :string, :enum => %w[favorites followers friends listed screen_name since tweets tweeted], :default => 'screen_name', :desc => 'Specify the order of the results.', :banner => 'ORDER'
717
728
  method_option 'unsorted', :aliases => '-u', :type => :boolean, :default => false, :desc => 'Output is not sorted.'
718
729
  def users(user, *users)
719
730
  users.unshift(user)
@@ -722,32 +733,34 @@ module T
722
733
  users = client.users(users)
723
734
  print_users(users)
724
735
  end
725
- map %w(stats) => :users
736
+ map %w[stats] => :users
726
737
 
727
738
  desc 'version', 'Show version.'
728
739
  def version
729
740
  require 't/version'
730
741
  say T::Version
731
742
  end
732
- map %w(-v --version) => :version
743
+ map %w[-v --version] => :version
733
744
 
734
745
  desc 'whois USER', 'Retrieves profile information for the user.'
735
746
  method_option 'csv', :aliases => '-c', :type => :boolean, :default => false, :desc => 'Output in CSV format.'
747
+ method_option 'decode_uris', :aliases => '-d', :type => :boolean, :default => false, :desc => 'Decodes t.co URLs into their original form.'
736
748
  method_option 'id', :aliases => '-i', :type => :boolean, :default => false, :desc => 'Specify user via ID instead of screen name.'
737
749
  method_option 'long', :aliases => '-l', :type => :boolean, :default => false, :desc => 'Output in long format.'
738
750
  method_option 'relative_dates', :aliases => '-a', :type => :boolean, :desc => 'Show relative dates.'
739
751
  def whois(user) # rubocop:disable CyclomaticComplexity
740
752
  require 't/core_ext/string'
753
+ opts = {}
754
+ opts[:include_entities] = !!options['decode_uris']
741
755
  user = options['id'] ? user.to_i : user.strip_ats
742
- user = client.user(user)
743
- require 'htmlentities'
756
+ user = client.user(user, opts)
744
757
  if options['csv'] || options['long']
745
758
  print_users([user])
746
759
  else
747
760
  array = []
748
761
  array << ['ID', user.id.to_s]
749
762
  array << ['Since', "#{ls_formatted_time(user, :created_at, false)} (#{time_ago_in_words(user.created_at)} ago)"]
750
- array << ['Last update', "#{decode_full_text(user.status).gsub(/\n+/, ' ')} (#{time_ago_in_words(user.status.created_at)} ago)"] unless user.status.nil?
763
+ array << ['Last update', "#{decode_full_text(user.status, options['decode_uris']).gsub(/\n+/, ' ')} (#{time_ago_in_words(user.status.created_at)} ago)"] unless user.status.nil?
751
764
  array << ['Screen name', "@#{user.screen_name}"]
752
765
  array << [user.verified ? 'Name (Verified)' : 'Name', user.name] unless user.name.nil?
753
766
  array << ['Tweets', number_with_delimiter(user.statuses_count)]
@@ -761,7 +774,7 @@ module T
761
774
  print_table(array)
762
775
  end
763
776
  end
764
- map %w(user) => :whois
777
+ map %w[user] => :whois
765
778
 
766
779
  desc 'delete SUBCOMMAND ...ARGS', 'Delete Tweets, Direct Messages, etc.'
767
780
  subcommand 'delete', T::Delete