t 0.6.4 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +19 -6
- data/lib/t/cli.rb +489 -191
- data/lib/t/core_ext/string.rb +4 -0
- data/lib/t/delete.rb +38 -23
- data/lib/t/list.rb +117 -46
- data/lib/t/printable.rb +56 -5
- data/lib/t/search.rb +35 -10
- data/lib/t/version.rb +2 -2
- data/spec/cli_spec.rb +1302 -163
- data/spec/delete_spec.rb +143 -125
- data/spec/fixtures/false.json +1 -0
- data/spec/fixtures/list.json +1 -1
- data/spec/fixtures/lists.json +1 -0
- data/spec/fixtures/locations.json +1 -0
- data/spec/fixtures/not_found.json +1 -0
- data/spec/fixtures/trends.json +1 -0
- data/spec/fixtures/true.json +1 -0
- data/spec/helper.rb +4 -0
- data/spec/list_spec.rb +204 -66
- data/spec/rcfile_spec.rb +7 -7
- data/spec/search_spec.rb +189 -6
- data/t.gemspec +3 -1
- metadata +48 -4
data/README.md
CHANGED
@@ -66,6 +66,14 @@ interpreted by your shell.
|
|
66
66
|
### <a name="follow"></a>Follow users
|
67
67
|
t follow @sferik @gem
|
68
68
|
|
69
|
+
### <a name="does-follow"></a>Does one user follow another user?
|
70
|
+
t does_follow @ev @sferik
|
71
|
+
|
72
|
+
**Note**: When this command returns false, `t` exits with a non-zero exit code,
|
73
|
+
which which allows you to execute conditional commands, for example:
|
74
|
+
|
75
|
+
t does_follow @ev @sferik && t dm @ev "What's up, bro?"
|
76
|
+
|
69
77
|
### <a name="friends"></a>List your friends (ordered by number of followers)
|
70
78
|
t friends -lf
|
71
79
|
|
@@ -75,11 +83,14 @@ interpreted by your shell.
|
|
75
83
|
### <a name="unfollow"></a>Unfollow everyone you follow who doesn't follow you back
|
76
84
|
t leaders | xargs t unfollow
|
77
85
|
|
78
|
-
### Follow back everyone who follows you
|
86
|
+
### <a name="follow"></a>Follow back everyone who follows you
|
79
87
|
t disciples | xargs t follow
|
80
88
|
|
81
|
-
### Follow roulette: randomly follow someone who follows you
|
82
|
-
t disciples |
|
89
|
+
### <a name="follow-roulette"></a>Follow roulette: randomly follow someone who follows you
|
90
|
+
t disciples | shuf | head -1 | xargs t follow
|
91
|
+
|
92
|
+
### <a name="favorite"></a>Favorite the last 10 tweets that mention you
|
93
|
+
t mentions -l -n 10 | awk '{print $1}' | xargs t favorite
|
83
94
|
|
84
95
|
### <a name="list-create"></a>Create a list
|
85
96
|
t list create presidents
|
@@ -87,16 +98,16 @@ interpreted by your shell.
|
|
87
98
|
### <a name="list-add"></a>Add users to a list
|
88
99
|
t list add presidents @BarackObama @Jasonfinn
|
89
100
|
|
90
|
-
###
|
101
|
+
### Create a list that contains today's date in the name
|
91
102
|
t list create following-`date "+%Y-%m-%d"`
|
92
103
|
|
93
|
-
### Add everyone you're following to a list
|
104
|
+
### <a name="followings"></a>Add everyone you're following to a list
|
94
105
|
t followings | xargs t list add following-`date "+%Y-%m-%d"`
|
95
106
|
|
96
107
|
### <a name="list-members"></a>Display members of a list
|
97
108
|
t list members following-`date "+%Y-%m-%d"`
|
98
109
|
|
99
|
-
### Count the number of Twitter employees
|
110
|
+
### <a name="list-members-count"></a>Count the number of Twitter employees
|
100
111
|
t list members twitter team | wc -l
|
101
112
|
|
102
113
|
### <a name="search-all"></a>Search Twitter for the 20 most recent Tweets that match a specified query
|
@@ -123,6 +134,8 @@ interpreted by your shell.
|
|
123
134
|
* Designed for Unix: All output is designed to be piped to other Unix
|
124
135
|
utilities, like grep, cut, awk, bc, wc, and xargs for advanced processing.
|
125
136
|
* 99% C0 Code Coverage: Extremely well tested, with a 3:1 test-to-code ratio.
|
137
|
+
* Generate spreadsheets: Output any command in CSV format simply by adding the
|
138
|
+
`--csv` flag.
|
126
139
|
|
127
140
|
## <a name="terminology"></a>Relationship Terminology
|
128
141
|
|
data/lib/t/cli.rb
CHANGED
@@ -3,6 +3,9 @@ require 'active_support/core_ext/array/grouping'
|
|
3
3
|
require 'active_support/core_ext/date/calculations'
|
4
4
|
require 'active_support/core_ext/integer/time'
|
5
5
|
require 'active_support/core_ext/numeric/time'
|
6
|
+
require 'csv'
|
7
|
+
# 'fastercsv' required on Ruby versions < 1.9
|
8
|
+
require 'fastercsv' unless Array.new.respond_to?(:to_csv)
|
6
9
|
require 'geokit'
|
7
10
|
require 'launchy'
|
8
11
|
require 'oauth'
|
@@ -22,6 +25,11 @@ require 'time'
|
|
22
25
|
require 'twitter'
|
23
26
|
require 'yaml'
|
24
27
|
|
28
|
+
# twitter-text requires $KCODE to be set to UTF8 on Ruby versions < 1.8
|
29
|
+
major, minor, patch = RUBY_VERSION.split('.')
|
30
|
+
$KCODE='u' if major.to_i == 1 && minor.to_i < 9
|
31
|
+
require 'twitter-text'
|
32
|
+
|
25
33
|
module T
|
26
34
|
class CLI < Thor
|
27
35
|
include ActionView::Helpers::DateHelper
|
@@ -30,6 +38,7 @@ module T
|
|
30
38
|
include T::Collectable
|
31
39
|
include T::Printable
|
32
40
|
include T::Requestable
|
41
|
+
include Twitter::Extractor
|
33
42
|
|
34
43
|
DEFAULT_NUM_RESULTS = 20
|
35
44
|
MAX_SCREEN_NAME_SIZE = 20
|
@@ -58,15 +67,15 @@ module T
|
|
58
67
|
end
|
59
68
|
|
60
69
|
desc "authorize", "Allows an application to request user authorization"
|
61
|
-
method_option :consumer_key, :aliases => "-c", :required => true
|
62
|
-
method_option :consumer_secret, :aliases => "-s", :required => true
|
70
|
+
method_option :consumer_key, :aliases => "-c", :required => true, :desc => "This can be found at https://dev.twitter.com/apps"
|
71
|
+
method_option :consumer_secret, :aliases => "-s", :required => true, :desc => "This can be found at https://dev.twitter.com/apps"
|
72
|
+
method_option :display_url, :aliases => "-d", :type => :boolean, :default => false, :desc => "Display the authorization URL instead of attempting to open it."
|
63
73
|
method_option :prompt, :aliases => "-p", :type => :boolean, :default => true
|
64
|
-
method_option :dry_run, :type => :boolean
|
65
74
|
def authorize
|
66
75
|
request_token = consumer.get_request_token
|
67
76
|
url = generate_authorize_url(request_token)
|
68
77
|
if options['prompt']
|
69
|
-
say "In a moment,
|
78
|
+
say "In a moment, you will be directed to the Twitter app authorization page."
|
70
79
|
say "Perform the following steps to complete the authorization process:"
|
71
80
|
say " 1. Sign in to Twitter"
|
72
81
|
say " 2. Press \"Authorize app\""
|
@@ -76,7 +85,7 @@ module T
|
|
76
85
|
ask "Press [Enter] to open the Twitter app authorization page."
|
77
86
|
say
|
78
87
|
end
|
79
|
-
Launchy.open(url, :dry_run => options
|
88
|
+
Launchy.open(url, :dry_run => options['display_url'])
|
80
89
|
pin = ask "Paste in the supplied PIN:"
|
81
90
|
access_token = request_token.get_access_token(:oauth_verifier => pin.chomp)
|
82
91
|
oauth_response = access_token.get('/1/account/verify_credentials.json')
|
@@ -95,31 +104,41 @@ module T
|
|
95
104
|
say "Authorization successful."
|
96
105
|
end
|
97
106
|
|
98
|
-
desc "block
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
107
|
+
desc "block USER [USER...]", "Block users."
|
108
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify input as Twitter user IDs instead of screen names."
|
109
|
+
def block(user, *users)
|
110
|
+
users.unshift(user)
|
111
|
+
if options['id']
|
112
|
+
users.map!(&:to_i)
|
113
|
+
else
|
114
|
+
users.map!(&:strip_ats)
|
115
|
+
end
|
116
|
+
users = users.threaded_map do |user|
|
104
117
|
retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
105
|
-
client.block(
|
118
|
+
client.block(user, :include_entities => false)
|
106
119
|
end
|
107
120
|
end
|
108
|
-
number =
|
121
|
+
number = users.length
|
109
122
|
say "@#{@rcfile.default_profile[0]} blocked #{number} #{number == 1 ? 'user' : 'users'}."
|
110
123
|
say
|
111
|
-
say "Run `#{File.basename($0)} delete block #{
|
124
|
+
say "Run `#{File.basename($0)} delete block #{users.map{|user| "@#{user.screen_name}"}.join(' ')}` to unblock."
|
112
125
|
end
|
113
126
|
|
114
127
|
desc "direct_messages", "Returns the #{DEFAULT_NUM_RESULTS} most recent Direct Messages sent to you."
|
115
|
-
method_option :
|
128
|
+
method_option :csv, :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
129
|
+
method_option :long, :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
116
130
|
method_option :number, :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS, :desc => "Limit the number of results."
|
117
131
|
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
118
132
|
def direct_messages
|
119
133
|
count = options['number'] || DEFAULT_NUM_RESULTS
|
120
134
|
direct_messages = client.direct_messages(:count => count, :include_entities => false)
|
121
135
|
direct_messages.reverse! if options['reverse']
|
122
|
-
if options['
|
136
|
+
if options['csv']
|
137
|
+
say ["ID", "Posted at", "Screen name", "Text"].to_csv unless direct_messages.empty?
|
138
|
+
direct_messages.each do |direct_message|
|
139
|
+
say [direct_message.id, direct_message.created_at.utc.strftime("%Y-%m-%d %H:%M:%S %z"), direct_message.sender.screen_name, direct_message.text].to_csv
|
140
|
+
end
|
141
|
+
elsif options['long']
|
123
142
|
array = direct_messages.map do |direct_message|
|
124
143
|
created_at = direct_message.created_at > 6.months.ago ? direct_message.created_at.strftime("%b %e %H:%M") : direct_message.created_at.strftime("%b %e %Y")
|
125
144
|
[direct_message.id.to_s, created_at, "@#{direct_message.sender.screen_name}", direct_message.text.gsub(/\n+/, ' ')]
|
@@ -135,17 +154,23 @@ module T
|
|
135
154
|
end
|
136
155
|
end
|
137
156
|
end
|
138
|
-
map %w(dms) => :direct_messages
|
157
|
+
map %w(directmessages dms) => :direct_messages
|
139
158
|
|
140
159
|
desc "direct_messages_sent", "Returns the #{DEFAULT_NUM_RESULTS} most recent Direct Messages sent to you."
|
141
|
-
method_option :
|
160
|
+
method_option :csv, :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
161
|
+
method_option :long, :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
142
162
|
method_option :number, :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS, :desc => "Limit the number of results."
|
143
163
|
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
144
164
|
def direct_messages_sent
|
145
165
|
count = options['number'] || DEFAULT_NUM_RESULTS
|
146
166
|
direct_messages = client.direct_messages_sent(:count => count, :include_entities => false)
|
147
167
|
direct_messages.reverse! if options['reverse']
|
148
|
-
if options['
|
168
|
+
if options['csv']
|
169
|
+
say ["ID", "Posted at", "Screen name", "Text"].to_csv unless direct_messages.empty?
|
170
|
+
direct_messages.each do |direct_message|
|
171
|
+
say [direct_message.id, direct_message.created_at.utc.strftime("%Y-%m-%d %H:%M:%S %z"), direct_message.recipient.screen_name, direct_message.text].to_csv
|
172
|
+
end
|
173
|
+
elsif options['long']
|
149
174
|
array = direct_messages.map do |direct_message|
|
150
175
|
created_at = direct_message.created_at > 6.months.ago ? direct_message.created_at.strftime("%b %e %H:%M") : direct_message.created_at.strftime("%b %e %Y")
|
151
176
|
[direct_message.id.to_s, created_at, "@#{direct_message.recipient.screen_name}", direct_message.text.gsub(/\n+/, ' ')]
|
@@ -161,24 +186,33 @@ module T
|
|
161
186
|
end
|
162
187
|
end
|
163
188
|
end
|
164
|
-
map %w(sent_messages sms) => :direct_messages_sent
|
189
|
+
map %w(directmessagessent sent_messages sentmessages sms) => :direct_messages_sent
|
165
190
|
|
166
|
-
desc "disciples", "Returns the list of people who follow you but you don't follow back."
|
167
|
-
method_option :
|
191
|
+
desc "disciples [USER]", "Returns the list of people who follow you but you don't follow back."
|
192
|
+
method_option :csv, :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
168
193
|
method_option :favorites, :aliases => "-v", :type => :boolean, :default => false, :desc => "Sort by number of favorites."
|
169
194
|
method_option :followers, :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
|
170
|
-
method_option :friends, :aliases => "-
|
171
|
-
method_option :
|
172
|
-
method_option :
|
195
|
+
method_option :friends, :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
|
196
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
197
|
+
method_option :listed, :aliases => "-s", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
|
198
|
+
method_option :long, :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
199
|
+
method_option :posted, :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
|
173
200
|
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
174
201
|
method_option :tweets, :aliases => "-t", :type => :boolean, :default => false, :desc => "Sort by number of Tweets."
|
175
202
|
method_option :unsorted, :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
|
176
|
-
def disciples
|
203
|
+
def disciples(user=nil)
|
204
|
+
if user
|
205
|
+
user = if options['id']
|
206
|
+
user.to_i
|
207
|
+
else
|
208
|
+
user.strip_ats
|
209
|
+
end
|
210
|
+
end
|
177
211
|
follower_ids = collect_with_cursor do |cursor|
|
178
|
-
client.follower_ids(:cursor => cursor)
|
212
|
+
client.follower_ids(user, :cursor => cursor)
|
179
213
|
end
|
180
214
|
following_ids = collect_with_cursor do |cursor|
|
181
|
-
client.friend_ids(:cursor => cursor)
|
215
|
+
client.friend_ids(user, :cursor => cursor)
|
182
216
|
end
|
183
217
|
disciple_ids = (follower_ids - following_ids)
|
184
218
|
users = disciple_ids.in_groups_of(MAX_USERS_PER_REQUEST, false).threaded_map do |disciple_id_group|
|
@@ -186,24 +220,87 @@ module T
|
|
186
220
|
client.users(disciple_id_group, :include_entities => false)
|
187
221
|
end
|
188
222
|
end.flatten
|
189
|
-
|
223
|
+
print_users(users)
|
190
224
|
end
|
191
225
|
|
192
|
-
desc "dm
|
193
|
-
|
194
|
-
|
195
|
-
|
226
|
+
desc "dm USER MESSAGE", "Sends that person a Direct Message."
|
227
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
228
|
+
def dm(user, message)
|
229
|
+
user = if options['id']
|
230
|
+
user.to_i
|
231
|
+
else
|
232
|
+
user.strip_ats
|
233
|
+
end
|
234
|
+
direct_message = client.direct_message_create(user, message, :include_entities => false)
|
196
235
|
say "Direct Message sent from @#{@rcfile.default_profile[0]} to @#{direct_message.recipient.screen_name} (#{time_ago_in_words(direct_message.created_at)} ago)."
|
197
236
|
end
|
198
237
|
map %w(d m) => :dm
|
199
238
|
|
239
|
+
desc "does_contain [USER/]LIST USER", "Find out whether a list contains a user."
|
240
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
241
|
+
def does_contain(list, user=nil)
|
242
|
+
owner, list = list.split('/')
|
243
|
+
if list.nil?
|
244
|
+
list = owner
|
245
|
+
owner = @rcfile.default_profile[0]
|
246
|
+
else
|
247
|
+
owner = if options['id']
|
248
|
+
client.user(owner.to_i, :include_entities => false).screen_name
|
249
|
+
else
|
250
|
+
owner.strip_ats
|
251
|
+
end
|
252
|
+
end
|
253
|
+
if user.nil?
|
254
|
+
user = @rcfile.default_profile[0]
|
255
|
+
else
|
256
|
+
user = if options['id']
|
257
|
+
user = client.user(user.to_i, :include_entities => false).screen_name
|
258
|
+
else
|
259
|
+
user.strip_ats
|
260
|
+
end
|
261
|
+
end
|
262
|
+
if client.list_member?(owner, list, user)
|
263
|
+
say "Yes, @#{owner}/#{list} contains @#{user}."
|
264
|
+
else
|
265
|
+
say "No, @#{owner}/#{list} does not contain @#{user}."
|
266
|
+
exit 1
|
267
|
+
end
|
268
|
+
end
|
269
|
+
map %w(dc doescontain) => :does_contain
|
270
|
+
|
271
|
+
desc "does_follow USER [USER]", "Find out whether one user follows another."
|
272
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
273
|
+
def does_follow(user1, user2=nil)
|
274
|
+
user1 = if options['id']
|
275
|
+
client.user(user1.to_i, :include_entities => false).screen_name
|
276
|
+
else
|
277
|
+
user1.strip_ats
|
278
|
+
end
|
279
|
+
if user2.nil?
|
280
|
+
user2 = @rcfile.default_profile[0]
|
281
|
+
else
|
282
|
+
user2 = if options['id']
|
283
|
+
client.user(user2.to_i, :include_entities => false).screen_name
|
284
|
+
else
|
285
|
+
user2.strip_ats
|
286
|
+
end
|
287
|
+
end
|
288
|
+
if client.friendship?(user1, user2)
|
289
|
+
say "Yes, @#{user1} follows @#{user2}."
|
290
|
+
else
|
291
|
+
say "No, @#{user1} does not follow @#{user2}."
|
292
|
+
exit 1
|
293
|
+
end
|
294
|
+
end
|
295
|
+
map %w(df doesfollow) => :does_follow
|
296
|
+
|
200
297
|
desc "favorite STATUS_ID [STATUS_ID...]", "Marks Tweets as favorites."
|
201
298
|
def favorite(status_id, *status_ids)
|
202
299
|
status_ids.unshift(status_id)
|
203
|
-
status_ids.map!(&:strip_commas)
|
300
|
+
status_ids.map!(&:strip_commas)
|
204
301
|
favorites = status_ids.threaded_map do |status_id|
|
205
302
|
retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
206
|
-
client.favorite(status_id, :include_entities => false)
|
303
|
+
client.favorite(status_id.to_i, :include_entities => false)
|
207
304
|
end
|
208
305
|
end
|
209
306
|
number = favorites.length
|
@@ -211,94 +308,135 @@ module T
|
|
211
308
|
say
|
212
309
|
say "Run `#{File.basename($0)} delete favorite #{status_ids.join(' ')}` to unfavorite."
|
213
310
|
end
|
214
|
-
map %w(fave) => :favorite
|
311
|
+
map %w(fave favourite) => :favorite
|
215
312
|
|
216
|
-
desc "favorites", "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets you favorited."
|
217
|
-
method_option :
|
313
|
+
desc "favorites [USER]", "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets you favorited."
|
314
|
+
method_option :csv, :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
315
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
316
|
+
method_option :long, :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
218
317
|
method_option :number, :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS, :desc => "Limit the number of results."
|
219
318
|
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
220
|
-
def favorites
|
319
|
+
def favorites(user=nil)
|
320
|
+
if user
|
321
|
+
user = if options['id']
|
322
|
+
user.to_i
|
323
|
+
else
|
324
|
+
user.strip_ats
|
325
|
+
end
|
326
|
+
end
|
221
327
|
count = options['number'] || DEFAULT_NUM_RESULTS
|
222
|
-
statuses = client.favorites(:count => count, :include_entities => false)
|
223
|
-
|
328
|
+
statuses = client.favorites(user, :count => count, :include_entities => false)
|
329
|
+
print_statuses(statuses)
|
224
330
|
end
|
225
|
-
map %w(faves) => :favorites
|
331
|
+
map %w(faves favourites) => :favorites
|
226
332
|
|
227
|
-
desc "follow
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
333
|
+
desc "follow USER [USER...]", "Allows you to start following users."
|
334
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify input as Twitter user IDs instead of screen names."
|
335
|
+
def follow(user, *users)
|
336
|
+
users.unshift(user)
|
337
|
+
if options['id']
|
338
|
+
users.map!(&:to_i)
|
339
|
+
else
|
340
|
+
users.map!(&:strip_ats)
|
341
|
+
end
|
342
|
+
users = users.threaded_map do |user|
|
232
343
|
retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
233
|
-
client.follow(
|
344
|
+
client.follow(user, :include_entities => false)
|
234
345
|
end
|
235
346
|
end
|
236
|
-
number =
|
347
|
+
number = users.length
|
237
348
|
say "@#{@rcfile.default_profile[0]} is now following #{number} more #{number == 1 ? 'user' : 'users'}."
|
238
349
|
say
|
239
|
-
say "Run `#{File.basename($0)} unfollow #{
|
350
|
+
say "Run `#{File.basename($0)} unfollow #{users.map{|user| "@#{user.screen_name}"}.join(' ')}` to stop."
|
240
351
|
end
|
241
352
|
|
242
|
-
desc "followings", "Returns a list of the people you follow on Twitter."
|
243
|
-
method_option :
|
353
|
+
desc "followings [USER]", "Returns a list of the people you follow on Twitter."
|
354
|
+
method_option :csv, :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
244
355
|
method_option :favorites, :aliases => "-v", :type => :boolean, :default => false, :desc => "Sort by number of favorites."
|
245
356
|
method_option :followers, :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
|
246
|
-
method_option :friends, :aliases => "-
|
247
|
-
method_option :
|
248
|
-
method_option :
|
357
|
+
method_option :friends, :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
|
358
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
359
|
+
method_option :listed, :aliases => "-s", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
|
360
|
+
method_option :long, :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
361
|
+
method_option :posted, :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
|
249
362
|
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
250
363
|
method_option :tweets, :aliases => "-t", :type => :boolean, :default => false, :desc => "Sort by number of Tweets."
|
251
364
|
method_option :unsorted, :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
|
252
|
-
def followings
|
365
|
+
def followings(user=nil)
|
366
|
+
if user
|
367
|
+
user = if options['id']
|
368
|
+
user.to_i
|
369
|
+
else
|
370
|
+
user.strip_ats
|
371
|
+
end
|
372
|
+
end
|
253
373
|
following_ids = collect_with_cursor do |cursor|
|
254
|
-
client.friend_ids(:cursor => cursor)
|
374
|
+
client.friend_ids(user, :cursor => cursor)
|
255
375
|
end
|
256
376
|
users = following_ids.in_groups_of(MAX_USERS_PER_REQUEST, false).threaded_map do |following_id_group|
|
257
377
|
retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
258
378
|
client.users(following_id_group, :include_entities => false)
|
259
379
|
end
|
260
380
|
end.flatten
|
261
|
-
|
381
|
+
print_users(users)
|
262
382
|
end
|
263
383
|
|
264
|
-
desc "followers", "Returns a list of the people who follow you on Twitter."
|
265
|
-
method_option :
|
384
|
+
desc "followers [USER]", "Returns a list of the people who follow you on Twitter."
|
385
|
+
method_option :csv, :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
266
386
|
method_option :favorites, :aliases => "-v", :type => :boolean, :default => false, :desc => "Sort by number of favorites."
|
267
387
|
method_option :followers, :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
|
268
|
-
method_option :friends, :aliases => "-
|
269
|
-
method_option :
|
270
|
-
method_option :
|
388
|
+
method_option :friends, :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
|
389
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
390
|
+
method_option :listed, :aliases => "-s", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
|
391
|
+
method_option :long, :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
392
|
+
method_option :posted, :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
|
271
393
|
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
272
394
|
method_option :tweets, :aliases => "-t", :type => :boolean, :default => false, :desc => "Sort by number of Tweets."
|
273
395
|
method_option :unsorted, :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
|
274
|
-
def followers
|
396
|
+
def followers(user=nil)
|
397
|
+
if user
|
398
|
+
user = if options['id']
|
399
|
+
user.to_i
|
400
|
+
else
|
401
|
+
user.strip_ats
|
402
|
+
end
|
403
|
+
end
|
275
404
|
follower_ids = collect_with_cursor do |cursor|
|
276
|
-
client.follower_ids(:cursor => cursor)
|
405
|
+
client.follower_ids(user, :cursor => cursor)
|
277
406
|
end
|
278
407
|
users = follower_ids.in_groups_of(MAX_USERS_PER_REQUEST, false).threaded_map do |follower_id_group|
|
279
408
|
retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
280
409
|
client.users(follower_id_group, :include_entities => false)
|
281
410
|
end
|
282
411
|
end.flatten
|
283
|
-
|
412
|
+
print_users(users)
|
284
413
|
end
|
285
414
|
|
286
|
-
desc "friends", "Returns the list of people who you follow and follow you back."
|
287
|
-
method_option :
|
415
|
+
desc "friends [USER]", "Returns the list of people who you follow and follow you back."
|
416
|
+
method_option :csv, :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
288
417
|
method_option :favorites, :aliases => "-v", :type => :boolean, :default => false, :desc => "Sort by number of favorites."
|
289
418
|
method_option :followers, :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
|
290
|
-
method_option :friends, :aliases => "-
|
291
|
-
method_option :
|
292
|
-
method_option :
|
419
|
+
method_option :friends, :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
|
420
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
421
|
+
method_option :listed, :aliases => "-s", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
|
422
|
+
method_option :long, :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
423
|
+
method_option :posted, :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
|
293
424
|
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
294
425
|
method_option :tweets, :aliases => "-t", :type => :boolean, :default => false, :desc => "Sort by number of Tweets."
|
295
426
|
method_option :unsorted, :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
|
296
|
-
def friends
|
427
|
+
def friends(user=nil)
|
428
|
+
if user
|
429
|
+
user = if options['id']
|
430
|
+
user.to_i
|
431
|
+
else
|
432
|
+
user.strip_ats
|
433
|
+
end
|
434
|
+
end
|
297
435
|
following_ids = collect_with_cursor do |cursor|
|
298
|
-
client.friend_ids(:cursor => cursor)
|
436
|
+
client.friend_ids(user, :cursor => cursor)
|
299
437
|
end
|
300
438
|
follower_ids = collect_with_cursor do |cursor|
|
301
|
-
client.follower_ids(:cursor => cursor)
|
439
|
+
client.follower_ids(user, :cursor => cursor)
|
302
440
|
end
|
303
441
|
friend_ids = (following_ids & follower_ids)
|
304
442
|
users = friend_ids.in_groups_of(MAX_USERS_PER_REQUEST, false).threaded_map do |friend_id_group|
|
@@ -306,25 +444,34 @@ module T
|
|
306
444
|
client.users(friend_id_group, :include_entities => false)
|
307
445
|
end
|
308
446
|
end.flatten
|
309
|
-
|
447
|
+
print_users(users)
|
310
448
|
end
|
311
449
|
|
312
|
-
desc "leaders", "Returns the list of people who you follow but don't follow you back."
|
313
|
-
method_option :
|
450
|
+
desc "leaders [USER]", "Returns the list of people who you follow but don't follow you back."
|
451
|
+
method_option :csv, :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
314
452
|
method_option :favorites, :aliases => "-v", :type => :boolean, :default => false, :desc => "Sort by number of favorites."
|
315
453
|
method_option :followers, :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
|
316
|
-
method_option :friends, :aliases => "-
|
317
|
-
method_option :
|
318
|
-
method_option :
|
454
|
+
method_option :friends, :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
|
455
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
456
|
+
method_option :listed, :aliases => "-s", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
|
457
|
+
method_option :long, :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
458
|
+
method_option :posted, :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
|
319
459
|
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
320
460
|
method_option :tweets, :aliases => "-t", :type => :boolean, :default => false, :desc => "Sort by number of Tweets."
|
321
461
|
method_option :unsorted, :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
|
322
|
-
def leaders
|
462
|
+
def leaders(user=nil)
|
463
|
+
if user
|
464
|
+
user = if options['id']
|
465
|
+
user.to_i
|
466
|
+
else
|
467
|
+
user.strip_ats
|
468
|
+
end
|
469
|
+
end
|
323
470
|
following_ids = collect_with_cursor do |cursor|
|
324
|
-
client.friend_ids(:cursor => cursor)
|
471
|
+
client.friend_ids(user, :cursor => cursor)
|
325
472
|
end
|
326
473
|
follower_ids = collect_with_cursor do |cursor|
|
327
|
-
client.follower_ids(:cursor => cursor)
|
474
|
+
client.follower_ids(user, :cursor => cursor)
|
328
475
|
end
|
329
476
|
leader_ids = (following_ids - follower_ids)
|
330
477
|
users = leader_ids.in_groups_of(MAX_USERS_PER_REQUEST, false).threaded_map do |leader_id_group|
|
@@ -332,62 +479,105 @@ module T
|
|
332
479
|
client.users(leader_id_group, :include_entities => false)
|
333
480
|
end
|
334
481
|
end.flatten
|
335
|
-
|
482
|
+
print_users(users)
|
483
|
+
end
|
484
|
+
|
485
|
+
desc "lists [USER]", "Returns the lists created by a user."
|
486
|
+
method_option :csv, :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
487
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
488
|
+
method_option :long, :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
489
|
+
method_option :members, :aliases => "-m", :type => :boolean, :default => false, :desc => "Sort by number of members."
|
490
|
+
method_option :mode, :aliases => "-o", :type => :boolean, :default => false, :desc => "Sort by mode."
|
491
|
+
method_option :posted, :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter list was posted."
|
492
|
+
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
493
|
+
method_option :subscribers, :aliases => "-s", :type => :boolean, :default => false, :desc => "Sort by number of subscribers."
|
494
|
+
method_option :unsorted, :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
|
495
|
+
def lists(user=nil)
|
496
|
+
if user
|
497
|
+
user = if options['id']
|
498
|
+
user.to_i
|
499
|
+
else
|
500
|
+
user.strip_ats
|
501
|
+
end
|
502
|
+
end
|
503
|
+
lists = collect_with_cursor do |cursor|
|
504
|
+
client.lists(user, :cursor => cursor)
|
505
|
+
end
|
506
|
+
print_lists(lists)
|
336
507
|
end
|
337
508
|
|
338
509
|
desc "mentions", "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets mentioning you."
|
339
|
-
method_option :
|
510
|
+
method_option :csv, :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
511
|
+
method_option :long, :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
340
512
|
method_option :number, :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS, :desc => "Limit the number of results."
|
341
513
|
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
342
514
|
def mentions
|
343
515
|
count = options['number'] || DEFAULT_NUM_RESULTS
|
344
516
|
statuses = client.mentions(:count => count, :include_entities => false)
|
345
|
-
|
517
|
+
print_statuses(statuses)
|
346
518
|
end
|
347
519
|
map %w(replies) => :mentions
|
348
520
|
|
349
|
-
desc "open
|
350
|
-
method_option :
|
351
|
-
|
352
|
-
|
353
|
-
|
521
|
+
desc "open USER", "Opens that user's profile in a web browser."
|
522
|
+
method_option :display_url, :aliases => "-d", :type => :boolean, :default => false, :desc => "Display the requested URL instead of attempting to open it."
|
523
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
524
|
+
method_option :status, :aliases => "-s", :type => :boolean, :default => false, :desc => "Specify input as a Twitter status ID instead of a screen name."
|
525
|
+
def open(user)
|
526
|
+
if options['id']
|
527
|
+
user = client.user(user.to_i, :include_entities => false)
|
528
|
+
Launchy.open("https://twitter.com/#{user.screen_name}", :dry_run => options['display_url'])
|
529
|
+
elsif options['status']
|
530
|
+
status = client.status(user.to_i, :include_entities => false, :include_my_retweet => false)
|
531
|
+
Launchy.open("https://twitter.com/#{status.user.screen_name}/status/#{status.id}", :dry_run => options['display_url'])
|
532
|
+
else
|
533
|
+
Launchy.open("https://twitter.com/#{user.strip_ats}", :dry_run => options['display_url'])
|
534
|
+
end
|
354
535
|
end
|
355
536
|
|
356
537
|
desc "reply STATUS_ID MESSAGE", "Post your Tweet as a reply directed at another person."
|
538
|
+
method_option :all, :aliases => "-a", :type => "boolean", :default => false, :desc => "Reply to all users mentioned in the Tweet."
|
357
539
|
method_option :location, :aliases => "-l", :type => :boolean, :default => false
|
358
540
|
def reply(status_id, message)
|
359
541
|
status_id = status_id.strip_commas
|
360
|
-
status = client.status(status_id, :include_entities => false, :include_my_retweet => false)
|
542
|
+
status = client.status(status_id.to_i, :include_entities => false, :include_my_retweet => false)
|
543
|
+
users = Array(status.user.screen_name)
|
544
|
+
users += extract_mentioned_screen_names(status.text) if options['all']
|
545
|
+
users.uniq!
|
546
|
+
users.map!(&:prepend_at)
|
361
547
|
opts = {:in_reply_to_status_id => status.id, :include_entities => false, :trim_user => true}
|
362
548
|
opts.merge!(:lat => location.lat, :long => location.lng) if options['location']
|
363
|
-
reply = client.update("
|
364
|
-
say "Reply created by @#{@rcfile.default_profile[0]} to
|
549
|
+
reply = client.update("#{users.join(' ')} #{message}", opts)
|
550
|
+
say "Reply created by @#{@rcfile.default_profile[0]} to #{users.join(' ')} (#{time_ago_in_words(reply.created_at)} ago)."
|
365
551
|
say
|
366
552
|
say "Run `#{File.basename($0)} delete status #{reply.id}` to delete."
|
367
553
|
end
|
368
554
|
|
369
|
-
desc "report_spam
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
555
|
+
desc "report_spam USER [USER...]", "Report users for spam."
|
556
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify input as Twitter user IDs instead of screen names."
|
557
|
+
def report_spam(user, *users)
|
558
|
+
users.unshift(user)
|
559
|
+
if options['id']
|
560
|
+
users.map!(&:to_i)
|
561
|
+
else
|
562
|
+
users.map!(&:strip_ats)
|
563
|
+
end
|
564
|
+
users = users.threaded_map do |user|
|
375
565
|
retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
376
|
-
client.report_spam(
|
566
|
+
client.report_spam(user, :include_entities => false)
|
377
567
|
end
|
378
568
|
end
|
379
|
-
number =
|
569
|
+
number = users.length
|
380
570
|
say "@#{@rcfile.default_profile[0]} reported #{number} #{number == 1 ? 'user' : 'users'}."
|
381
571
|
end
|
382
|
-
map %w(report spam) => :report_spam
|
572
|
+
map %w(report reportspam spam) => :report_spam
|
383
573
|
|
384
574
|
desc "retweet STATUS_ID [STATUS_ID...]", "Sends Tweets to your followers."
|
385
575
|
def retweet(status_id, *status_ids)
|
386
576
|
status_ids.unshift(status_id)
|
387
|
-
status_ids.map!(&:strip_commas)
|
577
|
+
status_ids.map!(&:strip_commas)
|
388
578
|
retweets = status_ids.threaded_map do |status_id|
|
389
579
|
retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
390
|
-
client.retweet(status_id, :include_entities => false, :trim_user => true)
|
580
|
+
client.retweet(status_id.to_i, :include_entities => false, :trim_user => true)
|
391
581
|
end
|
392
582
|
end
|
393
583
|
number = retweets.length
|
@@ -397,90 +587,182 @@ module T
|
|
397
587
|
end
|
398
588
|
map %w(rt) => :retweet
|
399
589
|
|
400
|
-
desc "retweets [
|
401
|
-
method_option :
|
590
|
+
desc "retweets [USER]", "Returns the #{DEFAULT_NUM_RESULTS} most recent Retweets by a user."
|
591
|
+
method_option :csv, :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
592
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
593
|
+
method_option :long, :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
402
594
|
method_option :number, :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS, :desc => "Limit the number of results."
|
403
595
|
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
404
|
-
def retweets(
|
405
|
-
|
596
|
+
def retweets(user=nil)
|
597
|
+
if user
|
598
|
+
user = if options['id']
|
599
|
+
user.to_i
|
600
|
+
else
|
601
|
+
user.strip_ats
|
602
|
+
end
|
603
|
+
end
|
406
604
|
count = options['number'] || DEFAULT_NUM_RESULTS
|
407
|
-
statuses = client.retweeted_by(
|
408
|
-
|
605
|
+
statuses = client.retweeted_by(user, :count => count, :include_entities => false)
|
606
|
+
print_statuses(statuses)
|
409
607
|
end
|
410
608
|
map %w(rts) => :retweets
|
411
609
|
|
610
|
+
desc "ruler", "Prints a 140-character ruler"
|
611
|
+
def ruler
|
612
|
+
say "----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|----|"
|
613
|
+
end
|
614
|
+
|
412
615
|
desc "status STATUS_ID", "Retrieves detailed information about a Tweet."
|
616
|
+
method_option :csv, :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
413
617
|
def status(status_id)
|
414
618
|
status_id = status_id.strip_commas
|
415
|
-
status = client.status(status_id, :include_entities => false, :include_my_retweet => false)
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
619
|
+
status = client.status(status_id.to_i, :include_entities => false, :include_my_retweet => false)
|
620
|
+
if status.geo
|
621
|
+
geoloc = Geokit::Geocoders::MultiGeocoder.reverse_geocode(status.geo.coordinates)
|
622
|
+
location = if geoloc.city && geoloc.state && geoloc.country
|
623
|
+
[geoloc.city, geoloc.state, geoloc.country].join(", ")
|
624
|
+
elsif geoloc.state && geoloc.country
|
625
|
+
[geoloc.state, geoloc.country].join(", ")
|
626
|
+
else
|
627
|
+
geoloc.country
|
628
|
+
end
|
629
|
+
else
|
630
|
+
location = nil
|
631
|
+
end
|
632
|
+
if options['csv']
|
633
|
+
say ["ID", "Text", "Screen name", "Posted at", "Location", "Retweets", "Source", "URL"].to_csv
|
634
|
+
say [status.id, status.text, status.user.screen_name, status.created_at.utc.strftime("%Y-%m-%d %H:%M:%S %z"), location, status.retweet_count, strip_tags(status.source), "https://twitter.com/#{status.user.screen_name}/status/#{status.id}"].to_csv
|
635
|
+
else
|
636
|
+
array = []
|
637
|
+
array << ["ID", status.id.to_s]
|
638
|
+
array << ["Text", status.text.gsub(/\n+/, ' ')]
|
639
|
+
array << ["Screen name", "@#{status.user.screen_name}"]
|
640
|
+
posted_at = status.created_at > 6.months.ago ? status.created_at.strftime("%b %e %H:%M") : status.created_at.strftime("%b %e %Y")
|
641
|
+
array << ["Posted at", posted_at]
|
642
|
+
array << ["Location", location] unless location.nil?
|
643
|
+
array << ["Retweets", number_with_delimiter(status.retweet_count)]
|
644
|
+
array << ["Source", strip_tags(status.source)]
|
645
|
+
array << ["URL", "https://twitter.com/#{status.user.screen_name}/status/#{status.id}"]
|
646
|
+
print_table(array)
|
647
|
+
end
|
648
|
+
end
|
649
|
+
|
650
|
+
desc "suggest [USER]", "Returns a listing of Twitter users' accounts you might enjoy following."
|
651
|
+
method_option :csv, :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
440
652
|
method_option :favorites, :aliases => "-v", :type => :boolean, :default => false, :desc => "Sort by number of favorites."
|
441
653
|
method_option :followers, :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
|
442
|
-
method_option :friends, :aliases => "-
|
443
|
-
method_option :
|
444
|
-
method_option :
|
654
|
+
method_option :friends, :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
|
655
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
656
|
+
method_option :listed, :aliases => "-s", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
|
657
|
+
method_option :long, :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
445
658
|
method_option :number, :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS, :desc => "Limit the number of results."
|
659
|
+
method_option :posted, :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
|
446
660
|
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
447
661
|
method_option :tweets, :aliases => "-t", :type => :boolean, :default => false, :desc => "Sort by number of Tweets."
|
448
662
|
method_option :unsorted, :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
|
449
|
-
def suggest
|
663
|
+
def suggest(user=nil)
|
664
|
+
if user
|
665
|
+
user = if options['id']
|
666
|
+
user.to_i
|
667
|
+
else
|
668
|
+
user.strip_ats
|
669
|
+
end
|
670
|
+
end
|
450
671
|
limit = options['number'] || DEFAULT_NUM_RESULTS
|
451
|
-
users = client.recommendations(:limit => limit, :include_entities => false)
|
452
|
-
|
672
|
+
users = client.recommendations(user, :limit => limit, :include_entities => false)
|
673
|
+
print_users(users)
|
453
674
|
end
|
454
675
|
|
455
|
-
desc "timeline [
|
456
|
-
method_option :
|
676
|
+
desc "timeline [USER]", "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets posted by a user."
|
677
|
+
method_option :csv, :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
678
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
679
|
+
method_option :long, :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
457
680
|
method_option :number, :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS, :desc => "Limit the number of results."
|
458
681
|
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
459
|
-
def timeline(
|
682
|
+
def timeline(user=nil)
|
460
683
|
count = options['number'] || DEFAULT_NUM_RESULTS
|
461
|
-
if
|
462
|
-
|
463
|
-
|
684
|
+
if user
|
685
|
+
user = if options['id']
|
686
|
+
user.to_i
|
687
|
+
else
|
688
|
+
user.strip_ats
|
689
|
+
end
|
690
|
+
statuses = client.user_timeline(user, :count => count, :include_entities => false)
|
464
691
|
else
|
465
692
|
statuses = client.home_timeline(:count => count, :include_entities => false)
|
466
693
|
end
|
467
|
-
|
694
|
+
print_statuses(statuses)
|
468
695
|
end
|
469
696
|
map %w(tl) => :timeline
|
470
697
|
|
471
|
-
desc "
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
698
|
+
desc "trends [WOEID]", "Returns the top 10 trending topics."
|
699
|
+
method_option :exclude_hashtags, :aliases => "-x", :type => "boolean", :default => false, :desc => "Remove all hashtags from the trends list."
|
700
|
+
def trends(woe_id=1)
|
701
|
+
opts = {}
|
702
|
+
opts.merge!(:exclude => "hashtags") if options['exclude_hashtags']
|
703
|
+
trends = client.trends(woe_id, opts)
|
704
|
+
if STDOUT.tty?
|
705
|
+
print_in_columns(trends.map(&:name))
|
706
|
+
else
|
707
|
+
trends.each do |trend|
|
708
|
+
say trend.name
|
709
|
+
end
|
710
|
+
end
|
711
|
+
end
|
712
|
+
|
713
|
+
desc "trends_locations", "Returns the locations for which Twitter has trending topic information."
|
714
|
+
method_option :csv, :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
715
|
+
method_option :long, :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
716
|
+
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
717
|
+
method_option :unsorted, :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
|
718
|
+
def trend_locations
|
719
|
+
places = client.trend_locations
|
720
|
+
places = places.sort_by{|places| places.name.downcase} unless options['unsorted']
|
721
|
+
places.reverse! if options['reverse']
|
722
|
+
if options['csv']
|
723
|
+
say ["WOEID", "Parent ID", "Type", "Name", "Country"].to_csv unless places.empty?
|
724
|
+
places.each do |place|
|
725
|
+
say [place.woeid, place.parent_id, place.place_type, place.name, place.country].to_csv
|
726
|
+
end
|
727
|
+
elsif options['long']
|
728
|
+
array = places.map do |place|
|
729
|
+
[place.woeid.to_s, place.parent_id.to_s, place.place_type, place.name, place.country]
|
730
|
+
end
|
731
|
+
if STDOUT.tty?
|
732
|
+
headings = ["WOEID", "Parent ID", "Type", "Name", "Country"]
|
733
|
+
array.unshift(headings) unless places.empty?
|
734
|
+
end
|
735
|
+
print_table(array)
|
736
|
+
else
|
737
|
+
if STDOUT.tty?
|
738
|
+
print_in_columns(places.map(&:name))
|
739
|
+
else
|
740
|
+
places.each do |place|
|
741
|
+
say place.name
|
742
|
+
end
|
743
|
+
end
|
744
|
+
end
|
745
|
+
end
|
746
|
+
map %w(locations trendlocations) => :trend_locations
|
747
|
+
|
748
|
+
desc "unfollow USER [USER...]", "Allows you to stop following users."
|
749
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify input as Twitter user IDs instead of screen names."
|
750
|
+
def unfollow(user, *users)
|
751
|
+
users.unshift(user)
|
752
|
+
if options['id']
|
753
|
+
users.map!(&:to_i)
|
754
|
+
else
|
755
|
+
users.map!(&:strip_ats)
|
756
|
+
end
|
757
|
+
users = users.threaded_map do |user|
|
476
758
|
retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
477
|
-
client.unfollow(
|
759
|
+
client.unfollow(user, :include_entities => false)
|
478
760
|
end
|
479
761
|
end
|
480
|
-
number =
|
762
|
+
number = users.length
|
481
763
|
say "@#{@rcfile.default_profile[0]} is no longer following #{number} #{number == 1 ? 'user' : 'users'}."
|
482
764
|
say
|
483
|
-
say "Run `#{File.basename($0)} follow #{
|
765
|
+
say "Run `#{File.basename($0)} follow #{users.map{|user| "@#{user.screen_name}"}.join(' ')}` to follow again."
|
484
766
|
end
|
485
767
|
|
486
768
|
desc "update MESSAGE", "Post a Tweet."
|
@@ -495,21 +777,27 @@ module T
|
|
495
777
|
end
|
496
778
|
map %w(post tweet) => :update
|
497
779
|
|
498
|
-
desc "users
|
499
|
-
method_option :
|
780
|
+
desc "users USER [USER...]", "Returns a list of users you specify."
|
781
|
+
method_option :csv, :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
500
782
|
method_option :favorites, :aliases => "-v", :type => :boolean, :default => false, :desc => "Sort by number of favorites."
|
501
783
|
method_option :followers, :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
|
502
|
-
method_option :friends, :aliases => "-
|
503
|
-
method_option :
|
504
|
-
method_option :
|
784
|
+
method_option :friends, :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
|
785
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify input as Twitter user IDs instead of screen names."
|
786
|
+
method_option :listed, :aliases => "-s", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
|
787
|
+
method_option :long, :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
788
|
+
method_option :posted, :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
|
505
789
|
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
506
790
|
method_option :tweets, :aliases => "-t", :type => :boolean, :default => false, :desc => "Sort by number of Tweets."
|
507
791
|
method_option :unsorted, :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
|
508
|
-
def users(
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
792
|
+
def users(user, *users)
|
793
|
+
users.unshift(user)
|
794
|
+
if options['id']
|
795
|
+
users.map!(&:to_i)
|
796
|
+
else
|
797
|
+
users.map!(&:strip_ats)
|
798
|
+
end
|
799
|
+
users = client.users(users, :include_entities => false)
|
800
|
+
print_users(users)
|
513
801
|
end
|
514
802
|
map %w(stats) => :users
|
515
803
|
|
@@ -519,39 +807,49 @@ module T
|
|
519
807
|
end
|
520
808
|
map %w(-v --version) => :version
|
521
809
|
|
522
|
-
desc "whois
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
810
|
+
desc "whois USER", "Retrieves profile information for the user."
|
811
|
+
method_option :csv, :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
812
|
+
method_option :id, :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
813
|
+
def whois(user)
|
814
|
+
user = if options['id']
|
815
|
+
user.to_i
|
816
|
+
else
|
817
|
+
user.strip_ats
|
818
|
+
end
|
819
|
+
user = client.user(user, :include_entities => false)
|
820
|
+
if options['csv']
|
821
|
+
say ["ID", "Verified", "Name", "Screen name", "Bio", "Location", "Following", "Last update", "Lasted updated at", "Since", "Tweets", "Favorites", "Listed", "Following", "Followers", "URL"].to_csv
|
822
|
+
say [user.id, user.verified?, user.name, user.screen_name, user.description, user.location, user.following?, user.status.text, user.status.created_at.utc.strftime("%Y-%m-%d %H:%M:%S %z"), user.created_at.utc.strftime("%Y-%m-%d %H:%M:%S %z"), user.statuses_count, user.favourites_count, user.listed_count, user.friends_count, user.followers_count, user.url].to_csv
|
823
|
+
else
|
824
|
+
array = []
|
825
|
+
name_label = user.verified ? "Name (Verified)" : "Name"
|
826
|
+
array << ["ID", user.id.to_s]
|
827
|
+
array << [name_label, user.name] unless user.name.nil?
|
828
|
+
array << ["Bio", user.description.gsub(/\n+/, ' ')] unless user.description.nil?
|
829
|
+
array << ["Location", user.location] unless user.location.nil?
|
830
|
+
status = user.following ? "Following" : "Not following"
|
831
|
+
array << ["Status", status]
|
832
|
+
array << ["Last update", "#{user.status.text.gsub(/\n+/, ' ')} (#{time_ago_in_words(user.status.created_at)} ago)"] unless user.status.nil?
|
833
|
+
created_at = user.created_at > 6.months.ago ? user.created_at.strftime("%b %e %H:%M") : user.created_at.strftime("%b %e %Y")
|
834
|
+
array << ["Since", created_at]
|
835
|
+
array << ["Tweets", number_with_delimiter(user.statuses_count)]
|
836
|
+
array << ["Favorites", number_with_delimiter(user.favourites_count)]
|
837
|
+
array << ["Listed", number_with_delimiter(user.listed_count)]
|
838
|
+
array << ["Following", number_with_delimiter(user.friends_count)]
|
839
|
+
array << ["Followers", number_with_delimiter(user.followers_count)]
|
840
|
+
array << ["URL", user.url] unless user.url.nil?
|
841
|
+
print_table(array)
|
842
|
+
end
|
544
843
|
end
|
844
|
+
map %w(user) => :whois
|
545
845
|
|
546
846
|
desc "delete SUBCOMMAND ...ARGS", "Delete Tweets, Direct Messages, etc."
|
547
|
-
method_option :force, :aliases => "-f", :type => :boolean, :default => false
|
548
847
|
subcommand 'delete', T::Delete
|
549
848
|
|
550
849
|
desc "list SUBCOMMAND ...ARGS", "Do various things with lists."
|
551
850
|
subcommand 'list', T::List
|
552
851
|
|
553
852
|
desc "search SUBCOMMAND ...ARGS", "Search through Tweets."
|
554
|
-
method_option :long, :aliases => "-l", :type => :boolean, :default => false, :desc => "List in long format."
|
555
853
|
subcommand 'search', T::Search
|
556
854
|
|
557
855
|
desc "set SUBCOMMAND ...ARGS", "Change various account settings."
|