t 3.1.0 → 4.0.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,45 +542,45 @@ 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"
583
+ require "set"
577
584
  status_thread = Thread.new { client.status(tweet_id.to_i, include_my_retweet: false) }
578
585
  threads = []
579
586
  client.retweeters_ids(tweet_id.to_i).each do |retweeter_id|
@@ -591,25 +598,25 @@ module T
591
598
  say number_with_delimiter(reach.size)
592
599
  end
593
600
 
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.'
601
+ desc "reply TWEET_ID [MESSAGE]", "Post your Tweet as a reply directed at another person."
602
+ method_option "all", aliases: "-a", type: :boolean, desc: "Reply to all users mentioned in the Tweet."
603
+ 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."
604
+ method_option "file", aliases: "-f", type: :string, desc: "The path to an image to attach to your tweet."
598
605
  def reply(status_id, message = nil)
599
606
  message = T::Editor.gets if message.to_s.empty?
600
607
  status = client.status(status_id.to_i, include_my_retweet: false)
601
608
  users = Array(status.user.screen_name)
602
- if options['all']
609
+ if options["all"]
603
610
  users += extract_mentioned_screen_names(status.full_text)
604
611
  users.uniq!
605
612
  end
606
613
  users.delete(@rcfile.active_profile[0])
607
- require 't/core_ext/string'
614
+ require "t/core_ext/string"
608
615
  users.collect!(&:prepend_at)
609
616
  opts = {in_reply_to_status_id: status.id, trim_user: true}
610
617
  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)
618
+ reply = if options["file"]
619
+ client.update_with_media("#{users.join(' ')} #{message}", File.new(File.expand_path(options["file"])), opts)
613
620
  else
614
621
  client.update("#{users.join(' ')} #{message}", opts)
615
622
  end
@@ -618,21 +625,21 @@ module T
618
625
  say "Run `#{File.basename($PROGRAM_NAME)} delete status #{reply.id}` to delete."
619
626
  end
620
627
 
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.'
628
+ desc "report_spam USER [USER...]", "Report users for spam."
629
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify input as Twitter user IDs instead of screen names."
623
630
  def report_spam(user, *users)
624
631
  _, number = fetch_users(users.unshift(user), options) do |users_to_report|
625
632
  client.report_spam(users_to_report)
626
633
  end
627
634
  say "@#{@rcfile.active_profile[0]} reported #{pluralize(number, 'user')}."
628
635
  end
629
- map %w(report reportspam spam) => :report_spam
636
+ map %w[report reportspam spam] => :report_spam
630
637
 
631
- desc 'retweet TWEET_ID [TWEET_ID...]', 'Sends Tweets to your followers.'
638
+ desc "retweet TWEET_ID [TWEET_ID...]", "Sends Tweets to your followers."
632
639
  def retweet(status_id, *status_ids)
633
640
  status_ids.unshift(status_id)
634
641
  status_ids.collect!(&:to_i)
635
- require 'retryable'
642
+ require "retryable"
636
643
  retweets = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
637
644
  client.retweet(status_ids, trim_user: true)
638
645
  end
@@ -641,23 +648,23 @@ module T
641
648
  say
642
649
  say "Run `#{File.basename($PROGRAM_NAME)} delete status #{retweets.collect(&:id).join(' ')}` to undo."
643
650
  end
644
- map %w(rt) => :retweet
651
+ map %w[rt] => :retweet
645
652
 
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.'
653
+ desc "retweets [USER]", "Returns the #{DEFAULT_NUM_RESULTS} most recent Retweets by a user."
654
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
655
+ method_option "decode_uris", aliases: "-d", type: :boolean, desc: "Decodes t.co URLs into their original form."
656
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
657
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
658
+ method_option "number", aliases: "-n", type: :numeric, default: DEFAULT_NUM_RESULTS, desc: "Limit the number of results."
659
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
660
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
654
661
  def retweets(user = nil)
655
- count = options['number'] || DEFAULT_NUM_RESULTS
662
+ count = options["number"] || DEFAULT_NUM_RESULTS
656
663
  opts = {}
657
- opts[:include_entities] = !!options['decode_uris']
664
+ opts[:include_entities] = !!options["decode_uris"]
658
665
  tweets = if user
659
- require 't/core_ext/string'
660
- user = options['id'] ? user.to_i : user.strip_ats
666
+ require "t/core_ext/string"
667
+ user = options["id"] ? user.to_i : user.strip_ats
661
668
  collect_with_count(count) do |count_opts|
662
669
  client.retweeted_by_user(user, count_opts.merge(opts))
663
670
  end
@@ -668,51 +675,51 @@ module T
668
675
  end
669
676
  print_tweets(tweets)
670
677
  end
671
- map %w(rts) => :retweets
678
+ map %w[rts] => :retweets
672
679
 
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.'
680
+ desc "retweets_of_me", "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets of the authenticated user that have been retweeted by others."
681
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
682
+ method_option "decode_uris", aliases: "-d", type: :boolean, desc: "Decodes t.co URLs into their original form."
683
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
684
+ method_option "number", aliases: "-n", type: :numeric, default: DEFAULT_NUM_RESULTS, desc: "Limit the number of results."
685
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
686
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
680
687
  def retweets_of_me
681
- count = options['number'] || DEFAULT_NUM_RESULTS
688
+ count = options["number"] || DEFAULT_NUM_RESULTS
682
689
  opts = {}
683
- opts[:include_entities] = !!options['decode_uris']
690
+ opts[:include_entities] = !!options["decode_uris"]
684
691
  tweets = collect_with_count(count) do |count_opts|
685
692
  client.retweets_of_me(count_opts.merge(opts))
686
693
  end
687
694
  print_tweets(tweets)
688
695
  end
689
- map %w(retweetsofme) => :retweets_of_me
696
+ map %w[retweetsofme] => :retweets_of_me
690
697
 
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.'
698
+ desc "ruler", "Prints a 140-character ruler"
699
+ method_option "indent", aliases: "-i", type: :numeric, default: 0, desc: "The number of spaces to print before the ruler."
693
700
  def ruler
694
- markings = '----|'.chars.cycle.take(140).join
701
+ markings = "----|".chars.cycle.take(140).join
695
702
  say "#{' ' * options['indent'].to_i}#{markings}"
696
703
  end
697
704
 
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
705
+ desc "status TWEET_ID", "Retrieves detailed information about a Tweet."
706
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
707
+ method_option "decode_uris", aliases: "-d", type: :boolean, desc: "Decodes t.co URLs into their original form."
708
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
709
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
710
+ def status(status_id) # rubocop:disable Metrics/CyclomaticComplexity
704
711
  opts = {include_my_retweet: false}
705
- opts[:include_entities] = !!options['decode_uris']
712
+ opts[:include_entities] = !!options["decode_uris"]
706
713
  status = client.status(status_id.to_i, opts)
707
714
  location = if status.place?
708
715
  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(', ')
716
+ [status.place.name, status.place.attributes[:street_address], status.place.attributes[:locality], status.place.attributes[:region], status.place.country].join(", ")
710
717
  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(', ')
718
+ [status.place.name, status.place.attributes[:locality], status.place.attributes[:region], status.place.country].join(", ")
712
719
  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(', ')
720
+ [status.place.full_name, status.place.attributes[:region], status.place.country].join(", ")
714
721
  elsif status.place.full_name? && status.place.country?
715
- [status.place.full_name, status.place.country].join(', ')
722
+ [status.place.full_name, status.place.country].join(", ")
716
723
  elsif status.place.full_name?
717
724
  status.place.full_name
718
725
  else
@@ -721,51 +728,51 @@ module T
721
728
  elsif status.geo?
722
729
  reverse_geocode(status.geo)
723
730
  end
724
- status_headings = ['ID', 'Posted at', 'Screen name', 'Text', 'Retweets', 'Favorites', 'Source', 'Location']
725
- if options['csv']
726
- require 'csv'
731
+ status_headings = ["ID", "Posted at", "Screen name", "Text", "Retweets", "Favorites", "Source", "Location"]
732
+ if options["csv"]
733
+ require "csv"
727
734
  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' }
735
+ 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
736
+ elsif options["long"]
737
+ 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]
738
+ format = options["format"] || Array.new(status_headings.size) { "%s" }
732
739
  print_table_with_headings([array], status_headings, format)
733
740
  else
734
741
  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?
742
+ array << ["ID", status.id.to_s]
743
+ array << ["Text", decode_full_text(status, options["decode_uris"]).gsub(/\n+/, " ")]
744
+ array << ["Screen name", "@#{status.user.screen_name}"]
745
+ array << ["Posted at", "#{ls_formatted_time(status, :created_at, false)} (#{time_ago_in_words(status.created_at)} ago)"]
746
+ array << ["Retweets", number_with_delimiter(status.retweet_count)]
747
+ array << ["Favorites", number_with_delimiter(status.favorite_count)]
748
+ array << ["Source", strip_tags(status.source)]
749
+ array << ["Location", location] unless location.nil?
743
750
  print_table(array)
744
751
  end
745
752
  end
746
753
 
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.'
754
+ desc "timeline [USER]", "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets posted by a user."
755
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
756
+ method_option "decode_uris", aliases: "-d", type: :boolean, desc: "Decodes t.co URLs into their original form."
757
+ method_option "exclude", aliases: "-e", type: :string, enum: %w[replies retweets], desc: "Exclude certain types of Tweets from the results.", banner: "TYPE"
758
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
759
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
760
+ method_option "max_id", aliases: "-m", type: :numeric, desc: "Returns only the results with an ID less than the specified ID."
761
+ method_option "number", aliases: "-n", type: :numeric, default: DEFAULT_NUM_RESULTS, desc: "Limit the number of results."
762
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
763
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
764
+ method_option "since_id", aliases: "-s", type: :numeric, desc: "Returns only the results with an ID greater than the specified ID."
758
765
  def timeline(user = nil)
759
- count = options['number'] || DEFAULT_NUM_RESULTS
766
+ count = options["number"] || DEFAULT_NUM_RESULTS
760
767
  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']
768
+ opts[:exclude_replies] = true if options["exclude"] == "replies"
769
+ opts[:include_entities] = !!options["decode_uris"]
770
+ opts[:include_rts] = false if options["exclude"] == "retweets"
771
+ opts[:max_id] = options["max_id"] if options["max_id"]
772
+ opts[:since_id] = options["since_id"] if options["since_id"]
766
773
  if user
767
- require 't/core_ext/string'
768
- user = options['id'] ? user.to_i : user.strip_ats
774
+ require "t/core_ext/string"
775
+ user = options["id"] ? user.to_i : user.strip_ats
769
776
  tweets = collect_with_count(count) do |count_opts|
770
777
  client.user_timeline(user, count_opts.merge(opts))
771
778
  end
@@ -776,61 +783,61 @@ module T
776
783
  end
777
784
  print_tweets(tweets)
778
785
  end
779
- map %w(tl) => :timeline
786
+ map %w[tl] => :timeline
780
787
 
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.'
788
+ desc "trends [WOEID]", "Returns the top 50 trending topics."
789
+ method_option "exclude-hashtags", aliases: "-x", type: :boolean, desc: "Remove all hashtags from the trends list."
783
790
  def trends(woe_id = 1)
784
791
  opts = {}
785
- opts[:exclude] = 'hashtags' if options['exclude-hashtags']
792
+ opts[:exclude] = "hashtags" if options["exclude-hashtags"]
786
793
  trends = client.trends(woe_id, opts)
787
794
  print_attribute(trends, :name)
788
795
  end
789
796
 
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.'
797
+ desc "trend_locations", "Returns the locations for which Twitter has trending topic information."
798
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
799
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
800
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
801
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
802
+ 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"
803
+ method_option "unsorted", aliases: "-u", type: :boolean, desc: "Output is not sorted."
797
804
  def trend_locations
798
805
  places = client.trend_locations
799
- unless options['unsorted']
800
- places = case options['sort']
801
- when 'country'
806
+ unless options["unsorted"]
807
+ places = case options["sort"]
808
+ when "country"
802
809
  places.sort_by { |place| place.country.downcase }
803
- when 'parent'
810
+ when "parent"
804
811
  places.sort_by { |place| place.parent_id.to_i }
805
- when 'type'
812
+ when "type"
806
813
  places.sort_by { |place| place.place_type.downcase }
807
- when 'woeid'
814
+ when "woeid"
808
815
  places.sort_by { |place| place.woeid.to_i }
809
816
  else
810
817
  places.sort_by { |place| place.name.downcase }
811
818
  end
812
819
  end
813
- places.reverse! if options['reverse']
814
- if options['csv']
815
- require 'csv'
820
+ places.reverse! if options["reverse"]
821
+ if options["csv"]
822
+ require "csv"
816
823
  say TREND_HEADINGS.to_csv unless places.empty?
817
824
  places.each do |place|
818
825
  say [place.woeid, place.parent_id, place.place_type, place.name, place.country].to_csv
819
826
  end
820
- elsif options['long']
827
+ elsif options["long"]
821
828
  array = places.collect do |place|
822
829
  [place.woeid, place.parent_id, place.place_type, place.name, place.country]
823
830
  end
824
- format = options['format'] || Array.new(TREND_HEADINGS.size) { '%s' }
831
+ format = options["format"] || Array.new(TREND_HEADINGS.size) { "%s" }
825
832
  print_table_with_headings(array, TREND_HEADINGS, format)
826
833
  else
827
834
  print_attribute(places, :name)
828
835
  end
829
836
  end
830
- map %w(locations trendlocations) => :trend_locations
837
+ map %w[locations trendlocations] => :trend_locations
831
838
 
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.'
839
+ desc "unfollow USER [USER...]", "Allows you to stop following users."
840
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify input as Twitter user IDs instead of screen names."
834
841
  def unfollow(user, *users)
835
842
  unfollowed_users, number = fetch_users(users.unshift(user), options) do |users_to_unfollow|
836
843
  client.unfollow(users_to_unfollow)
@@ -840,15 +847,15 @@ module T
840
847
  say "Run `#{File.basename($PROGRAM_NAME)} follow #{unfollowed_users.collect { |unfollowed_user| "@#{unfollowed_user.screen_name}" }.join(' ')}` to follow again."
841
848
  end
842
849
 
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.'
850
+ desc "update [MESSAGE]", "Post a Tweet."
851
+ 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."
852
+ method_option "file", aliases: "-f", type: :string, desc: "The path to an image to attach to your tweet."
846
853
  def update(message = nil)
847
854
  message = T::Editor.gets if message.to_s.empty?
848
855
  opts = {trim_user: true}
849
856
  add_location!(options, opts)
850
- status = if options['file']
851
- client.update_with_media(message, File.new(File.expand_path(options['file'])), opts)
857
+ status = if options["file"]
858
+ client.update_with_media(message, File.new(File.expand_path(options["file"])), opts)
852
859
  else
853
860
  client.update(message, opts)
854
861
  end
@@ -856,102 +863,102 @@ module T
856
863
  say
857
864
  say "Run `#{File.basename($PROGRAM_NAME)} delete status #{status.id}` to delete."
858
865
  end
859
- map %w(post tweet) => :update
866
+ map %w[post tweet] => :update
860
867
 
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.'
868
+ desc "users USER [USER...]", "Returns a list of users you specify."
869
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
870
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify input as Twitter user IDs instead of screen names."
871
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
872
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
873
+ method_option "reverse", aliases: "-r", type: :boolean, desc: "Reverse the order of the sort."
874
+ 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"
875
+ method_option "unsorted", aliases: "-u", type: :boolean, desc: "Output is not sorted."
869
876
  def users(user, *users)
870
877
  users.unshift(user)
871
- require 't/core_ext/string'
872
- options['id'] ? users.collect!(&:to_i) : users.collect!(&:strip_ats)
873
- require 'retryable'
878
+ require "t/core_ext/string"
879
+ options["id"] ? users.collect!(&:to_i) : users.collect!(&:strip_ats)
880
+ require "retryable"
874
881
  users = Retryable.retryable(tries: 3, on: Twitter::Error, sleep: 0) do
875
882
  client.users(users)
876
883
  end
877
884
  print_users(users)
878
885
  end
879
- map %w(stats) => :users
886
+ map %w[stats] => :users
880
887
 
881
- desc 'version', 'Show version.'
888
+ desc "version", "Show version."
882
889
  def version
883
- require 't/version'
890
+ require "t/version"
884
891
  say T::Version
885
892
  end
886
- map %w(-v --version) => :version
893
+ map %w[-v --version] => :version
887
894
 
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.'
895
+ desc "whois USER", "Retrieves profile information for the user."
896
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
897
+ method_option "decode_uris", aliases: "-d", type: :boolean, desc: "Decodes t.co URLs into their original form."
898
+ method_option "id", aliases: "-i", type: :boolean, desc: "Specify user via ID instead of screen name."
899
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
900
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
894
901
  def whois(user)
895
- require 't/core_ext/string'
902
+ require "t/core_ext/string"
896
903
  opts = {}
897
- opts[:include_entities] = !!options['decode_uris']
898
- user = options['id'] ? user.to_i : user.strip_ats
904
+ opts[:include_entities] = !!options["decode_uris"]
905
+ user = options["id"] ? user.to_i : user.strip_ats
899
906
  user = client.user(user, opts)
900
- if options['csv'] || options['long']
907
+ if options["csv"] || options["long"]
901
908
  print_users([user])
902
909
  else
903
910
  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?
911
+ array << ["ID", user.id.to_s]
912
+ array << ["Since", "#{ls_formatted_time(user, :created_at, false)} (#{time_ago_in_words(user.created_at)} ago)"]
913
+ 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?
914
+ array << ["Screen name", "@#{user.screen_name}"]
915
+ array << [user.verified? ? "Name (Verified)" : "Name", user.name] unless user.name.nil? # rubocop:disable Metrics/BlockNesting
916
+ array << ["Tweets", number_with_delimiter(user.statuses_count)]
917
+ array << ["Favorites", number_with_delimiter(user.favorites_count)]
918
+ array << ["Listed", number_with_delimiter(user.listed_count)]
919
+ array << ["Following", number_with_delimiter(user.friends_count)]
920
+ array << ["Followers", number_with_delimiter(user.followers_count)]
921
+ array << ["Bio", user.description.gsub(/\n+/, " ")] unless user.description.nil?
922
+ array << ["Location", user.location] unless user.location.nil?
923
+ array << ["URL", user.website] unless user.website.nil?
917
924
  print_table(array)
918
925
  end
919
926
  end
920
- map %w(user) => :whois
927
+ map %w[user] => :whois
921
928
 
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.'
929
+ desc "whoami", "Retrieves profile information for the authenticated user."
930
+ method_option "csv", aliases: "-c", type: :boolean, desc: "Output in CSV format."
931
+ method_option "decode_uris", aliases: "-d", type: :boolean, desc: "Decodes t.co URLs into their original form."
932
+ method_option "long", aliases: "-l", type: :boolean, desc: "Output in long format."
933
+ method_option "relative_dates", aliases: "-a", type: :boolean, desc: "Show relative dates."
927
934
  def whoami
928
935
  if @rcfile.active_profile && @rcfile.active_profile[0]
929
936
  user = @rcfile.active_profile[0]
930
937
  whois(user)
931
938
  else
932
- $stderr.puts "You haven't authorized an account, run `t authorize` to get started."
939
+ warn "You haven't authorized an account, run `t authorize` to get started."
933
940
  end
934
941
  end
935
942
 
936
- desc 'delete SUBCOMMAND ...ARGS', 'Delete Tweets, Direct Messages, etc.'
937
- subcommand 'delete', T::Delete
943
+ desc "delete SUBCOMMAND ...ARGS", "Delete Tweets, Direct Messages, etc."
944
+ subcommand "delete", T::Delete
938
945
 
939
- desc 'list SUBCOMMAND ...ARGS', 'Do various things with lists.'
940
- subcommand 'list', T::List
946
+ desc "list SUBCOMMAND ...ARGS", "Do various things with lists."
947
+ subcommand "list", T::List
941
948
 
942
- desc 'search SUBCOMMAND ...ARGS', 'Search through Tweets.'
943
- subcommand 'search', T::Search
949
+ desc "search SUBCOMMAND ...ARGS", "Search through Tweets."
950
+ subcommand "search", T::Search
944
951
 
945
- desc 'set SUBCOMMAND ...ARGS', 'Change various account settings.'
946
- subcommand 'set', T::Set
952
+ desc "set SUBCOMMAND ...ARGS", "Change various account settings."
953
+ subcommand "set", T::Set
947
954
 
948
- desc 'stream SUBCOMMAND ...ARGS', 'Commands for streaming Tweets.'
949
- subcommand 'stream', T::Stream
955
+ desc "stream SUBCOMMAND ...ARGS", "Commands for streaming Tweets."
956
+ subcommand "stream", T::Stream
950
957
 
951
958
  private
952
959
 
953
960
  def extract_mentioned_screen_names(text)
954
- valid_mention_preceding_chars = /(?:[^a-zA-Z0-9_!#\$%&*@@]|^|RT:?)/o
961
+ valid_mention_preceding_chars = /(?:[^a-zA-Z0-9_!#$%&*@@]|^|RT:?)/o
955
962
  at_signs = /[@@]/
956
963
  valid_mentions = /
957
964
  (#{valid_mention_preceding_chars}) # $1: Preceeding character
@@ -968,41 +975,43 @@ module T
968
975
 
969
976
  def generate_authorize_uri(consumer, request_token)
970
977
  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('=')
978
+ params = request["Authorization"].sub(/^OAuth\s+/, "").split(/,\s+/).collect do |param|
979
+ key, value = param.split("=")
973
980
  value =~ /"(.*?)"/
974
981
  "#{key}=#{CGI.escape(Regexp.last_match[1])}"
975
- end.join('&')
976
- "#{Twitter::REST::Client::BASE_URL}#{request.path}?#{params}"
982
+ end.join("&")
983
+ "#{Twitter::REST::Request::BASE_URL}#{request.path}?#{params}"
977
984
  end
978
985
 
979
986
  def pin_auth_parameters
980
- {oauth_callback: 'oob'}
987
+ {oauth_callback: "oob"}
981
988
  end
982
989
 
983
990
  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)
991
+ return nil unless options["location"]
992
+
993
+ lat, lng = options["location"] == "location" ? [location.lat, location.lng] : options["location"].split(",").collect(&:to_f)
986
994
  opts.merge!(lat: lat, long: lng)
987
995
  end
988
996
 
989
997
  def location
990
998
  return @location if @location
991
- require 'geokit'
992
- require 'open-uri'
993
- ip_address = Kernel.open('http://checkip.dyndns.org/') do |body|
999
+
1000
+ require "geokit"
1001
+ require "open-uri"
1002
+ ip_address = URI.open("http://checkip.dyndns.org/") do |body|
994
1003
  /(?:\d{1,3}\.){3}\d{1,3}/.match(body.read)[0]
995
1004
  end
996
1005
  @location = Geokit::Geocoders::MultiGeocoder.geocode(ip_address)
997
1006
  end
998
1007
 
999
1008
  def reverse_geocode(geo)
1000
- require 'geokit'
1009
+ require "geokit"
1001
1010
  geoloc = Geokit::Geocoders::MultiGeocoder.reverse_geocode(geo.coordinates)
1002
1011
  if geoloc.city && geoloc.state && geoloc.country
1003
- [geoloc.city, geoloc.state, geoloc.country].join(', ')
1012
+ [geoloc.city, geoloc.state, geoloc.country].join(", ")
1004
1013
  elsif geoloc.state && geoloc.country
1005
- [geoloc.state, geoloc.country].join(', ')
1014
+ [geoloc.state, geoloc.country].join(", ")
1006
1015
  else
1007
1016
  geoloc.country
1008
1017
  end