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.
- checksums.yaml +5 -5
- data/LICENSE.md +1 -1
- data/README.md +8 -9
- data/bin/t +21 -21
- data/lib/t/cli.rb +470 -463
- data/lib/t/collectable.rb +9 -8
- data/lib/t/core_ext/kernel.rb +3 -3
- data/lib/t/core_ext/string.rb +2 -2
- data/lib/t/delete.rb +44 -41
- data/lib/t/editor.rb +5 -5
- data/lib/t/identicon.rb +1 -1
- data/lib/t/list.rb +51 -51
- data/lib/t/printable.rb +69 -65
- data/lib/t/rcfile.rb +18 -17
- data/lib/t/requestable.rb +3 -2
- data/lib/t/search.rb +82 -82
- data/lib/t/set.rb +21 -21
- data/lib/t/stream.rb +67 -60
- data/lib/t/utils.rb +25 -25
- data/lib/t/version.rb +2 -2
- data/lib/t.rb +2 -2
- data/t.gemspec +22 -21
- metadata +29 -49
data/lib/t/cli.rb
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
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 = [
|
28
|
+
DIRECT_MESSAGE_HEADINGS = ["ID", "Posted at", "Screen name", "Text"].freeze
|
28
29
|
MAX_SEARCH_RESULTS = 100
|
29
|
-
TREND_HEADINGS = [
|
30
|
+
TREND_HEADINGS = ["WOEID", "Parent ID", "Type", "Name", "Country"].freeze
|
30
31
|
|
31
32
|
check_unknown_options!
|
32
33
|
|
33
|
-
class_option
|
34
|
-
class_option
|
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
|
42
|
+
desc "accounts", "List accounts"
|
42
43
|
def accounts
|
43
|
-
@rcfile.path = options[
|
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
|
53
|
-
method_option
|
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[
|
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
|
59
|
-
say
|
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
|
62
|
-
say
|
63
|
-
say
|
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
|
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
|
68
|
+
say " prompted."
|
68
69
|
else
|
69
70
|
say "It looks like you've already registered an application with Twitter."
|
70
|
-
say
|
71
|
-
say
|
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
|
74
|
+
say " 3. Copy and paste the consumer key and secret below when prompted."
|
74
75
|
end
|
75
76
|
say
|
76
|
-
ask
|
77
|
+
ask "Press [Enter] to open the Twitter Developer site."
|
77
78
|
say
|
78
|
-
require
|
79
|
-
open_or_print(
|
80
|
-
key = ask
|
81
|
-
secret = ask
|
82
|
-
consumer = OAuth::Consumer.new(key, secret, site: Twitter::REST::
|
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
|
87
|
-
say
|
88
|
-
say
|
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
|
91
|
+
say " 3. Copy and paste the supplied PIN below when prompted."
|
91
92
|
say
|
92
|
-
ask
|
93
|
+
ask "Press [Enter] to open the Twitter app authorization page."
|
93
94
|
say
|
94
|
-
open_or_print(uri, dry_run: options[
|
95
|
-
pin = ask
|
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(
|
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
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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 = {
|
109
|
-
say
|
109
|
+
@rcfile.active_profile = {"username" => screen_name, "consumer_key" => key}
|
110
|
+
say "Authorization successful."
|
110
111
|
end
|
111
112
|
|
112
|
-
desc
|
113
|
-
method_option
|
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
|
124
|
-
method_option
|
125
|
-
method_option
|
126
|
-
method_option
|
127
|
-
method_option
|
128
|
-
method_option
|
129
|
-
method_option
|
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[
|
132
|
+
count = options["number"] || DEFAULT_NUM_RESULTS
|
132
133
|
opts = {}
|
133
|
-
opts[:include_entities] = !!options[
|
134
|
+
opts[:include_entities] = !!options["decode_uris"]
|
134
135
|
direct_messages = collect_with_count(count) do |count_opts|
|
135
|
-
client.
|
136
|
+
client.direct_messages_received(count_opts.merge(opts))
|
136
137
|
end
|
137
|
-
direct_messages.
|
138
|
-
|
139
|
-
|
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
|
-
|
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[
|
149
|
+
elsif options["long"]
|
145
150
|
array = direct_messages.collect do |direct_message|
|
146
|
-
|
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[
|
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
|
-
|
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
|
163
|
+
map %w[directmessages dms] => :direct_messages
|
157
164
|
|
158
|
-
desc
|
159
|
-
method_option
|
160
|
-
method_option
|
161
|
-
method_option
|
162
|
-
method_option
|
163
|
-
method_option
|
164
|
-
method_option
|
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[
|
173
|
+
count = options["number"] || DEFAULT_NUM_RESULTS
|
167
174
|
opts = {}
|
168
|
-
opts[:include_entities] = !!options[
|
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[
|
173
|
-
if options[
|
174
|
-
require
|
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[
|
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[
|
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[
|
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[
|
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
|
198
|
+
map %w[directmessagessent sent_messages sentmessages sms] => :direct_messages_sent
|
192
199
|
|
193
|
-
desc
|
194
|
-
method_option
|
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
|
197
|
-
|
198
|
-
|
199
|
-
say "Direct Message sent from @#{@rcfile.active_profile[0]} to @#{
|
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
|
208
|
+
map %w[d m] => :dm
|
202
209
|
|
203
|
-
desc
|
204
|
-
method_option
|
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
|
-
|
214
|
+
user = if user.nil?
|
215
|
+
@rcfile.active_profile[0]
|
209
216
|
else
|
210
|
-
require
|
211
|
-
|
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
|
226
|
+
map %w[dc doescontain] => :does_contain
|
220
227
|
|
221
|
-
desc
|
222
|
-
method_option
|
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
|
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
|
227
|
-
thread1 = if options[
|
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[
|
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
|
254
|
+
map %w[df doesfollow] => :does_follow
|
248
255
|
|
249
|
-
desc
|
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
|
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
|
263
|
-
|
264
|
-
desc
|
265
|
-
method_option
|
266
|
-
method_option
|
267
|
-
method_option
|
268
|
-
method_option
|
269
|
-
method_option
|
270
|
-
method_option
|
271
|
-
method_option
|
272
|
-
method_option
|
273
|
-
method_option
|
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[
|
282
|
+
count = options["number"] || DEFAULT_NUM_RESULTS
|
276
283
|
opts = {}
|
277
|
-
opts[:exclude_replies] = true if options[
|
278
|
-
opts[:include_entities] = !!options[
|
279
|
-
opts[:include_rts] = false if options[
|
280
|
-
opts[:max_id] = options[
|
281
|
-
opts[:since_id] = options[
|
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
|
284
|
-
user = options[
|
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
|
298
|
+
map %w[faves favourites] => :favorites
|
292
299
|
|
293
|
-
desc
|
294
|
-
method_option
|
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
|
305
|
-
method_option
|
306
|
-
method_option
|
307
|
-
method_option
|
308
|
-
method_option
|
309
|
-
method_option
|
310
|
-
method_option
|
311
|
-
method_option
|
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
|
315
|
-
user = options[
|
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
|
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
|
326
|
-
method_option
|
327
|
-
method_option
|
328
|
-
method_option
|
329
|
-
method_option
|
330
|
-
method_option
|
331
|
-
method_option
|
332
|
-
method_option
|
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
|
335
|
-
user1 = options[
|
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[
|
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
|
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
|
357
|
+
map %w[ff followingsfollowing] => :followings_following
|
351
358
|
|
352
|
-
desc
|
353
|
-
method_option
|
354
|
-
method_option
|
355
|
-
method_option
|
356
|
-
method_option
|
357
|
-
method_option
|
358
|
-
method_option
|
359
|
-
method_option
|
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
|
363
|
-
user = options[
|
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
|
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
|
374
|
-
method_option
|
375
|
-
method_option
|
376
|
-
method_option
|
377
|
-
method_option
|
378
|
-
method_option
|
379
|
-
method_option
|
380
|
-
method_option
|
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
|
384
|
-
options[
|
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
|
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
|
399
|
-
method_option
|
400
|
-
method_option
|
401
|
-
method_option
|
402
|
-
method_option
|
403
|
-
method_option
|
404
|
-
method_option
|
405
|
-
method_option
|
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
|
409
|
-
options[
|
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
|
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
|
423
|
-
|
424
|
-
desc
|
425
|
-
method_option
|
426
|
-
method_option
|
427
|
-
method_option
|
428
|
-
method_option
|
429
|
-
method_option
|
430
|
-
method_option
|
431
|
-
method_option
|
432
|
-
method_option
|
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
|
438
|
-
options[
|
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[
|
441
|
-
when
|
447
|
+
case options["type"]
|
448
|
+
when "followings"
|
442
449
|
client.friend_ids(user).to_a
|
443
|
-
when
|
450
|
+
when "followers"
|
444
451
|
client.follower_ids(user).to_a
|
445
452
|
end
|
446
453
|
end
|
447
454
|
intersection = sets.reduce(:&)
|
448
|
-
require
|
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
|
461
|
+
map %w[overlap] => :intersection
|
455
462
|
|
456
|
-
desc
|
457
|
-
method_option
|
458
|
-
method_option
|
459
|
-
method_option
|
460
|
-
method_option
|
461
|
-
method_option
|
462
|
-
method_option
|
463
|
-
method_option
|
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
|
467
|
-
options[
|
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
|
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
|
482
|
-
method_option
|
483
|
-
method_option
|
484
|
-
method_option
|
485
|
-
method_option
|
486
|
-
method_option
|
487
|
-
method_option
|
488
|
-
method_option
|
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
|
492
|
-
user = options[
|
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
|
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(
|
510
|
+
tweets = client.search("lang:ja", opts)
|
504
511
|
tweets.each do |tweet|
|
505
|
-
say(tweet.text.gsub(/[^\u3000\u3040-\u309f]/,
|
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
|
510
|
-
method_option
|
511
|
-
method_option
|
512
|
-
method_option
|
513
|
-
method_option
|
514
|
-
method_option
|
515
|
-
method_option
|
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[
|
524
|
+
count = options["number"] || DEFAULT_NUM_RESULTS
|
518
525
|
opts = {}
|
519
|
-
opts[:include_entities] = !!options[
|
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
|
532
|
+
map %w[replies] => :mentions
|
526
533
|
|
527
|
-
desc
|
528
|
-
method_option
|
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
|
539
|
-
method_option
|
540
|
-
method_option
|
541
|
-
method_option
|
542
|
-
method_option
|
543
|
-
method_option
|
544
|
-
method_option
|
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
|
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
|
560
|
+
map %w[mutes] => :muted
|
554
561
|
|
555
|
-
desc
|
556
|
-
method_option
|
557
|
-
method_option
|
558
|
-
method_option
|
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
|
561
|
-
if options[
|
567
|
+
require "launchy"
|
568
|
+
if options["id"]
|
562
569
|
user = client.user(user.to_i)
|
563
|
-
open_or_print(user.uri, dry_run: options[
|
564
|
-
elsif options[
|
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[
|
573
|
+
open_or_print(status.uri, dry_run: options["display-uri"])
|
567
574
|
else
|
568
|
-
require
|
569
|
-
open_or_print("https://twitter.com/#{user.strip_ats}", dry_run: options[
|
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
|
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
|
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
|
-
|
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
|
595
|
-
method_option
|
596
|
-
method_option
|
597
|
-
method_option
|
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[
|
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
|
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[
|
612
|
-
client.update_with_media("#{users.join(' ')} #{message}", File.new(File.expand_path(options[
|
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
|
622
|
-
method_option
|
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
|
634
|
+
map %w[report reportspam spam] => :report_spam
|
630
635
|
|
631
|
-
desc
|
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
|
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
|
649
|
+
map %w[rt] => :retweet
|
645
650
|
|
646
|
-
desc
|
647
|
-
method_option
|
648
|
-
method_option
|
649
|
-
method_option
|
650
|
-
method_option
|
651
|
-
method_option
|
652
|
-
method_option
|
653
|
-
method_option
|
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[
|
660
|
+
count = options["number"] || DEFAULT_NUM_RESULTS
|
656
661
|
opts = {}
|
657
|
-
opts[:include_entities] = !!options[
|
662
|
+
opts[:include_entities] = !!options["decode_uris"]
|
658
663
|
tweets = if user
|
659
|
-
require
|
660
|
-
user = options[
|
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
|
676
|
+
map %w[rts] => :retweets
|
672
677
|
|
673
|
-
desc
|
674
|
-
method_option
|
675
|
-
method_option
|
676
|
-
method_option
|
677
|
-
method_option
|
678
|
-
method_option
|
679
|
-
method_option
|
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[
|
686
|
+
count = options["number"] || DEFAULT_NUM_RESULTS
|
682
687
|
opts = {}
|
683
|
-
opts[:include_entities] = !!options[
|
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
|
694
|
+
map %w[retweetsofme] => :retweets_of_me
|
690
695
|
|
691
|
-
desc
|
692
|
-
method_option
|
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 =
|
699
|
+
markings = "----|".chars.cycle.take(140).join
|
695
700
|
say "#{' ' * options['indent'].to_i}#{markings}"
|
696
701
|
end
|
697
702
|
|
698
|
-
desc
|
699
|
-
method_option
|
700
|
-
method_option
|
701
|
-
method_option
|
702
|
-
method_option
|
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[
|
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 = [
|
725
|
-
if options[
|
726
|
-
require
|
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[
|
729
|
-
elsif options[
|
730
|
-
array = [status.id, ls_formatted_time(status), "@#{status.user.screen_name}", decode_full_text(status, options[
|
731
|
-
format = options[
|
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 << [
|
736
|
-
array << [
|
737
|
-
array << [
|
738
|
-
array << [
|
739
|
-
array << [
|
740
|
-
array << [
|
741
|
-
array << [
|
742
|
-
array << [
|
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
|
748
|
-
method_option
|
749
|
-
method_option
|
750
|
-
method_option
|
751
|
-
method_option
|
752
|
-
method_option
|
753
|
-
method_option
|
754
|
-
method_option
|
755
|
-
method_option
|
756
|
-
method_option
|
757
|
-
method_option
|
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[
|
764
|
+
count = options["number"] || DEFAULT_NUM_RESULTS
|
760
765
|
opts = {}
|
761
|
-
opts[:exclude_replies] = true if options[
|
762
|
-
opts[:include_entities] = !!options[
|
763
|
-
opts[:include_rts] = false if options[
|
764
|
-
opts[:max_id] = options[
|
765
|
-
opts[:since_id] = options[
|
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
|
768
|
-
user = options[
|
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
|
784
|
+
map %w[tl] => :timeline
|
780
785
|
|
781
|
-
desc
|
782
|
-
method_option
|
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] =
|
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
|
791
|
-
method_option
|
792
|
-
method_option
|
793
|
-
method_option
|
794
|
-
method_option
|
795
|
-
method_option
|
796
|
-
method_option
|
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[
|
800
|
-
places = case options[
|
801
|
-
when
|
804
|
+
unless options["unsorted"]
|
805
|
+
places = case options["sort"]
|
806
|
+
when "country"
|
802
807
|
places.sort_by { |place| place.country.downcase }
|
803
|
-
when
|
808
|
+
when "parent"
|
804
809
|
places.sort_by { |place| place.parent_id.to_i }
|
805
|
-
when
|
810
|
+
when "type"
|
806
811
|
places.sort_by { |place| place.place_type.downcase }
|
807
|
-
when
|
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[
|
814
|
-
if options[
|
815
|
-
require
|
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[
|
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[
|
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
|
835
|
+
map %w[locations trendlocations] => :trend_locations
|
831
836
|
|
832
|
-
desc
|
833
|
-
method_option
|
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
|
844
|
-
method_option
|
845
|
-
method_option
|
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[
|
851
|
-
client.update_with_media(message, File.new(File.expand_path(options[
|
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
|
864
|
+
map %w[post tweet] => :update
|
860
865
|
|
861
|
-
desc
|
862
|
-
method_option
|
863
|
-
method_option
|
864
|
-
method_option
|
865
|
-
method_option
|
866
|
-
method_option
|
867
|
-
method_option
|
868
|
-
method_option
|
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
|
872
|
-
options[
|
873
|
-
require
|
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
|
884
|
+
map %w[stats] => :users
|
880
885
|
|
881
|
-
desc
|
886
|
+
desc "version", "Show version."
|
882
887
|
def version
|
883
|
-
require
|
888
|
+
require "t/version"
|
884
889
|
say T::Version
|
885
890
|
end
|
886
|
-
map %w
|
891
|
+
map %w[-v --version] => :version
|
887
892
|
|
888
|
-
desc
|
889
|
-
method_option
|
890
|
-
method_option
|
891
|
-
method_option
|
892
|
-
method_option
|
893
|
-
method_option
|
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
|
900
|
+
require "t/core_ext/string"
|
896
901
|
opts = {}
|
897
|
-
opts[:include_entities] = !!options[
|
898
|
-
user = options[
|
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[
|
905
|
+
if options["csv"] || options["long"]
|
901
906
|
print_users([user])
|
902
907
|
else
|
903
908
|
array = []
|
904
|
-
array << [
|
905
|
-
array << [
|
906
|
-
array << [
|
907
|
-
array << [
|
908
|
-
array << [user.verified? ?
|
909
|
-
array << [
|
910
|
-
array << [
|
911
|
-
array << [
|
912
|
-
array << [
|
913
|
-
array << [
|
914
|
-
array << [
|
915
|
-
array << [
|
916
|
-
array << [
|
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
|
925
|
+
map %w[user] => :whois
|
921
926
|
|
922
|
-
desc
|
923
|
-
method_option
|
924
|
-
method_option
|
925
|
-
method_option
|
926
|
-
method_option
|
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
|
-
|
937
|
+
warn "You haven't authorized an account, run `t authorize` to get started."
|
933
938
|
end
|
934
939
|
end
|
935
940
|
|
936
|
-
desc
|
937
|
-
subcommand
|
941
|
+
desc "delete SUBCOMMAND ...ARGS", "Delete Tweets, Direct Messages, etc."
|
942
|
+
subcommand "delete", T::Delete
|
938
943
|
|
939
|
-
desc
|
940
|
-
subcommand
|
944
|
+
desc "list SUBCOMMAND ...ARGS", "Do various things with lists."
|
945
|
+
subcommand "list", T::List
|
941
946
|
|
942
|
-
desc
|
943
|
-
subcommand
|
947
|
+
desc "search SUBCOMMAND ...ARGS", "Search through Tweets."
|
948
|
+
subcommand "search", T::Search
|
944
949
|
|
945
|
-
desc
|
946
|
-
subcommand
|
950
|
+
desc "set SUBCOMMAND ...ARGS", "Change various account settings."
|
951
|
+
subcommand "set", T::Set
|
947
952
|
|
948
|
-
desc
|
949
|
-
subcommand
|
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_
|
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 []
|
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[
|
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::
|
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:
|
985
|
+
{oauth_callback: "oob"}
|
981
986
|
end
|
982
987
|
|
983
988
|
def add_location!(options, opts)
|
984
|
-
return nil unless options[
|
985
|
-
|
986
|
-
|
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
|
-
|
992
|
-
require
|
993
|
-
|
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
|
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
|