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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/README.md +1 -0
- data/bin/t +4 -9
- data/lib/t.rb +7 -7
- data/lib/t/cli.rb +148 -135
- data/lib/t/collectable.rb +1 -1
- data/lib/t/delete.rb +3 -3
- data/lib/t/list.rb +7 -4
- data/lib/t/printable.rb +29 -30
- data/lib/t/search.rb +18 -19
- data/lib/t/set.rb +3 -3
- data/lib/t/stream.rb +2 -2
- data/lib/t/utils.rb +4 -4
- data/lib/t/version.rb +6 -21
- data/spec/cli_spec.rb +257 -182
- data/spec/fixtures/geo.json +414 -0
- data/spec/fixtures/geo_no_city.json +344 -0
- data/spec/fixtures/geo_no_state.json +299 -0
- data/spec/helper.rb +1 -1
- data/spec/list_spec.rb +25 -11
- data/spec/search_spec.rb +107 -107
- data/spec/stream_spec.rb +15 -15
- data/t.gemspec +13 -13
- metadata +65 -82
- metadata.gz.sig +0 -0
- data/spec/fixtures/geo.kml +0 -64
- data/spec/fixtures/geo_no_city.kml +0 -24
- data/spec/fixtures/geo_no_state.kml +0 -16
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
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') {
|
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
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
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,
|
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}",
|
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
|
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,
|
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}",
|
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
|
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
|
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
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
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
|
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
|
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
|
-
|
245
|
-
exit 1
|
244
|
+
abort "No, #{list} does not contain @#{user}."
|
246
245
|
end
|
247
246
|
end
|
248
|
-
map %w
|
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
|
-
|
255
|
-
|
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
|
-
|
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
|
-
|
264
|
-
exit 1
|
271
|
+
abort "No, @#{user1} does not follow @#{user2}."
|
265
272
|
end
|
266
273
|
end
|
267
|
-
map %w
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
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
|
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
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
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
|
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
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
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
|
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
|
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
|
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
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
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
|
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
|
-
|
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
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
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 '
|
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
|
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
|
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
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
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
|
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
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
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
|
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
|
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
|
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
|
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
|
777
|
+
map %w[user] => :whois
|
765
778
|
|
766
779
|
desc 'delete SUBCOMMAND ...ARGS', 'Delete Tweets, Direct Messages, etc.'
|
767
780
|
subcommand 'delete', T::Delete
|