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.
- checksums.yaml +5 -5
- data/LICENSE.md +1 -1
- data/README.md +9 -7
- data/bin/t +21 -21
- data/lib/t/cli.rb +467 -458
- data/lib/t/collectable.rb +6 -4
- 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 +50 -50
- data/lib/t/printable.rb +68 -64
- data/lib/t/rcfile.rb +18 -17
- data/lib/t/requestable.rb +3 -2
- data/lib/t/search.rb +81 -81
- data/lib/t/set.rb +19 -19
- data/lib/t/stream.rb +67 -60
- data/lib/t/utils.rb +25 -25
- data/lib/t/version.rb +3 -3
- data/lib/t.rb +2 -2
- data/t.gemspec +22 -21
- metadata +23 -37
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,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
|
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
|
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
|
595
|
-
method_option
|
596
|
-
method_option
|
597
|
-
method_option
|
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[
|
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
|
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[
|
612
|
-
client.update_with_media("#{users.join(' ')} #{message}", File.new(File.expand_path(options[
|
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
|
622
|
-
method_option
|
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
|
636
|
+
map %w[report reportspam spam] => :report_spam
|
630
637
|
|
631
|
-
desc
|
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
|
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
|
651
|
+
map %w[rt] => :retweet
|
645
652
|
|
646
|
-
desc
|
647
|
-
method_option
|
648
|
-
method_option
|
649
|
-
method_option
|
650
|
-
method_option
|
651
|
-
method_option
|
652
|
-
method_option
|
653
|
-
method_option
|
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[
|
662
|
+
count = options["number"] || DEFAULT_NUM_RESULTS
|
656
663
|
opts = {}
|
657
|
-
opts[:include_entities] = !!options[
|
664
|
+
opts[:include_entities] = !!options["decode_uris"]
|
658
665
|
tweets = if user
|
659
|
-
require
|
660
|
-
user = options[
|
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
|
678
|
+
map %w[rts] => :retweets
|
672
679
|
|
673
|
-
desc
|
674
|
-
method_option
|
675
|
-
method_option
|
676
|
-
method_option
|
677
|
-
method_option
|
678
|
-
method_option
|
679
|
-
method_option
|
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[
|
688
|
+
count = options["number"] || DEFAULT_NUM_RESULTS
|
682
689
|
opts = {}
|
683
|
-
opts[:include_entities] = !!options[
|
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
|
696
|
+
map %w[retweetsofme] => :retweets_of_me
|
690
697
|
|
691
|
-
desc
|
692
|
-
method_option
|
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 =
|
701
|
+
markings = "----|".chars.cycle.take(140).join
|
695
702
|
say "#{' ' * options['indent'].to_i}#{markings}"
|
696
703
|
end
|
697
704
|
|
698
|
-
desc
|
699
|
-
method_option
|
700
|
-
method_option
|
701
|
-
method_option
|
702
|
-
method_option
|
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[
|
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 = [
|
725
|
-
if options[
|
726
|
-
require
|
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[
|
729
|
-
elsif options[
|
730
|
-
array = [status.id, ls_formatted_time(status), "@#{status.user.screen_name}", decode_full_text(status, options[
|
731
|
-
format = options[
|
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 << [
|
736
|
-
array << [
|
737
|
-
array << [
|
738
|
-
array << [
|
739
|
-
array << [
|
740
|
-
array << [
|
741
|
-
array << [
|
742
|
-
array << [
|
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
|
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
|
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[
|
766
|
+
count = options["number"] || DEFAULT_NUM_RESULTS
|
760
767
|
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[
|
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
|
768
|
-
user = options[
|
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
|
786
|
+
map %w[tl] => :timeline
|
780
787
|
|
781
|
-
desc
|
782
|
-
method_option
|
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] =
|
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
|
791
|
-
method_option
|
792
|
-
method_option
|
793
|
-
method_option
|
794
|
-
method_option
|
795
|
-
method_option
|
796
|
-
method_option
|
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[
|
800
|
-
places = case options[
|
801
|
-
when
|
806
|
+
unless options["unsorted"]
|
807
|
+
places = case options["sort"]
|
808
|
+
when "country"
|
802
809
|
places.sort_by { |place| place.country.downcase }
|
803
|
-
when
|
810
|
+
when "parent"
|
804
811
|
places.sort_by { |place| place.parent_id.to_i }
|
805
|
-
when
|
812
|
+
when "type"
|
806
813
|
places.sort_by { |place| place.place_type.downcase }
|
807
|
-
when
|
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[
|
814
|
-
if options[
|
815
|
-
require
|
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[
|
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[
|
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
|
837
|
+
map %w[locations trendlocations] => :trend_locations
|
831
838
|
|
832
|
-
desc
|
833
|
-
method_option
|
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
|
844
|
-
method_option
|
845
|
-
method_option
|
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[
|
851
|
-
client.update_with_media(message, File.new(File.expand_path(options[
|
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
|
866
|
+
map %w[post tweet] => :update
|
860
867
|
|
861
|
-
desc
|
862
|
-
method_option
|
863
|
-
method_option
|
864
|
-
method_option
|
865
|
-
method_option
|
866
|
-
method_option
|
867
|
-
method_option
|
868
|
-
method_option
|
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
|
872
|
-
options[
|
873
|
-
require
|
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
|
886
|
+
map %w[stats] => :users
|
880
887
|
|
881
|
-
desc
|
888
|
+
desc "version", "Show version."
|
882
889
|
def version
|
883
|
-
require
|
890
|
+
require "t/version"
|
884
891
|
say T::Version
|
885
892
|
end
|
886
|
-
map %w
|
893
|
+
map %w[-v --version] => :version
|
887
894
|
|
888
|
-
desc
|
889
|
-
method_option
|
890
|
-
method_option
|
891
|
-
method_option
|
892
|
-
method_option
|
893
|
-
method_option
|
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
|
902
|
+
require "t/core_ext/string"
|
896
903
|
opts = {}
|
897
|
-
opts[:include_entities] = !!options[
|
898
|
-
user = options[
|
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[
|
907
|
+
if options["csv"] || options["long"]
|
901
908
|
print_users([user])
|
902
909
|
else
|
903
910
|
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 << [
|
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
|
927
|
+
map %w[user] => :whois
|
921
928
|
|
922
|
-
desc
|
923
|
-
method_option
|
924
|
-
method_option
|
925
|
-
method_option
|
926
|
-
method_option
|
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
|
-
|
939
|
+
warn "You haven't authorized an account, run `t authorize` to get started."
|
933
940
|
end
|
934
941
|
end
|
935
942
|
|
936
|
-
desc
|
937
|
-
subcommand
|
943
|
+
desc "delete SUBCOMMAND ...ARGS", "Delete Tweets, Direct Messages, etc."
|
944
|
+
subcommand "delete", T::Delete
|
938
945
|
|
939
|
-
desc
|
940
|
-
subcommand
|
946
|
+
desc "list SUBCOMMAND ...ARGS", "Do various things with lists."
|
947
|
+
subcommand "list", T::List
|
941
948
|
|
942
|
-
desc
|
943
|
-
subcommand
|
949
|
+
desc "search SUBCOMMAND ...ARGS", "Search through Tweets."
|
950
|
+
subcommand "search", T::Search
|
944
951
|
|
945
|
-
desc
|
946
|
-
subcommand
|
952
|
+
desc "set SUBCOMMAND ...ARGS", "Change various account settings."
|
953
|
+
subcommand "set", T::Set
|
947
954
|
|
948
|
-
desc
|
949
|
-
subcommand
|
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_
|
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[
|
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::
|
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:
|
987
|
+
{oauth_callback: "oob"}
|
981
988
|
end
|
982
989
|
|
983
990
|
def add_location!(options, opts)
|
984
|
-
return nil unless options[
|
985
|
-
|
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
|
-
|
992
|
-
require
|
993
|
-
|
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
|
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
|