t 3.1.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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