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