t 3.1.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/t/cli.rb CHANGED
@@ -1,19 +1,20 @@
1
1
  # encoding: utf-8
2
- require 'forwardable'
3
- require 'oauth'
4
- require 'thor'
5
- require 'twitter'
6
- require 't/collectable'
7
- require 't/delete'
8
- require 't/editor'
9
- require 't/list'
10
- require 't/printable'
11
- require 't/rcfile'
12
- require 't/requestable'
13
- require 't/search'
14
- require 't/set'
15
- require 't/stream'
16
- require 't/utils'
2
+
3
+ require "forwardable"
4
+ require "oauth"
5
+ require "thor"
6
+ require "twitter"
7
+ require "t/collectable"
8
+ require "t/delete"
9
+ require "t/editor"
10
+ require "t/list"
11
+ require "t/printable"
12
+ require "t/rcfile"
13
+ require "t/requestable"
14
+ require "t/search"
15
+ require "t/set"
16
+ require "t/stream"
17
+ require "t/utils"
17
18
 
18
19
  module T
19
20
  class CLI < Thor
@@ -24,23 +25,23 @@ module T
24
25
  include T::Utils
25
26
 
26
27
  DEFAULT_NUM_RESULTS = 20
27
- DIRECT_MESSAGE_HEADINGS = ['ID', 'Posted at', 'Screen name', 'Text'].freeze
28
+ DIRECT_MESSAGE_HEADINGS = ["ID", "Posted at", "Screen name", "Text"].freeze
28
29
  MAX_SEARCH_RESULTS = 100
29
- TREND_HEADINGS = ['WOEID', 'Parent ID', 'Type', 'Name', 'Country'].freeze
30
+ TREND_HEADINGS = ["WOEID", "Parent ID", "Type", "Name", "Country"].freeze
30
31
 
31
32
  check_unknown_options!
32
33
 
33
- class_option 'color', aliases: '-C', type: :string, enum: %w(icon auto never), default: 'auto', desc: 'Control how color is used in output'
34
- 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
+ class_option "color", aliases: "-C", type: :string, enum: %w[icon auto never], default: "auto", desc: "Control how color is used in output"
35
+ class_option "profile", aliases: "-P", type: :string, default: File.join(File.expand_path("~"), T::RCFile::FILE_NAME), desc: "Path to RC file", banner: "FILE"
35
36
 
36
37
  def initialize(*)
37
38
  @rcfile = T::RCFile.instance
38
39
  super
39
40
  end
40
41
 
41
- desc 'accounts', 'List accounts'
42
+ desc "accounts", "List accounts"
42
43
  def accounts
43
- @rcfile.path = options['profile'] if options['profile']
44
+ @rcfile.path = options["profile"] if options["profile"]
44
45
  @rcfile.profiles.each do |profile|
45
46
  say profile[0]
46
47
  profile[1].each_key do |key|
@@ -49,68 +50,68 @@ module T
49
50
  end
50
51
  end
51
52
 
52
- desc 'authorize', 'Allows an application to request user authorization'
53
- method_option 'display-uri', aliases: '-d', type: :boolean, desc: 'Display the authorization URL instead of attempting to open it.'
53
+ desc "authorize", "Allows an application to request user authorization"
54
+ method_option "display-uri", aliases: "-d", type: :boolean, desc: "Display the authorization URL instead of attempting to open it."
54
55
  def authorize
55
- @rcfile.path = options['profile'] if options['profile']
56
+ @rcfile.path = options["profile"] if options["profile"]
56
57
  if @rcfile.empty?
57
58
  say "Welcome! Before you can use t, you'll first need to register an"
58
- say 'application with Twitter. Just follow the steps below:'
59
- say ' 1. Sign in to the Twitter Application Management site and click'
59
+ say "application with Twitter. Just follow the steps below:"
60
+ say " 1. Sign in to the Twitter Application Management site and click"
60
61
  say ' "Create New App".'
61
- say ' 2. Complete the required fields and submit the form.'
62
- say ' Note: Your application must have a unique name.'
63
- say ' 3. Go to the Permissions tab of your application, and change the'
62
+ say " 2. Complete the required fields and submit the form."
63
+ say " Note: Your application must have a unique name."
64
+ say " 3. Go to the Permissions tab of your application, and change the"
64
65
  say ' Access setting to "Read, Write and Access direct messages".'
65
- say ' 4. Go to the Keys and Access Tokens tab to view the consumer key'
66
+ say " 4. Go to the Keys and Access Tokens tab to view the consumer key"
66
67
  say " and secret which you'll need to copy and paste below when"
67
- say ' prompted.'
68
+ say " prompted."
68
69
  else
69
70
  say "It looks like you've already registered an application with Twitter."
70
- say 'To authorize a new account, just follow the steps below:'
71
- say ' 1. Sign in to the Twitter Developer site.'
71
+ say "To authorize a new account, just follow the steps below:"
72
+ say " 1. Sign in to the Twitter Developer site."
72
73
  say " 2. Select the application for which you'd like to authorize an account."
73
- say ' 3. Copy and paste the consumer key and secret below when prompted.'
74
+ say " 3. Copy and paste the consumer key and secret below when prompted."
74
75
  end
75
76
  say
76
- ask 'Press [Enter] to open the Twitter Developer site.'
77
+ ask "Press [Enter] to open the Twitter Developer site."
77
78
  say
78
- require 'launchy'
79
- open_or_print('https://apps.twitter.com', dry_run: options['display-uri'])
80
- key = ask 'Enter your API key:'
81
- secret = ask 'Enter your API secret:'
82
- consumer = OAuth::Consumer.new(key, secret, site: Twitter::REST::Client::BASE_URL)
79
+ require "launchy"
80
+ open_or_print("https://apps.twitter.com", dry_run: options["display-uri"])
81
+ key = ask "Enter your API key:"
82
+ secret = ask "Enter your API secret:"
83
+ consumer = OAuth::Consumer.new(key, secret, site: Twitter::REST::Request::BASE_URL)
83
84
  request_token = consumer.get_request_token
84
85
  uri = generate_authorize_uri(consumer, request_token)
85
86
  say
86
- say 'In a moment, you will be directed to the Twitter app authorization page.'
87
- say 'Perform the following steps to complete the authorization process:'
88
- say ' 1. Sign in to Twitter.'
87
+ say "In a moment, you will be directed to the Twitter app authorization page."
88
+ say "Perform the following steps to complete the authorization process:"
89
+ say " 1. Sign in to Twitter."
89
90
  say ' 2. Press "Authorize app".'
90
- say ' 3. Copy and paste the supplied PIN below when prompted.'
91
+ say " 3. Copy and paste the supplied PIN below when prompted."
91
92
  say
92
- ask 'Press [Enter] to open the Twitter app authorization page.'
93
+ ask "Press [Enter] to open the Twitter app authorization page."
93
94
  say
94
- open_or_print(uri, dry_run: options['display-uri'])
95
- pin = ask 'Enter the supplied PIN:'
95
+ open_or_print(uri, dry_run: options["display-uri"])
96
+ pin = ask "Enter the supplied PIN:"
96
97
  access_token = request_token.get_access_token(oauth_verifier: pin.chomp)
97
- oauth_response = access_token.get('/1.1/account/verify_credentials.json?skip_status=true')
98
+ oauth_response = access_token.get("/1.1/account/verify_credentials.json?skip_status=true")
98
99
  screen_name = oauth_response.body.match(/"screen_name"\s*:\s*"(.*?)"/).captures.first
99
100
  @rcfile[screen_name] = {
100
101
  key => {
101
- 'username' => screen_name,
102
- 'consumer_key' => key,
103
- 'consumer_secret' => secret,
104
- 'token' => access_token.token,
105
- 'secret' => access_token.secret,
102
+ "username" => screen_name,
103
+ "consumer_key" => key,
104
+ "consumer_secret" => secret,
105
+ "token" => access_token.token,
106
+ "secret" => access_token.secret,
106
107
  },
107
108
  }
108
- @rcfile.active_profile = {'username' => screen_name, 'consumer_key' => key}
109
- say 'Authorization successful.'
109
+ @rcfile.active_profile = {"username" => screen_name, "consumer_key" => key}
110
+ say "Authorization successful."
110
111
  end
111
112
 
112
- desc 'block USER [USER...]', 'Block users.'
113
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify input as Twitter user IDs instead of screen names.'
113
+ desc "block USER [USER...]", "Block users."
114
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify input as Twitter user IDs instead of screen names."
114
115
  def block(user, *users)
115
116
  blocked_users, number = fetch_users(users.unshift(user), options) do |users_to_block|
116
117
  client.block(users_to_block)
@@ -120,67 +121,73 @@ module T
120
121
  say "Run `#{File.basename($PROGRAM_NAME)} delete block #{blocked_users.collect { |blocked_user| "@#{blocked_user.screen_name}" }.join(' ')}` to unblock."
121
122
  end
122
123
 
123
- desc 'direct_messages', "Returns the #{DEFAULT_NUM_RESULTS} most recent Direct Messages sent to you."
124
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
125
- method_option 'decode_uris', aliases: '-d', type: :boolean, desc: 'Decodes t.co URLs into their original form.'
126
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
127
- method_option 'number', aliases: '-n', type: :numeric, default: DEFAULT_NUM_RESULTS, desc: 'Limit the number of results.'
128
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
129
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
124
+ desc "direct_messages", "Returns the #{DEFAULT_NUM_RESULTS} most recent Direct Messages sent to you."
125
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
126
+ method_option "decode_uris", aliases: "-d", type: :boolean, desc: "Decodes t.co URLs into their original form."
127
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
128
+ method_option "number", aliases: "-n", type: :numeric, default: DEFAULT_NUM_RESULTS, desc: "Limit the number of results."
129
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
130
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
130
131
  def direct_messages
131
- count = options['number'] || DEFAULT_NUM_RESULTS
132
+ count = options["number"] || DEFAULT_NUM_RESULTS
132
133
  opts = {}
133
- opts[:include_entities] = !!options['decode_uris']
134
+ opts[:include_entities] = !!options["decode_uris"]
134
135
  direct_messages = collect_with_count(count) do |count_opts|
135
- client.direct_messages(count_opts.merge(opts))
136
+ client.direct_messages_received(count_opts.merge(opts))
136
137
  end
137
- direct_messages.reverse! if options['reverse']
138
- if options['csv']
139
- require 'csv'
138
+ users = direct_messages.empty? ? [] : client.users(direct_messages.map(&:sender_id))
139
+ users_hash = users.each_with_object({}) { |user, hash| hash[user.id] = user }
140
+
141
+ direct_messages.reverse! if options["reverse"]
142
+ if options["csv"]
143
+ require "csv"
140
144
  say DIRECT_MESSAGE_HEADINGS.to_csv unless direct_messages.empty?
141
145
  direct_messages.each do |direct_message|
142
- say [direct_message.id, csv_formatted_time(direct_message), direct_message.sender.screen_name, decode_full_text(direct_message, options['decode_uris'])].to_csv
146
+ sender = users_hash[direct_message.sender_id] || Twitter::NullObject.new
147
+ say [direct_message.id, csv_formatted_time(direct_message), sender.screen_name, decode_full_text(direct_message, options["decode_uris"])].to_csv
143
148
  end
144
- elsif options['long']
149
+ elsif options["long"]
145
150
  array = direct_messages.collect do |direct_message|
146
- [direct_message.id, ls_formatted_time(direct_message), "@#{direct_message.sender.screen_name}", decode_full_text(direct_message, options['decode_uris']).gsub(/\n+/, ' ')]
151
+ sender = users_hash[direct_message.sender_id] || Twitter::NullObject.new
152
+ [direct_message.id, ls_formatted_time(direct_message), "@#{sender.screen_name}", decode_full_text(direct_message, options["decode_uris"]).gsub(/\n+/, " ")]
147
153
  end
148
- format = options['format'] || Array.new(DIRECT_MESSAGE_HEADINGS.size) { '%s' }
154
+ format = options["format"] || Array.new(DIRECT_MESSAGE_HEADINGS.size) { "%s" }
149
155
  print_table_with_headings(array, DIRECT_MESSAGE_HEADINGS, format)
150
156
  else
151
157
  direct_messages.each do |direct_message|
152
- print_message(direct_message.sender.screen_name, direct_message.text)
158
+ sender = users_hash[direct_message.sender_id] || Twitter::NullObject.new
159
+ print_message(sender.screen_name, direct_message.text)
153
160
  end
154
161
  end
155
162
  end
156
- map %w(directmessages dms) => :direct_messages
163
+ map %w[directmessages dms] => :direct_messages
157
164
 
158
- desc 'direct_messages_sent', "Returns the #{DEFAULT_NUM_RESULTS} most recent Direct Messages you've sent."
159
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
160
- method_option 'decode_uris', aliases: '-d', type: :boolean, desc: 'Decodes t.co URLs into their original form.'
161
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
162
- method_option 'number', aliases: '-n', type: :numeric, default: DEFAULT_NUM_RESULTS, desc: 'Limit the number of results.'
163
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
164
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
165
+ desc "direct_messages_sent", "Returns the #{DEFAULT_NUM_RESULTS} most recent Direct Messages you've sent."
166
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
167
+ method_option "decode_uris", aliases: "-d", type: :boolean, desc: "Decodes t.co URLs into their original form."
168
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
169
+ method_option "number", aliases: "-n", type: :numeric, default: DEFAULT_NUM_RESULTS, desc: "Limit the number of results."
170
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
171
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
165
172
  def direct_messages_sent
166
- count = options['number'] || DEFAULT_NUM_RESULTS
173
+ count = options["number"] || DEFAULT_NUM_RESULTS
167
174
  opts = {}
168
- opts[:include_entities] = !!options['decode_uris']
175
+ opts[:include_entities] = !!options["decode_uris"]
169
176
  direct_messages = collect_with_count(count) do |count_opts|
170
177
  client.direct_messages_sent(count_opts.merge(opts))
171
178
  end
172
- direct_messages.reverse! if options['reverse']
173
- if options['csv']
174
- require 'csv'
179
+ direct_messages.reverse! if options["reverse"]
180
+ if options["csv"]
181
+ require "csv"
175
182
  say DIRECT_MESSAGE_HEADINGS.to_csv unless direct_messages.empty?
176
183
  direct_messages.each do |direct_message|
177
- say [direct_message.id, csv_formatted_time(direct_message), direct_message.recipient.screen_name, decode_full_text(direct_message, options['decode_uris'])].to_csv
184
+ say [direct_message.id, csv_formatted_time(direct_message), direct_message.recipient.screen_name, decode_full_text(direct_message, options["decode_uris"])].to_csv
178
185
  end
179
- elsif options['long']
186
+ elsif options["long"]
180
187
  array = direct_messages.collect do |direct_message|
181
- [direct_message.id, ls_formatted_time(direct_message), "@#{direct_message.recipient.screen_name}", decode_full_text(direct_message, options['decode_uris']).gsub(/\n+/, ' ')]
188
+ [direct_message.id, ls_formatted_time(direct_message), "@#{direct_message.recipient.screen_name}", decode_full_text(direct_message, options["decode_uris"]).gsub(/\n+/, " ")]
182
189
  end
183
- format = options['format'] || Array.new(DIRECT_MESSAGE_HEADINGS.size) { '%s' }
190
+ format = options["format"] || Array.new(DIRECT_MESSAGE_HEADINGS.size) { "%s" }
184
191
  print_table_with_headings(array, DIRECT_MESSAGE_HEADINGS, format)
185
192
  else
186
193
  direct_messages.each do |direct_message|
@@ -188,27 +195,27 @@ module T
188
195
  end
189
196
  end
190
197
  end
191
- map %w(directmessagessent sent_messages sentmessages sms) => :direct_messages_sent
198
+ map %w[directmessagessent sent_messages sentmessages sms] => :direct_messages_sent
192
199
 
193
- desc 'dm USER MESSAGE', 'Sends that person a Direct Message.'
194
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify user via ID instead of screen name.'
200
+ desc "dm USER MESSAGE", "Sends that person a Direct Message."
201
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
195
202
  def dm(user, message)
196
- require 't/core_ext/string'
197
- user = options['id'] ? user.to_i : user.strip_ats
198
- direct_message = client.create_direct_message(user, message)
199
- say "Direct Message sent from @#{@rcfile.active_profile[0]} to @#{direct_message.recipient.screen_name}."
203
+ require "t/core_ext/string"
204
+ recipient = options["id"] ? client.user(user.to_i) : client.user(user.strip_ats)
205
+ client.create_direct_message_event(recipient, message)
206
+ say "Direct Message sent from @#{@rcfile.active_profile[0]} to @#{recipient.screen_name}."
200
207
  end
201
- map %w(d m) => :dm
208
+ map %w[d m] => :dm
202
209
 
203
- desc 'does_contain [USER/]LIST USER', 'Find out whether a list contains a user.'
204
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify user via ID instead of screen name.'
210
+ desc "does_contain [USER/]LIST USER", "Find out whether a list contains a user."
211
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
205
212
  def does_contain(user_list, user = nil)
206
213
  owner, list_name = extract_owner(user_list, options)
207
- if user.nil?
208
- user = @rcfile.active_profile[0]
214
+ user = if user.nil?
215
+ @rcfile.active_profile[0]
209
216
  else
210
- require 't/core_ext/string'
211
- user = options['id'] ? client.user(user.to_i).screen_name : user.strip_ats
217
+ require "t/core_ext/string"
218
+ options["id"] ? client.user(user.to_i).screen_name : user.strip_ats
212
219
  end
213
220
  if client.list_member?(owner, list_name, user)
214
221
  say "Yes, #{list_name} contains @#{user}."
@@ -216,22 +223,22 @@ module T
216
223
  abort "No, #{list_name} does not contain @#{user}."
217
224
  end
218
225
  end
219
- map %w(dc doescontain) => :does_contain
226
+ map %w[dc doescontain] => :does_contain
220
227
 
221
- desc 'does_follow USER [USER]', 'Find out whether one user follows another.'
222
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify user via ID instead of screen name.'
228
+ desc "does_follow USER [USER]", "Find out whether one user follows another."
229
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
223
230
  def does_follow(user1, user2 = nil)
224
- abort 'No, you are not following yourself.' if user2.nil? && @rcfile.active_profile[0].casecmp(user1).zero?
231
+ abort "No, you are not following yourself." if user2.nil? && @rcfile.active_profile[0].casecmp(user1).zero?
225
232
  abort "No, @#{user1} is not following themself." if user1 == user2
226
- require 't/core_ext/string'
227
- thread1 = if options['id']
233
+ require "t/core_ext/string"
234
+ thread1 = if options["id"]
228
235
  Thread.new { client.user(user1.to_i).screen_name }
229
236
  else
230
237
  Thread.new { user1.strip_ats }
231
238
  end
232
239
  thread2 = if user2.nil?
233
240
  Thread.new { @rcfile.active_profile[0] }
234
- elsif options['id']
241
+ elsif options["id"]
235
242
  Thread.new { client.user(user2.to_i).screen_name }
236
243
  else
237
244
  Thread.new { user2.strip_ats }
@@ -244,13 +251,13 @@ module T
244
251
  abort "No, @#{user1} does not follow @#{user2}."
245
252
  end
246
253
  end
247
- map %w(df doesfollow) => :does_follow
254
+ map %w[df doesfollow] => :does_follow
248
255
 
249
- desc 'favorite TWEET_ID [TWEET_ID...]', 'Marks Tweets as favorites.'
256
+ desc "favorite TWEET_ID [TWEET_ID...]", "Marks Tweets as favorites."
250
257
  def favorite(status_id, *status_ids)
251
258
  status_ids.unshift(status_id)
252
259
  status_ids.collect!(&:to_i)
253
- require 'retryable'
260
+ require "retryable"
254
261
  favorites = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
255
262
  client.favorite(status_ids)
256
263
  end
@@ -259,39 +266,39 @@ module T
259
266
  say
260
267
  say "Run `#{File.basename($PROGRAM_NAME)} delete favorite #{status_ids.join(' ')}` to unfavorite."
261
268
  end
262
- map %w(fave favourite) => :favorite
263
-
264
- desc 'favorites [USER]', "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets you favorited."
265
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
266
- method_option 'decode_uris', aliases: '-d', type: :boolean, desc: 'Decodes t.co URLs into their original form.'
267
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify user via ID instead of screen name.'
268
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
269
- method_option 'max_id', aliases: '-m', type: :numeric, desc: 'Returns only the results with an ID less than the specified ID.'
270
- method_option 'number', aliases: '-n', type: :numeric, default: DEFAULT_NUM_RESULTS, desc: 'Limit the number of results.'
271
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
272
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
273
- method_option 'since_id', aliases: '-s', type: :numeric, desc: 'Returns only the results with an ID greater than the specified ID.'
269
+ map %w[fave favourite] => :favorite
270
+
271
+ desc "favorites [USER]", "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets you favorited."
272
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
273
+ method_option "decode_uris", aliases: "-d", type: :boolean, desc: "Decodes t.co URLs into their original form."
274
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
275
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
276
+ method_option "max_id", aliases: "-m", type: :numeric, desc: "Returns only the results with an ID less than the specified ID."
277
+ method_option "number", aliases: "-n", type: :numeric, default: DEFAULT_NUM_RESULTS, desc: "Limit the number of results."
278
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
279
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
280
+ method_option "since_id", aliases: "-s", type: :numeric, desc: "Returns only the results with an ID greater than the specified ID."
274
281
  def favorites(user = nil)
275
- count = options['number'] || DEFAULT_NUM_RESULTS
282
+ count = options["number"] || DEFAULT_NUM_RESULTS
276
283
  opts = {}
277
- opts[:exclude_replies] = true if options['exclude'] == 'replies'
278
- opts[:include_entities] = !!options['decode_uris']
279
- opts[:include_rts] = false if options['exclude'] == 'retweets'
280
- opts[:max_id] = options['max_id'] if options['max_id']
281
- opts[:since_id] = options['since_id'] if options['since_id']
284
+ opts[:exclude_replies] = true if options["exclude"] == "replies"
285
+ opts[:include_entities] = !!options["decode_uris"]
286
+ opts[:include_rts] = false if options["exclude"] == "retweets"
287
+ opts[:max_id] = options["max_id"] if options["max_id"]
288
+ opts[:since_id] = options["since_id"] if options["since_id"]
282
289
  if user
283
- require 't/core_ext/string'
284
- user = options['id'] ? user.to_i : user.strip_ats
290
+ require "t/core_ext/string"
291
+ user = options["id"] ? user.to_i : user.strip_ats
285
292
  end
286
293
  tweets = collect_with_count(count) do |count_opts|
287
294
  client.favorites(user, count_opts.merge(opts))
288
295
  end
289
296
  print_tweets(tweets)
290
297
  end
291
- map %w(faves favourites) => :favorites
298
+ map %w[faves favourites] => :favorites
292
299
 
293
- desc 'follow USER [USER...]', 'Allows you to start following users.'
294
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify input as Twitter user IDs instead of screen names.'
300
+ desc "follow USER [USER...]", "Allows you to start following users."
301
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify input as Twitter user IDs instead of screen names."
295
302
  def follow(user, *users)
296
303
  followed_users, number = fetch_users(users.unshift(user), options) do |users_to_follow|
297
304
  client.follow(users_to_follow)
@@ -301,195 +308,195 @@ module T
301
308
  say "Run `#{File.basename($PROGRAM_NAME)} unfollow #{followed_users.collect { |followed_user| "@#{followed_user.screen_name}" }.join(' ')}` to stop."
302
309
  end
303
310
 
304
- desc 'followings [USER]', 'Returns a list of the people you follow on Twitter.'
305
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
306
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify user via ID instead of screen name.'
307
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
308
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
309
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
310
- 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'
311
- method_option 'unsorted', aliases: '-u', type: :boolean, desc: 'Output is not sorted.'
311
+ desc "followings [USER]", "Returns a list of the people you follow on Twitter."
312
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
313
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
314
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
315
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
316
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
317
+ 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"
318
+ method_option "unsorted", aliases: "-u", type: :boolean, desc: "Output is not sorted."
312
319
  def followings(user = nil)
313
320
  if user
314
- require 't/core_ext/string'
315
- user = options['id'] ? user.to_i : user.strip_ats
321
+ require "t/core_ext/string"
322
+ user = options["id"] ? user.to_i : user.strip_ats
316
323
  end
317
324
  following_ids = client.friend_ids(user).to_a
318
- require 'retryable'
325
+ require "retryable"
319
326
  users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
320
327
  client.users(following_ids)
321
328
  end
322
329
  print_users(users)
323
330
  end
324
331
 
325
- desc 'followings_following USER [USER]', 'Displays your friends who follow the specified user.'
326
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
327
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify input as Twitter user IDs instead of screen names.'
328
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
329
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
330
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
331
- 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'
332
- method_option 'unsorted', aliases: '-u', type: :boolean, desc: 'Output is not sorted.'
332
+ desc "followings_following USER [USER]", "Displays your friends who follow the specified user."
333
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
334
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify input as Twitter user IDs instead of screen names."
335
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
336
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
337
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
338
+ 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"
339
+ method_option "unsorted", aliases: "-u", type: :boolean, desc: "Output is not sorted."
333
340
  def followings_following(user1, user2 = nil)
334
- require 't/core_ext/string'
335
- user1 = options['id'] ? user1.to_i : user1.strip_ats
341
+ require "t/core_ext/string"
342
+ user1 = options["id"] ? user1.to_i : user1.strip_ats
336
343
  user2 = if user2.nil?
337
344
  @rcfile.active_profile[0]
338
345
  else
339
- options['id'] ? user2.to_i : user2.strip_ats
346
+ options["id"] ? user2.to_i : user2.strip_ats
340
347
  end
341
348
  follower_ids = Thread.new { client.follower_ids(user1).to_a }
342
349
  following_ids = Thread.new { client.friend_ids(user2).to_a }
343
350
  followings_following_ids = follower_ids.value & following_ids.value
344
- require 'retryable'
351
+ require "retryable"
345
352
  users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
346
353
  client.users(followings_following_ids)
347
354
  end
348
355
  print_users(users)
349
356
  end
350
- map %w(ff followingsfollowing) => :followings_following
357
+ map %w[ff followingsfollowing] => :followings_following
351
358
 
352
- desc 'followers [USER]', 'Returns a list of the people who follow you on Twitter.'
353
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
354
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify user via ID instead of screen name.'
355
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
356
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
357
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
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'
359
- method_option 'unsorted', aliases: '-u', type: :boolean, desc: 'Output is not sorted.'
359
+ desc "followers [USER]", "Returns a list of the people who follow you on Twitter."
360
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
361
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
362
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
363
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
364
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
365
+ 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"
366
+ method_option "unsorted", aliases: "-u", type: :boolean, desc: "Output is not sorted."
360
367
  def followers(user = nil)
361
368
  if user
362
- require 't/core_ext/string'
363
- user = options['id'] ? user.to_i : user.strip_ats
369
+ require "t/core_ext/string"
370
+ user = options["id"] ? user.to_i : user.strip_ats
364
371
  end
365
372
  follower_ids = client.follower_ids(user).to_a
366
- require 'retryable'
373
+ require "retryable"
367
374
  users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
368
375
  client.users(follower_ids)
369
376
  end
370
377
  print_users(users)
371
378
  end
372
379
 
373
- desc 'friends [USER]', 'Returns the list of people who you follow and follow you back.'
374
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
375
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify user via ID instead of screen name.'
376
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
377
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
378
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
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'
380
- method_option 'unsorted', aliases: '-u', type: :boolean, desc: 'Output is not sorted.'
380
+ desc "friends [USER]", "Returns the list of people who you follow and follow you back."
381
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
382
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
383
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
384
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
385
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
386
+ 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"
387
+ method_option "unsorted", aliases: "-u", type: :boolean, desc: "Output is not sorted."
381
388
  def friends(user = nil)
382
389
  user = if user
383
- require 't/core_ext/string'
384
- options['id'] ? user.to_i : user.strip_ats
390
+ require "t/core_ext/string"
391
+ options["id"] ? user.to_i : user.strip_ats
385
392
  else
386
393
  client.verify_credentials.screen_name
387
394
  end
388
395
  following_ids = Thread.new { client.friend_ids(user).to_a }
389
396
  follower_ids = Thread.new { client.follower_ids(user).to_a }
390
397
  friend_ids = following_ids.value & follower_ids.value
391
- require 'retryable'
398
+ require "retryable"
392
399
  users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
393
400
  client.users(friend_ids)
394
401
  end
395
402
  print_users(users)
396
403
  end
397
404
 
398
- desc 'groupies [USER]', "Returns the list of people who follow you but you don't follow back."
399
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
400
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify user via ID instead of screen name.'
401
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
402
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
403
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
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'
405
- method_option 'unsorted', aliases: '-u', type: :boolean, desc: 'Output is not sorted.'
405
+ desc "groupies [USER]", "Returns the list of people who follow you but you don't follow back."
406
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
407
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
408
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
409
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
410
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
411
+ 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"
412
+ method_option "unsorted", aliases: "-u", type: :boolean, desc: "Output is not sorted."
406
413
  def groupies(user = nil)
407
414
  user = if user
408
- require 't/core_ext/string'
409
- options['id'] ? user.to_i : user.strip_ats
415
+ require "t/core_ext/string"
416
+ options["id"] ? user.to_i : user.strip_ats
410
417
  else
411
418
  client.verify_credentials.screen_name
412
419
  end
413
420
  follower_ids = Thread.new { client.follower_ids(user).to_a }
414
421
  following_ids = Thread.new { client.friend_ids(user).to_a }
415
422
  groupie_ids = (follower_ids.value - following_ids.value)
416
- require 'retryable'
423
+ require "retryable"
417
424
  users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
418
425
  client.users(groupie_ids)
419
426
  end
420
427
  print_users(users)
421
428
  end
422
- map %w(disciples) => :groupies
423
-
424
- desc 'intersection USER [USER...]', 'Displays the intersection of users followed by the specified users.'
425
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
426
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify input as Twitter user IDs instead of screen names.'
427
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
428
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
429
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
430
- 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'
431
- method_option 'type', aliases: '-t', type: :string, enum: %w(followers followings), default: 'followings', desc: 'Specify the type of intersection.'
432
- method_option 'unsorted', aliases: '-u', type: :boolean, desc: 'Output is not sorted.'
429
+ map %w[disciples] => :groupies
430
+
431
+ desc "intersection USER [USER...]", "Displays the intersection of users followed by the specified users."
432
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
433
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify input as Twitter user IDs instead of screen names."
434
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
435
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
436
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
437
+ 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"
438
+ method_option "type", aliases: "-t", type: :string, enum: %w[followers followings], default: "followings", desc: "Specify the type of intersection."
439
+ method_option "unsorted", aliases: "-u", type: :boolean, desc: "Output is not sorted."
433
440
  def intersection(first_user, *users)
434
441
  users.push(first_user)
435
442
  # If only one user is specified, compare to the authenticated user
436
443
  users.push(@rcfile.active_profile[0]) if users.size == 1
437
- require 't/core_ext/string'
438
- options['id'] ? users.collect!(&:to_i) : users.collect!(&:strip_ats)
444
+ require "t/core_ext/string"
445
+ options["id"] ? users.collect!(&:to_i) : users.collect!(&:strip_ats)
439
446
  sets = parallel_map(users) do |user|
440
- case options['type']
441
- when 'followings'
447
+ case options["type"]
448
+ when "followings"
442
449
  client.friend_ids(user).to_a
443
- when 'followers'
450
+ when "followers"
444
451
  client.follower_ids(user).to_a
445
452
  end
446
453
  end
447
454
  intersection = sets.reduce(:&)
448
- require 'retryable'
455
+ require "retryable"
449
456
  users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
450
457
  client.users(intersection)
451
458
  end
452
459
  print_users(users)
453
460
  end
454
- map %w(overlap) => :intersection
461
+ map %w[overlap] => :intersection
455
462
 
456
- desc 'leaders [USER]', "Returns the list of people who you follow but don't follow you back."
457
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
458
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify user via ID instead of screen name.'
459
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
460
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
461
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
462
- 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'
463
- method_option 'unsorted', aliases: '-u', type: :boolean, desc: 'Output is not sorted.'
463
+ desc "leaders [USER]", "Returns the list of people who you follow but don't follow you back."
464
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
465
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
466
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
467
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
468
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
469
+ 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"
470
+ method_option "unsorted", aliases: "-u", type: :boolean, desc: "Output is not sorted."
464
471
  def leaders(user = nil)
465
472
  user = if user
466
- require 't/core_ext/string'
467
- options['id'] ? user.to_i : user.strip_ats
473
+ require "t/core_ext/string"
474
+ options["id"] ? user.to_i : user.strip_ats
468
475
  else
469
476
  client.verify_credentials.screen_name
470
477
  end
471
478
  following_ids = Thread.new { client.friend_ids(user).to_a }
472
479
  follower_ids = Thread.new { client.follower_ids(user).to_a }
473
480
  leader_ids = (following_ids.value - follower_ids.value)
474
- require 'retryable'
481
+ require "retryable"
475
482
  users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
476
483
  client.users(leader_ids)
477
484
  end
478
485
  print_users(users)
479
486
  end
480
487
 
481
- desc 'lists [USER]', 'Returns the lists created by a user.'
482
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
483
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify user via ID instead of screen name.'
484
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
485
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
486
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
487
- 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'
488
- method_option 'unsorted', aliases: '-u', type: :boolean, desc: 'Output is not sorted.'
488
+ desc "lists [USER]", "Returns the lists created by a user."
489
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
490
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
491
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
492
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
493
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
494
+ 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"
495
+ method_option "unsorted", aliases: "-u", type: :boolean, desc: "Output is not sorted."
489
496
  def lists(user = nil)
490
497
  lists = if user
491
- require 't/core_ext/string'
492
- user = options['id'] ? user.to_i : user.strip_ats
498
+ require "t/core_ext/string"
499
+ user = options["id"] ? user.to_i : user.strip_ats
493
500
  client.lists(user)
494
501
  else
495
502
  client.lists
@@ -497,35 +504,35 @@ module T
497
504
  print_lists(lists)
498
505
  end
499
506
 
500
- desc 'matrix', 'Unfortunately, no one can be told what the Matrix is. You have to see it for yourself.'
507
+ desc "matrix", "Unfortunately, no one can be told what the Matrix is. You have to see it for yourself."
501
508
  def matrix
502
509
  opts = {count: MAX_SEARCH_RESULTS, include_entities: false}
503
- tweets = client.search('lang:ja', opts)
510
+ tweets = client.search("lang:ja", opts)
504
511
  tweets.each do |tweet|
505
- say(tweet.text.gsub(/[^\u3000\u3040-\u309f]/, '').reverse, [:bold, :green, :on_black], false)
512
+ say(tweet.text.gsub(/[^\u3000\u3040-\u309f]/, "").reverse, %i[bold green on_black], false)
506
513
  end
507
514
  end
508
515
 
509
- desc 'mentions', "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets mentioning you."
510
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
511
- method_option 'decode_uris', aliases: '-d', type: :boolean, desc: 'Decodes t.co URLs into their original form.'
512
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
513
- method_option 'number', aliases: '-n', type: :numeric, default: DEFAULT_NUM_RESULTS, desc: 'Limit the number of results.'
514
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
515
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
516
+ desc "mentions", "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets mentioning you."
517
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
518
+ method_option "decode_uris", aliases: "-d", type: :boolean, desc: "Decodes t.co URLs into their original form."
519
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
520
+ method_option "number", aliases: "-n", type: :numeric, default: DEFAULT_NUM_RESULTS, desc: "Limit the number of results."
521
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
522
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
516
523
  def mentions
517
- count = options['number'] || DEFAULT_NUM_RESULTS
524
+ count = options["number"] || DEFAULT_NUM_RESULTS
518
525
  opts = {}
519
- opts[:include_entities] = !!options['decode_uris']
526
+ opts[:include_entities] = !!options["decode_uris"]
520
527
  tweets = collect_with_count(count) do |count_opts|
521
528
  client.mentions(count_opts.merge(opts))
522
529
  end
523
530
  print_tweets(tweets)
524
531
  end
525
- map %w(replies) => :mentions
532
+ map %w[replies] => :mentions
526
533
 
527
- desc 'mute USER [USER...]', 'Mute users.'
528
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify input as Twitter user IDs instead of screen names.'
534
+ desc "mute USER [USER...]", "Mute users."
535
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify input as Twitter user IDs instead of screen names."
529
536
  def mute(user, *users)
530
537
  muted_users, number = fetch_users(users.unshift(user), options) do |users_to_mute|
531
538
  client.mute(users_to_mute)
@@ -535,49 +542,47 @@ module T
535
542
  say "Run `#{File.basename($PROGRAM_NAME)} delete mute #{muted_users.collect { |muted_user| "@#{muted_user.screen_name}" }.join(' ')}` to unmute."
536
543
  end
537
544
 
538
- desc 'muted [USER]', 'Returns a list of the people you have muted on Twitter.'
539
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
540
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
541
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
542
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
543
- 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'
544
- method_option 'unsorted', aliases: '-u', type: :boolean, desc: 'Output is not sorted.'
545
+ desc "muted [USER]", "Returns a list of the people you have muted on Twitter."
546
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
547
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
548
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
549
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
550
+ 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"
551
+ method_option "unsorted", aliases: "-u", type: :boolean, desc: "Output is not sorted."
545
552
  def muted
546
553
  muted_ids = client.muted_ids.to_a
547
- require 'retryable'
554
+ require "retryable"
548
555
  muted_users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
549
556
  client.users(muted_ids)
550
557
  end
551
558
  print_users(muted_users)
552
559
  end
553
- map %w(mutes) => :muted
560
+ map %w[mutes] => :muted
554
561
 
555
- desc 'open USER', "Opens that user's profile in a web browser."
556
- method_option 'display-uri', aliases: '-d', type: :boolean, desc: 'Display the requested URL instead of attempting to open it.'
557
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify user via ID instead of screen name.'
558
- method_option 'status', aliases: '-s', type: :boolean, desc: 'Specify input as a Twitter status ID instead of a screen name.'
562
+ desc "open USER", "Opens that user's profile in a web browser."
563
+ method_option "display-uri", aliases: "-d", type: :boolean, desc: "Display the requested URL instead of attempting to open it."
564
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
565
+ method_option "status", aliases: "-s", type: :boolean, desc: "Specify input as a Twitter status ID instead of a screen name."
559
566
  def open(user)
560
- require 'launchy'
561
- if options['id']
567
+ require "launchy"
568
+ if options["id"]
562
569
  user = client.user(user.to_i)
563
- open_or_print(user.uri, dry_run: options['display-uri'])
564
- elsif options['status']
570
+ open_or_print(user.uri, dry_run: options["display-uri"])
571
+ elsif options["status"]
565
572
  status = client.status(user.to_i, include_my_retweet: false)
566
- open_or_print(status.uri, dry_run: options['display-uri'])
573
+ open_or_print(status.uri, dry_run: options["display-uri"])
567
574
  else
568
- require 't/core_ext/string'
569
- open_or_print("https://twitter.com/#{user.strip_ats}", dry_run: options['display-uri'])
575
+ require "t/core_ext/string"
576
+ open_or_print("https://twitter.com/#{user.strip_ats}", dry_run: options["display-uri"])
570
577
  end
571
578
  end
572
579
 
573
- desc 'reach TWEET_ID', 'Shows the maximum number of people who may have seen the specified tweet in their timeline.'
580
+ desc "reach TWEET_ID", "Shows the maximum number of people who may have seen the specified tweet in their timeline."
574
581
  def reach(tweet_id)
575
- require 't/core_ext/string'
576
- require 'set'
582
+ require "t/core_ext/string"
577
583
  status_thread = Thread.new { client.status(tweet_id.to_i, include_my_retweet: false) }
578
- threads = []
579
- client.retweeters_ids(tweet_id.to_i).each do |retweeter_id|
580
- threads << Thread.new(retweeter_id) do |user_id|
584
+ threads = client.retweeters_ids(tweet_id.to_i).collect do |retweeter_id|
585
+ Thread.new(retweeter_id) do |user_id|
581
586
  client.follower_ids(user_id).to_a
582
587
  end
583
588
  end
@@ -591,25 +596,25 @@ module T
591
596
  say number_with_delimiter(reach.size)
592
597
  end
593
598
 
594
- desc 'reply TWEET_ID [MESSAGE]', 'Post your Tweet as a reply directed at another person.'
595
- method_option 'all', aliases: '-a', type: :boolean, desc: 'Reply to all users mentioned in the Tweet.'
596
- method_option 'location', aliases: '-l', type: :string, default: nil, desc: "Add location information. If the optional 'latitude,longitude' parameter is not supplied, looks up location by IP address."
597
- method_option 'file', aliases: '-f', type: :string, desc: 'The path to an image to attach to your tweet.'
599
+ desc "reply TWEET_ID [MESSAGE]", "Post your Tweet as a reply directed at another person."
600
+ method_option "all", aliases: "-a", type: :boolean, desc: "Reply to all users mentioned in the Tweet."
601
+ method_option "location", aliases: "-l", type: :string, default: nil, desc: "Add location information. If the optional 'latitude,longitude' parameter is not supplied, looks up location by IP address."
602
+ method_option "file", aliases: "-f", type: :string, desc: "The path to an image to attach to your tweet."
598
603
  def reply(status_id, message = nil)
599
604
  message = T::Editor.gets if message.to_s.empty?
600
605
  status = client.status(status_id.to_i, include_my_retweet: false)
601
606
  users = Array(status.user.screen_name)
602
- if options['all']
607
+ if options["all"]
603
608
  users += extract_mentioned_screen_names(status.full_text)
604
609
  users.uniq!
605
610
  end
606
611
  users.delete(@rcfile.active_profile[0])
607
- require 't/core_ext/string'
612
+ require "t/core_ext/string"
608
613
  users.collect!(&:prepend_at)
609
614
  opts = {in_reply_to_status_id: status.id, trim_user: true}
610
615
  add_location!(options, opts)
611
- reply = if options['file']
612
- client.update_with_media("#{users.join(' ')} #{message}", File.new(File.expand_path(options['file'])), opts)
616
+ reply = if options["file"]
617
+ client.update_with_media("#{users.join(' ')} #{message}", File.new(File.expand_path(options["file"])), opts)
613
618
  else
614
619
  client.update("#{users.join(' ')} #{message}", opts)
615
620
  end
@@ -618,21 +623,21 @@ module T
618
623
  say "Run `#{File.basename($PROGRAM_NAME)} delete status #{reply.id}` to delete."
619
624
  end
620
625
 
621
- desc 'report_spam USER [USER...]', 'Report users for spam.'
622
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify input as Twitter user IDs instead of screen names.'
626
+ desc "report_spam USER [USER...]", "Report users for spam."
627
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify input as Twitter user IDs instead of screen names."
623
628
  def report_spam(user, *users)
624
629
  _, number = fetch_users(users.unshift(user), options) do |users_to_report|
625
630
  client.report_spam(users_to_report)
626
631
  end
627
632
  say "@#{@rcfile.active_profile[0]} reported #{pluralize(number, 'user')}."
628
633
  end
629
- map %w(report reportspam spam) => :report_spam
634
+ map %w[report reportspam spam] => :report_spam
630
635
 
631
- desc 'retweet TWEET_ID [TWEET_ID...]', 'Sends Tweets to your followers.'
636
+ desc "retweet TWEET_ID [TWEET_ID...]", "Sends Tweets to your followers."
632
637
  def retweet(status_id, *status_ids)
633
638
  status_ids.unshift(status_id)
634
639
  status_ids.collect!(&:to_i)
635
- require 'retryable'
640
+ require "retryable"
636
641
  retweets = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
637
642
  client.retweet(status_ids, trim_user: true)
638
643
  end
@@ -641,23 +646,23 @@ module T
641
646
  say
642
647
  say "Run `#{File.basename($PROGRAM_NAME)} delete status #{retweets.collect(&:id).join(' ')}` to undo."
643
648
  end
644
- map %w(rt) => :retweet
649
+ map %w[rt] => :retweet
645
650
 
646
- desc 'retweets [USER]', "Returns the #{DEFAULT_NUM_RESULTS} most recent Retweets by a user."
647
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
648
- method_option 'decode_uris', aliases: '-d', type: :boolean, desc: 'Decodes t.co URLs into their original form.'
649
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify user via ID instead of screen name.'
650
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
651
- method_option 'number', aliases: '-n', type: :numeric, default: DEFAULT_NUM_RESULTS, desc: 'Limit the number of results.'
652
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
653
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
651
+ desc "retweets [USER]", "Returns the #{DEFAULT_NUM_RESULTS} most recent Retweets by a user."
652
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
653
+ method_option "decode_uris", aliases: "-d", type: :boolean, desc: "Decodes t.co URLs into their original form."
654
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
655
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
656
+ method_option "number", aliases: "-n", type: :numeric, default: DEFAULT_NUM_RESULTS, desc: "Limit the number of results."
657
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
658
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
654
659
  def retweets(user = nil)
655
- count = options['number'] || DEFAULT_NUM_RESULTS
660
+ count = options["number"] || DEFAULT_NUM_RESULTS
656
661
  opts = {}
657
- opts[:include_entities] = !!options['decode_uris']
662
+ opts[:include_entities] = !!options["decode_uris"]
658
663
  tweets = if user
659
- require 't/core_ext/string'
660
- user = options['id'] ? user.to_i : user.strip_ats
664
+ require "t/core_ext/string"
665
+ user = options["id"] ? user.to_i : user.strip_ats
661
666
  collect_with_count(count) do |count_opts|
662
667
  client.retweeted_by_user(user, count_opts.merge(opts))
663
668
  end
@@ -668,51 +673,51 @@ module T
668
673
  end
669
674
  print_tweets(tweets)
670
675
  end
671
- map %w(rts) => :retweets
676
+ map %w[rts] => :retweets
672
677
 
673
- desc 'retweets_of_me', "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets of the authenticated user that have been retweeted by others."
674
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
675
- method_option 'decode_uris', aliases: '-d', type: :boolean, desc: 'Decodes t.co URLs into their original form.'
676
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
677
- method_option 'number', aliases: '-n', type: :numeric, default: DEFAULT_NUM_RESULTS, desc: 'Limit the number of results.'
678
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
679
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
678
+ desc "retweets_of_me", "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets of the authenticated user that have been retweeted by others."
679
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
680
+ method_option "decode_uris", aliases: "-d", type: :boolean, desc: "Decodes t.co URLs into their original form."
681
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
682
+ method_option "number", aliases: "-n", type: :numeric, default: DEFAULT_NUM_RESULTS, desc: "Limit the number of results."
683
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
684
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
680
685
  def retweets_of_me
681
- count = options['number'] || DEFAULT_NUM_RESULTS
686
+ count = options["number"] || DEFAULT_NUM_RESULTS
682
687
  opts = {}
683
- opts[:include_entities] = !!options['decode_uris']
688
+ opts[:include_entities] = !!options["decode_uris"]
684
689
  tweets = collect_with_count(count) do |count_opts|
685
690
  client.retweets_of_me(count_opts.merge(opts))
686
691
  end
687
692
  print_tweets(tweets)
688
693
  end
689
- map %w(retweetsofme) => :retweets_of_me
694
+ map %w[retweetsofme] => :retweets_of_me
690
695
 
691
- desc 'ruler', 'Prints a 140-character ruler'
692
- method_option 'indent', aliases: '-i', type: :numeric, default: 0, desc: 'The number of spaces to print before the ruler.'
696
+ desc "ruler", "Prints a 140-character ruler"
697
+ method_option "indent", aliases: "-i", type: :numeric, default: 0, desc: "The number of spaces to print before the ruler."
693
698
  def ruler
694
- markings = '----|'.chars.cycle.take(140).join
699
+ markings = "----|".chars.cycle.take(140).join
695
700
  say "#{' ' * options['indent'].to_i}#{markings}"
696
701
  end
697
702
 
698
- desc 'status TWEET_ID', 'Retrieves detailed information about a Tweet.'
699
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
700
- method_option 'decode_uris', aliases: '-d', type: :boolean, desc: 'Decodes t.co URLs into their original form.'
701
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
702
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
703
- def status(status_id) # rubocop:disable CyclomaticComplexity
703
+ desc "status TWEET_ID", "Retrieves detailed information about a Tweet."
704
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
705
+ method_option "decode_uris", aliases: "-d", type: :boolean, desc: "Decodes t.co URLs into their original form."
706
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
707
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
708
+ def status(status_id) # rubocop:disable Metrics/CyclomaticComplexity
704
709
  opts = {include_my_retweet: false}
705
- opts[:include_entities] = !!options['decode_uris']
710
+ opts[:include_entities] = !!options["decode_uris"]
706
711
  status = client.status(status_id.to_i, opts)
707
712
  location = if status.place?
708
713
  if status.place.name? && status.place.attributes? && status.place.attributes[:street_address] && status.place.attributes[:locality] && status.place.attributes[:region] && status.place.country?
709
- [status.place.name, status.place.attributes[:street_address], status.place.attributes[:locality], status.place.attributes[:region], status.place.country].join(', ')
714
+ [status.place.name, status.place.attributes[:street_address], status.place.attributes[:locality], status.place.attributes[:region], status.place.country].join(", ")
710
715
  elsif status.place.name? && status.place.attributes? && status.place.attributes[:locality] && status.place.attributes[:region] && status.place.country?
711
- [status.place.name, status.place.attributes[:locality], status.place.attributes[:region], status.place.country].join(', ')
716
+ [status.place.name, status.place.attributes[:locality], status.place.attributes[:region], status.place.country].join(", ")
712
717
  elsif status.place.full_name? && status.place.attributes? && status.place.attributes[:region] && status.place.country?
713
- [status.place.full_name, status.place.attributes[:region], status.place.country].join(', ')
718
+ [status.place.full_name, status.place.attributes[:region], status.place.country].join(", ")
714
719
  elsif status.place.full_name? && status.place.country?
715
- [status.place.full_name, status.place.country].join(', ')
720
+ [status.place.full_name, status.place.country].join(", ")
716
721
  elsif status.place.full_name?
717
722
  status.place.full_name
718
723
  else
@@ -721,51 +726,51 @@ module T
721
726
  elsif status.geo?
722
727
  reverse_geocode(status.geo)
723
728
  end
724
- status_headings = ['ID', 'Posted at', 'Screen name', 'Text', 'Retweets', 'Favorites', 'Source', 'Location']
725
- if options['csv']
726
- require 'csv'
729
+ status_headings = ["ID", "Posted at", "Screen name", "Text", "Retweets", "Favorites", "Source", "Location"]
730
+ if options["csv"]
731
+ require "csv"
727
732
  say status_headings.to_csv
728
- 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
729
- elsif options['long']
730
- 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]
731
- format = options['format'] || Array.new(status_headings.size) { '%s' }
733
+ 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
734
+ elsif options["long"]
735
+ 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]
736
+ format = options["format"] || Array.new(status_headings.size) { "%s" }
732
737
  print_table_with_headings([array], status_headings, format)
733
738
  else
734
739
  array = []
735
- array << ['ID', status.id.to_s]
736
- array << ['Text', decode_full_text(status, options['decode_uris']).gsub(/\n+/, ' ')]
737
- array << ['Screen name', "@#{status.user.screen_name}"]
738
- array << ['Posted at', "#{ls_formatted_time(status, :created_at, false)} (#{time_ago_in_words(status.created_at)} ago)"]
739
- array << ['Retweets', number_with_delimiter(status.retweet_count)]
740
- array << ['Favorites', number_with_delimiter(status.favorite_count)]
741
- array << ['Source', strip_tags(status.source)]
742
- array << ['Location', location] unless location.nil?
740
+ array << ["ID", status.id.to_s]
741
+ array << ["Text", decode_full_text(status, options["decode_uris"]).gsub(/\n+/, " ")]
742
+ array << ["Screen name", "@#{status.user.screen_name}"]
743
+ array << ["Posted at", "#{ls_formatted_time(status, :created_at, false)} (#{time_ago_in_words(status.created_at)} ago)"]
744
+ array << ["Retweets", number_with_delimiter(status.retweet_count)]
745
+ array << ["Favorites", number_with_delimiter(status.favorite_count)]
746
+ array << ["Source", strip_tags(status.source)]
747
+ array << ["Location", location] unless location.nil?
743
748
  print_table(array)
744
749
  end
745
750
  end
746
751
 
747
- desc 'timeline [USER]', "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets posted by a user."
748
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
749
- method_option 'decode_uris', aliases: '-d', type: :boolean, desc: 'Decodes t.co URLs into their original form.'
750
- method_option 'exclude', aliases: '-e', type: :string, enum: %w(replies retweets), desc: 'Exclude certain types of Tweets from the results.', banner: 'TYPE'
751
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify user via ID instead of screen name.'
752
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
753
- method_option 'max_id', aliases: '-m', type: :numeric, desc: 'Returns only the results with an ID less than the specified ID.'
754
- method_option 'number', aliases: '-n', type: :numeric, default: DEFAULT_NUM_RESULTS, desc: 'Limit the number of results.'
755
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
756
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
757
- method_option 'since_id', aliases: '-s', type: :numeric, desc: 'Returns only the results with an ID greater than the specified ID.'
752
+ desc "timeline [USER]", "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets posted by a user."
753
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
754
+ method_option "decode_uris", aliases: "-d", type: :boolean, desc: "Decodes t.co URLs into their original form."
755
+ method_option "exclude", aliases: "-e", type: :string, enum: %w[replies retweets], desc: "Exclude certain types of Tweets from the results.", banner: "TYPE"
756
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
757
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
758
+ method_option "max_id", aliases: "-m", type: :numeric, desc: "Returns only the results with an ID less than the specified ID."
759
+ method_option "number", aliases: "-n", type: :numeric, default: DEFAULT_NUM_RESULTS, desc: "Limit the number of results."
760
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
761
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
762
+ method_option "since_id", aliases: "-s", type: :numeric, desc: "Returns only the results with an ID greater than the specified ID."
758
763
  def timeline(user = nil)
759
- count = options['number'] || DEFAULT_NUM_RESULTS
764
+ count = options["number"] || DEFAULT_NUM_RESULTS
760
765
  opts = {}
761
- opts[:exclude_replies] = true if options['exclude'] == 'replies'
762
- opts[:include_entities] = !!options['decode_uris']
763
- opts[:include_rts] = false if options['exclude'] == 'retweets'
764
- opts[:max_id] = options['max_id'] if options['max_id']
765
- opts[:since_id] = options['since_id'] if options['since_id']
766
+ opts[:exclude_replies] = true if options["exclude"] == "replies"
767
+ opts[:include_entities] = !!options["decode_uris"]
768
+ opts[:include_rts] = false if options["exclude"] == "retweets"
769
+ opts[:max_id] = options["max_id"] if options["max_id"]
770
+ opts[:since_id] = options["since_id"] if options["since_id"]
766
771
  if user
767
- require 't/core_ext/string'
768
- user = options['id'] ? user.to_i : user.strip_ats
772
+ require "t/core_ext/string"
773
+ user = options["id"] ? user.to_i : user.strip_ats
769
774
  tweets = collect_with_count(count) do |count_opts|
770
775
  client.user_timeline(user, count_opts.merge(opts))
771
776
  end
@@ -776,61 +781,61 @@ module T
776
781
  end
777
782
  print_tweets(tweets)
778
783
  end
779
- map %w(tl) => :timeline
784
+ map %w[tl] => :timeline
780
785
 
781
- desc 'trends [WOEID]', 'Returns the top 50 trending topics.'
782
- method_option 'exclude-hashtags', aliases: '-x', type: :boolean, desc: 'Remove all hashtags from the trends list.'
786
+ desc "trends [WOEID]", "Returns the top 50 trending topics."
787
+ method_option "exclude-hashtags", aliases: "-x", type: :boolean, desc: "Remove all hashtags from the trends list."
783
788
  def trends(woe_id = 1)
784
789
  opts = {}
785
- opts[:exclude] = 'hashtags' if options['exclude-hashtags']
790
+ opts[:exclude] = "hashtags" if options["exclude-hashtags"]
786
791
  trends = client.trends(woe_id, opts)
787
792
  print_attribute(trends, :name)
788
793
  end
789
794
 
790
- desc 'trend_locations', 'Returns the locations for which Twitter has trending topic information.'
791
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
792
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
793
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
794
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
795
- 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'
796
- method_option 'unsorted', aliases: '-u', type: :boolean, desc: 'Output is not sorted.'
795
+ desc "trend_locations", "Returns the locations for which Twitter has trending topic information."
796
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
797
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
798
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
799
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
800
+ 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"
801
+ method_option "unsorted", aliases: "-u", type: :boolean, desc: "Output is not sorted."
797
802
  def trend_locations
798
803
  places = client.trend_locations
799
- unless options['unsorted']
800
- places = case options['sort']
801
- when 'country'
804
+ unless options["unsorted"]
805
+ places = case options["sort"]
806
+ when "country"
802
807
  places.sort_by { |place| place.country.downcase }
803
- when 'parent'
808
+ when "parent"
804
809
  places.sort_by { |place| place.parent_id.to_i }
805
- when 'type'
810
+ when "type"
806
811
  places.sort_by { |place| place.place_type.downcase }
807
- when 'woeid'
812
+ when "woeid"
808
813
  places.sort_by { |place| place.woeid.to_i }
809
814
  else
810
815
  places.sort_by { |place| place.name.downcase }
811
816
  end
812
817
  end
813
- places.reverse! if options['reverse']
814
- if options['csv']
815
- require 'csv'
818
+ places.reverse! if options["reverse"]
819
+ if options["csv"]
820
+ require "csv"
816
821
  say TREND_HEADINGS.to_csv unless places.empty?
817
822
  places.each do |place|
818
823
  say [place.woeid, place.parent_id, place.place_type, place.name, place.country].to_csv
819
824
  end
820
- elsif options['long']
825
+ elsif options["long"]
821
826
  array = places.collect do |place|
822
827
  [place.woeid, place.parent_id, place.place_type, place.name, place.country]
823
828
  end
824
- format = options['format'] || Array.new(TREND_HEADINGS.size) { '%s' }
829
+ format = options["format"] || Array.new(TREND_HEADINGS.size) { "%s" }
825
830
  print_table_with_headings(array, TREND_HEADINGS, format)
826
831
  else
827
832
  print_attribute(places, :name)
828
833
  end
829
834
  end
830
- map %w(locations trendlocations) => :trend_locations
835
+ map %w[locations trendlocations] => :trend_locations
831
836
 
832
- desc 'unfollow USER [USER...]', 'Allows you to stop following users.'
833
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify input as Twitter user IDs instead of screen names.'
837
+ desc "unfollow USER [USER...]", "Allows you to stop following users."
838
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify input as Twitter user IDs instead of screen names."
834
839
  def unfollow(user, *users)
835
840
  unfollowed_users, number = fetch_users(users.unshift(user), options) do |users_to_unfollow|
836
841
  client.unfollow(users_to_unfollow)
@@ -840,15 +845,15 @@ module T
840
845
  say "Run `#{File.basename($PROGRAM_NAME)} follow #{unfollowed_users.collect { |unfollowed_user| "@#{unfollowed_user.screen_name}" }.join(' ')}` to follow again."
841
846
  end
842
847
 
843
- desc 'update [MESSAGE]', 'Post a Tweet.'
844
- method_option 'location', aliases: '-l', type: :string, default: nil, desc: "Add location information. If the optional 'latitude,longitude' parameter is not supplied, looks up location by IP address."
845
- method_option 'file', aliases: '-f', type: :string, desc: 'The path to an image to attach to your tweet.'
848
+ desc "update [MESSAGE]", "Post a Tweet."
849
+ method_option "location", aliases: "-l", type: :string, default: nil, desc: "Add location information. If the optional 'latitude,longitude' parameter is not supplied, looks up location by IP address."
850
+ method_option "file", aliases: "-f", type: :string, desc: "The path to an image to attach to your tweet."
846
851
  def update(message = nil)
847
852
  message = T::Editor.gets if message.to_s.empty?
848
853
  opts = {trim_user: true}
849
854
  add_location!(options, opts)
850
- status = if options['file']
851
- client.update_with_media(message, File.new(File.expand_path(options['file'])), opts)
855
+ status = if options["file"]
856
+ client.update_with_media(message, File.new(File.expand_path(options["file"])), opts)
852
857
  else
853
858
  client.update(message, opts)
854
859
  end
@@ -856,102 +861,102 @@ module T
856
861
  say
857
862
  say "Run `#{File.basename($PROGRAM_NAME)} delete status #{status.id}` to delete."
858
863
  end
859
- map %w(post tweet) => :update
864
+ map %w[post tweet] => :update
860
865
 
861
- desc 'users USER [USER...]', 'Returns a list of users you specify.'
862
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
863
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify input as Twitter user IDs instead of screen names.'
864
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
865
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
866
- method_option 'reverse', aliases: '-r', type: :boolean, desc: 'Reverse the order of the sort.'
867
- 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'
868
- method_option 'unsorted', aliases: '-u', type: :boolean, desc: 'Output is not sorted.'
866
+ desc "users USER [USER...]", "Returns a list of users you specify."
867
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
868
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify input as Twitter user IDs instead of screen names."
869
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
870
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
871
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
872
+ 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"
873
+ method_option "unsorted", aliases: "-u", type: :boolean, desc: "Output is not sorted."
869
874
  def users(user, *users)
870
875
  users.unshift(user)
871
- require 't/core_ext/string'
872
- options['id'] ? users.collect!(&:to_i) : users.collect!(&:strip_ats)
873
- require 'retryable'
876
+ require "t/core_ext/string"
877
+ options["id"] ? users.collect!(&:to_i) : users.collect!(&:strip_ats)
878
+ require "retryable"
874
879
  users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
875
880
  client.users(users)
876
881
  end
877
882
  print_users(users)
878
883
  end
879
- map %w(stats) => :users
884
+ map %w[stats] => :users
880
885
 
881
- desc 'version', 'Show version.'
886
+ desc "version", "Show version."
882
887
  def version
883
- require 't/version'
888
+ require "t/version"
884
889
  say T::Version
885
890
  end
886
- map %w(-v --version) => :version
891
+ map %w[-v --version] => :version
887
892
 
888
- desc 'whois USER', 'Retrieves profile information for the user.'
889
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
890
- method_option 'decode_uris', aliases: '-d', type: :boolean, desc: 'Decodes t.co URLs into their original form.'
891
- method_option 'id', aliases: '-i', type: :boolean, desc: 'Specify user via ID instead of screen name.'
892
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
893
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
893
+ desc "whois USER", "Retrieves profile information for the user."
894
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
895
+ method_option "decode_uris", aliases: "-d", type: :boolean, desc: "Decodes t.co URLs into their original form."
896
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
897
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
898
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
894
899
  def whois(user)
895
- require 't/core_ext/string'
900
+ require "t/core_ext/string"
896
901
  opts = {}
897
- opts[:include_entities] = !!options['decode_uris']
898
- user = options['id'] ? user.to_i : user.strip_ats
902
+ opts[:include_entities] = !!options["decode_uris"]
903
+ user = options["id"] ? user.to_i : user.strip_ats
899
904
  user = client.user(user, opts)
900
- if options['csv'] || options['long']
905
+ if options["csv"] || options["long"]
901
906
  print_users([user])
902
907
  else
903
908
  array = []
904
- array << ['ID', user.id.to_s]
905
- array << ['Since', "#{ls_formatted_time(user, :created_at, false)} (#{time_ago_in_words(user.created_at)} ago)"]
906
- 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?
907
- array << ['Screen name', "@#{user.screen_name}"]
908
- array << [user.verified? ? 'Name (Verified)' : 'Name', user.name] unless user.name.nil? # rubocop:disable BlockNesting
909
- array << ['Tweets', number_with_delimiter(user.statuses_count)]
910
- array << ['Favorites', number_with_delimiter(user.favorites_count)]
911
- array << ['Listed', number_with_delimiter(user.listed_count)]
912
- array << ['Following', number_with_delimiter(user.friends_count)]
913
- array << ['Followers', number_with_delimiter(user.followers_count)]
914
- array << ['Bio', user.description.gsub(/\n+/, ' ')] unless user.description.nil?
915
- array << ['Location', user.location] unless user.location.nil?
916
- array << ['URL', user.website] unless user.website.nil?
909
+ array << ["ID", user.id.to_s]
910
+ array << ["Since", "#{ls_formatted_time(user, :created_at, false)} (#{time_ago_in_words(user.created_at)} ago)"]
911
+ 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?
912
+ array << ["Screen name", "@#{user.screen_name}"]
913
+ array << [user.verified? ? "Name (Verified)" : "Name", user.name] unless user.name.nil? # rubocop:disable Metrics/BlockNesting
914
+ array << ["Tweets", number_with_delimiter(user.statuses_count)]
915
+ array << ["Favorites", number_with_delimiter(user.favorites_count)]
916
+ array << ["Listed", number_with_delimiter(user.listed_count)]
917
+ array << ["Following", number_with_delimiter(user.friends_count)]
918
+ array << ["Followers", number_with_delimiter(user.followers_count)]
919
+ array << ["Bio", user.description.gsub(/\n+/, " ")] unless user.description.nil?
920
+ array << ["Location", user.location] unless user.location.nil?
921
+ array << ["URL", user.website] unless user.website.nil?
917
922
  print_table(array)
918
923
  end
919
924
  end
920
- map %w(user) => :whois
925
+ map %w[user] => :whois
921
926
 
922
- desc 'whoami', 'Retrieves profile information for the authenticated user.'
923
- method_option 'csv', aliases: '-c', type: :boolean, desc: 'Output in CSV format.'
924
- method_option 'decode_uris', aliases: '-d', type: :boolean, desc: 'Decodes t.co URLs into their original form.'
925
- method_option 'long', aliases: '-l', type: :boolean, desc: 'Output in long format.'
926
- method_option 'relative_dates', aliases: '-a', type: :boolean, desc: 'Show relative dates.'
927
+ desc "whoami", "Retrieves profile information for the authenticated user."
928
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
929
+ method_option "decode_uris", aliases: "-d", type: :boolean, desc: "Decodes t.co URLs into their original form."
930
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
931
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
927
932
  def whoami
928
933
  if @rcfile.active_profile && @rcfile.active_profile[0]
929
934
  user = @rcfile.active_profile[0]
930
935
  whois(user)
931
936
  else
932
- $stderr.puts "You haven't authorized an account, run `t authorize` to get started."
937
+ warn "You haven't authorized an account, run `t authorize` to get started."
933
938
  end
934
939
  end
935
940
 
936
- desc 'delete SUBCOMMAND ...ARGS', 'Delete Tweets, Direct Messages, etc.'
937
- subcommand 'delete', T::Delete
941
+ desc "delete SUBCOMMAND ...ARGS", "Delete Tweets, Direct Messages, etc."
942
+ subcommand "delete", T::Delete
938
943
 
939
- desc 'list SUBCOMMAND ...ARGS', 'Do various things with lists.'
940
- subcommand 'list', T::List
944
+ desc "list SUBCOMMAND ...ARGS", "Do various things with lists."
945
+ subcommand "list", T::List
941
946
 
942
- desc 'search SUBCOMMAND ...ARGS', 'Search through Tweets.'
943
- subcommand 'search', T::Search
947
+ desc "search SUBCOMMAND ...ARGS", "Search through Tweets."
948
+ subcommand "search", T::Search
944
949
 
945
- desc 'set SUBCOMMAND ...ARGS', 'Change various account settings.'
946
- subcommand 'set', T::Set
950
+ desc "set SUBCOMMAND ...ARGS", "Change various account settings."
951
+ subcommand "set", T::Set
947
952
 
948
- desc 'stream SUBCOMMAND ...ARGS', 'Commands for streaming Tweets.'
949
- subcommand 'stream', T::Stream
953
+ desc "stream SUBCOMMAND ...ARGS", "Commands for streaming Tweets."
954
+ subcommand "stream", T::Stream
950
955
 
951
956
  private
952
957
 
953
958
  def extract_mentioned_screen_names(text)
954
- valid_mention_preceding_chars = /(?:[^a-zA-Z0-9_!#\$%&*@@]|^|RT:?)/o
959
+ valid_mention_preceding_chars = /(?:[^a-zA-Z0-9_!#$%&*@@]|^|RT:?)/o
955
960
  at_signs = /[@@]/
956
961
  valid_mentions = /
957
962
  (#{valid_mention_preceding_chars}) # $1: Preceeding character
@@ -959,7 +964,7 @@ module T
959
964
  ([a-zA-Z0-9_]{1,20}) # $3: Screen name
960
965
  /ox
961
966
 
962
- return [] if text !~ at_signs
967
+ return [] unless text&.match?(at_signs)
963
968
 
964
969
  text.to_s.scan(valid_mentions).collect do |_, _, screen_name|
965
970
  screen_name
@@ -968,41 +973,43 @@ module T
968
973
 
969
974
  def generate_authorize_uri(consumer, request_token)
970
975
  request = consumer.create_signed_request(:get, consumer.authorize_path, request_token, pin_auth_parameters)
971
- params = request['Authorization'].sub(/^OAuth\s+/, '').split(/,\s+/).collect do |param|
972
- key, value = param.split('=')
976
+ params = request["Authorization"].sub(/^OAuth\s+/, "").split(/,\s+/).collect do |param|
977
+ key, value = param.split("=")
973
978
  value =~ /"(.*?)"/
974
979
  "#{key}=#{CGI.escape(Regexp.last_match[1])}"
975
- end.join('&')
976
- "#{Twitter::REST::Client::BASE_URL}#{request.path}?#{params}"
980
+ end.join("&")
981
+ "#{Twitter::REST::Request::BASE_URL}#{request.path}?#{params}"
977
982
  end
978
983
 
979
984
  def pin_auth_parameters
980
- {oauth_callback: 'oob'}
985
+ {oauth_callback: "oob"}
981
986
  end
982
987
 
983
988
  def add_location!(options, opts)
984
- return nil unless options['location']
985
- lat, lng = options['location'] == 'location' ? [location.lat, location.lng] : options['location'].split(',').collect(&:to_f)
986
- opts.merge!(lat: lat, long: lng)
989
+ return nil unless options["location"]
990
+
991
+ lat, lng = options["location"] == "location" ? [location.lat, location.lng] : options["location"].split(",").collect(&:to_f)
992
+ opts.merge!(lat:, long: lng)
987
993
  end
988
994
 
989
995
  def location
990
996
  return @location if @location
991
- require 'geokit'
992
- require 'open-uri'
993
- ip_address = Kernel.open('http://checkip.dyndns.org/') do |body|
997
+
998
+ require "geokit"
999
+ require "open-uri"
1000
+ ip_address = URI.open("http://checkip.dyndns.org/") do |body|
994
1001
  /(?:\d{1,3}\.){3}\d{1,3}/.match(body.read)[0]
995
1002
  end
996
1003
  @location = Geokit::Geocoders::MultiGeocoder.geocode(ip_address)
997
1004
  end
998
1005
 
999
1006
  def reverse_geocode(geo)
1000
- require 'geokit'
1007
+ require "geokit"
1001
1008
  geoloc = Geokit::Geocoders::MultiGeocoder.reverse_geocode(geo.coordinates)
1002
1009
  if geoloc.city && geoloc.state && geoloc.country
1003
- [geoloc.city, geoloc.state, geoloc.country].join(', ')
1010
+ [geoloc.city, geoloc.state, geoloc.country].join(", ")
1004
1011
  elsif geoloc.state && geoloc.country
1005
- [geoloc.state, geoloc.country].join(', ')
1012
+ [geoloc.state, geoloc.country].join(", ")
1006
1013
  else
1007
1014
  geoloc.country
1008
1015
  end