t 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +29 -13
- data/lib/t/cli.rb +51 -59
- data/lib/t/cli/follow.rb +47 -6
- data/lib/t/cli/list.rb +10 -8
- data/lib/t/cli/list/add.rb +98 -4
- data/lib/t/cli/list/remove.rb +99 -6
- data/lib/t/cli/search.rb +128 -0
- data/lib/t/cli/set.rb +1 -1
- data/lib/t/cli/unfollow.rb +90 -6
- data/lib/t/collectable.rb +12 -0
- data/lib/t/version.rb +2 -2
- data/spec/cli/follow_spec.rb +192 -3
- data/spec/cli/list/add_spec.rb +407 -14
- data/spec/cli/list/remove_spec.rb +392 -2
- data/spec/cli/search_spec.rb +205 -0
- data/spec/cli/unfollow_spec.rb +362 -3
- data/spec/cli_spec.rb +72 -99
- data/spec/rcfile_spec.rb +15 -17
- data/t.gemspec +1 -0
- metadata +45 -42
- data/lib/t/cli/follow/all.rb +0 -97
- data/lib/t/cli/list/add/all.rb +0 -169
- data/lib/t/cli/list/remove/all.rb +0 -163
- data/lib/t/cli/unfollow/all.rb +0 -148
- data/spec/cli/follow/all_spec.rb +0 -158
- data/spec/cli/list/add/all_spec.rb +0 -435
- data/spec/cli/list/remove/all_spec.rb +0 -315
- data/spec/cli/unfollow/all_spec.rb +0 -292
data/README.md
CHANGED
@@ -66,10 +66,6 @@ type `t help TASK` to get help for a specific command.
|
|
66
66
|
|
67
67
|
t set location "San Francisco"
|
68
68
|
|
69
|
-
### <a name="get"></a>Get the latest Tweet posted by a user
|
70
|
-
|
71
|
-
t get sferik
|
72
|
-
|
73
69
|
### <a name="whois"></a>Retrieve profile information for a user
|
74
70
|
|
75
71
|
t whois sferik
|
@@ -86,17 +82,17 @@ type `t help TASK` to get help for a specific command.
|
|
86
82
|
|
87
83
|
t follow users sferik gem
|
88
84
|
|
89
|
-
### <a name="follow-
|
85
|
+
### <a name="follow-followers"></a>Follow all followers (i.e. follow back)
|
90
86
|
|
91
|
-
t follow
|
87
|
+
t follow followers
|
92
88
|
|
93
89
|
### <a name="unfollow-users"></a>Stop following users
|
94
90
|
|
95
91
|
t unfollow users sferik gem
|
96
92
|
|
97
|
-
### <a name="unfollow-
|
93
|
+
### <a name="unfollow-nonfollowers"></a>Unfollow all non-followers
|
98
94
|
|
99
|
-
t unfollow
|
95
|
+
t unfollow nonfollowers
|
100
96
|
|
101
97
|
### <a name="list-create"></a>Create a list
|
102
98
|
|
@@ -108,28 +104,36 @@ type `t help TASK` to get help for a specific command.
|
|
108
104
|
|
109
105
|
### <a name="list-add-friends"></a>Add all friends to a list
|
110
106
|
|
111
|
-
t list add
|
107
|
+
t list add friends presidents
|
112
108
|
|
113
109
|
### <a name="list-add-followers"></a>Add all followers to a list
|
114
110
|
|
115
|
-
t list add
|
111
|
+
t list add followers presidents
|
116
112
|
|
117
113
|
### <a name="list-add-followers"></a>Add all members of one list to another
|
118
114
|
|
119
|
-
t list add
|
115
|
+
t list add listed democrats presidents
|
120
116
|
|
121
117
|
### <a name="follow-all-listed"></a>Follow all members of a list
|
122
118
|
|
123
|
-
t follow
|
119
|
+
t follow listed presidents
|
124
120
|
|
125
121
|
### <a name="unfollow-all-listed"></a>Unfollow all members of a list
|
126
122
|
|
127
|
-
t unfollow
|
123
|
+
t unfollow listed presidents
|
128
124
|
|
129
125
|
### <a name="list-timeline"></a>Retrieve the timeline of status updates from a list
|
130
126
|
|
131
127
|
t list timeline presidents
|
132
128
|
|
129
|
+
### <a name="timeline"></a>Retrieve the timeline of status updates posted by you and the users you follow
|
130
|
+
|
131
|
+
t timeline
|
132
|
+
|
133
|
+
### <a name="timeline-user"></a>Retrieve the timeline of status updates posted by a user
|
134
|
+
|
135
|
+
t timeline sferik
|
136
|
+
|
133
137
|
### <a name="mentions"></a>Retrieve the timeline of status updates that mention you
|
134
138
|
|
135
139
|
t mentions
|
@@ -150,6 +154,18 @@ type `t help TASK` to get help for a specific command.
|
|
150
154
|
|
151
155
|
t favorite sferik
|
152
156
|
|
157
|
+
### <a name="search-all"></a>Retrieve the 20 most recent Tweets that match a specified query
|
158
|
+
|
159
|
+
t search all twitter
|
160
|
+
|
161
|
+
### <a name="search-timeline"></a>Retrieve Tweets in your timeline that match a specified query
|
162
|
+
|
163
|
+
t search timeline twitter
|
164
|
+
|
165
|
+
### <a name="search-user"></a>Retrieve Tweets in a user's timeline that match a specified query
|
166
|
+
|
167
|
+
t search user sferik twitter
|
168
|
+
|
153
169
|
## <a name="history"></a>History
|
154
170
|
![History](http://twitter.rubyforge.org/images/terminal_output.png "History")
|
155
171
|
|
data/lib/t/cli.rb
CHANGED
@@ -15,13 +15,15 @@ module T
|
|
15
15
|
|
16
16
|
DEFAULT_HOST = 'api.twitter.com'
|
17
17
|
DEFAULT_PROTOCOL = 'https'
|
18
|
-
|
19
|
-
|
20
|
-
class_option :no_ssl, :aliases => "-U", :type => :boolean, :default => false, :desc => "Disable SSL"
|
21
|
-
class_option :profile, :aliases => "-P", :type => :string, :default => File.join(File.expand_path("~"), RCFile::FILE_NAME), :desc => "Path to RC file", :banner => "FILE"
|
18
|
+
DEFAULT_NUM_RESULTS = 20
|
19
|
+
MAX_SCREEN_NAME_SIZE = 20
|
22
20
|
|
23
21
|
check_unknown_options!
|
24
22
|
|
23
|
+
option :host, :aliases => "-H", :type => :string, :default => DEFAULT_HOST, :desc => "Twitter API server"
|
24
|
+
option :no_ssl, :aliases => "-U", :type => :boolean, :default => false, :desc => "Disable SSL"
|
25
|
+
option :profile, :aliases => "-P", :type => :string, :default => File.join(File.expand_path("~"), RCFile::FILE_NAME), :desc => "Path to RC file", :banner => "FILE"
|
26
|
+
|
25
27
|
def initialize(*)
|
26
28
|
super
|
27
29
|
@rcfile = RCFile.instance
|
@@ -86,10 +88,13 @@ module T
|
|
86
88
|
end
|
87
89
|
|
88
90
|
desc "direct_messages", "Returns the 20 most recent Direct Messages sent to you."
|
91
|
+
method_option :number, :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS
|
89
92
|
def direct_messages
|
93
|
+
defaults = {:include_entities => false}
|
94
|
+
defaults.merge!(:count => options['number']) if options['number']
|
90
95
|
run_pager
|
91
|
-
client.direct_messages(
|
92
|
-
say "#{direct_message.sender.screen_name.rjust(
|
96
|
+
client.direct_messages(defaults).each do |direct_message|
|
97
|
+
say "#{direct_message.sender.screen_name.rjust(MAX_SCREEN_NAME_SIZE)}: #{direct_message.text} (#{time_ago_in_words(direct_message.created_at)} ago)"
|
93
98
|
end
|
94
99
|
end
|
95
100
|
map %w(dms) => :direct_messages
|
@@ -123,43 +128,32 @@ module T
|
|
123
128
|
end
|
124
129
|
map %w(fave) => :favorite
|
125
130
|
|
126
|
-
desc "favorites", "Returns the
|
127
|
-
method_option :number, :aliases => "-n", :type => :numeric, :default =>
|
131
|
+
desc "favorites", "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets you favorited."
|
132
|
+
method_option :number, :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS
|
128
133
|
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false
|
129
134
|
def favorites
|
130
|
-
|
131
|
-
|
132
|
-
timeline = client.favorites(
|
135
|
+
defaults = {:include_entities => false}
|
136
|
+
defaults.merge!(:count => options['number']) if options['number']
|
137
|
+
timeline = client.favorites(defaults)
|
133
138
|
timeline.reverse! if options['reverse']
|
134
139
|
run_pager
|
135
140
|
timeline.each do |status|
|
136
|
-
say "#{status.user.screen_name.rjust(
|
141
|
+
say "#{status.user.screen_name.rjust(MAX_SCREEN_NAME_SIZE)}: #{status.text} (#{time_ago_in_words(status.created_at)} ago)"
|
137
142
|
end
|
138
143
|
end
|
139
144
|
map %w(faves) => :favorites
|
140
145
|
|
141
|
-
desc "
|
142
|
-
|
143
|
-
screen_name = screen_name.strip_at
|
144
|
-
user = client.user(screen_name, :include_entities => false)
|
145
|
-
if user.status
|
146
|
-
say "#{user.status.text} (#{time_ago_in_words(user.status.created_at)} ago)"
|
147
|
-
else
|
148
|
-
raise Thor::Error, "Tweet not found"
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
desc "mentions", "Returns the 20 most recent Tweets mentioning you."
|
153
|
-
method_option :number, :aliases => "-n", :type => :numeric, :default => 20
|
146
|
+
desc "mentions", "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets mentioning you."
|
147
|
+
method_option :number, :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS
|
154
148
|
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false
|
155
149
|
def mentions
|
156
|
-
|
157
|
-
|
158
|
-
timeline = client.mentions(
|
150
|
+
defaults = {:include_entities => false}
|
151
|
+
defaults.merge!(:count => options['number']) if options['number']
|
152
|
+
timeline = client.mentions(defaults)
|
159
153
|
timeline.reverse! if options['reverse']
|
160
154
|
run_pager
|
161
155
|
timeline.each do |status|
|
162
|
-
say "#{status.user.screen_name.rjust(
|
156
|
+
say "#{status.user.screen_name.rjust(MAX_SCREEN_NAME_SIZE)}: #{status.text} (#{time_ago_in_words(status.created_at)} ago)"
|
163
157
|
end
|
164
158
|
end
|
165
159
|
map %w(replies) => :mentions
|
@@ -175,11 +169,11 @@ module T
|
|
175
169
|
method_option :location, :aliases => "-l", :type => :boolean, :default => true
|
176
170
|
def reply(screen_name, message)
|
177
171
|
screen_name = screen_name.strip_at
|
178
|
-
|
179
|
-
|
172
|
+
defaults = {:include_entities => false, :trim_user => true}
|
173
|
+
defaults.merge!(:lat => location.lat, :long => location.lng) if options['location']
|
180
174
|
user = client.user(screen_name, :include_entities => false)
|
181
|
-
|
182
|
-
status = client.update("@#{user.screen_name} #{message}",
|
175
|
+
defaults.merge!(:in_reply_to_status_id => user.status.id) if user.status
|
176
|
+
status = client.update("@#{user.screen_name} #{message}", defaults)
|
183
177
|
say "Reply created by @#{@rcfile.default_profile[0]} to @#{user.screen_name} (#{time_ago_in_words(status.created_at)} ago)"
|
184
178
|
say
|
185
179
|
say "Run `#{$0} delete status` to delete."
|
@@ -206,25 +200,14 @@ module T
|
|
206
200
|
end
|
207
201
|
map %w(rt) => :retweet
|
208
202
|
|
209
|
-
desc "search QUERY", "Returns the 20 most recent Tweets that match a specified query."
|
210
|
-
method_option :number, :aliases => "-n", :type => :numeric, :default => 20
|
211
|
-
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false
|
212
|
-
def search(query)
|
213
|
-
hash = {:include_entities => false}
|
214
|
-
hash.merge!(:rpp => options['number']) if options['number']
|
215
|
-
timeline = client.search(query, hash)
|
216
|
-
timeline.reverse! if options['reverse']
|
217
|
-
run_pager
|
218
|
-
timeline.each do |status|
|
219
|
-
say "#{status.from_user.rjust(20)}: #{status.text} (#{time_ago_in_words(status.created_at)} ago)"
|
220
|
-
end
|
221
|
-
end
|
222
|
-
|
223
203
|
desc "sent_messages", "Returns the 20 most recent Direct Messages sent to you."
|
204
|
+
method_option :number, :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS
|
224
205
|
def sent_messages
|
206
|
+
defaults = {:include_entities => false}
|
207
|
+
defaults.merge!(:count => options['number']) if options['number']
|
225
208
|
run_pager
|
226
|
-
client.direct_messages_sent(
|
227
|
-
say "#{direct_message.recipient.screen_name.rjust(
|
209
|
+
client.direct_messages_sent(defaults).each do |direct_message|
|
210
|
+
say "#{direct_message.recipient.screen_name.rjust(MAX_SCREEN_NAME_SIZE)}: #{direct_message.text} (#{time_ago_in_words(direct_message.created_at)} ago)"
|
228
211
|
end
|
229
212
|
end
|
230
213
|
map %w(sms) => :sent_messages
|
@@ -245,9 +228,9 @@ module T
|
|
245
228
|
desc "status MESSAGE", "Post a Tweet."
|
246
229
|
method_option :location, :aliases => "-l", :type => :boolean, :default => true
|
247
230
|
def status(message)
|
248
|
-
|
249
|
-
|
250
|
-
status = client.update(message,
|
231
|
+
defaults = {:include_entities => false, :trim_user => true}
|
232
|
+
defaults.merge!(:lat => location.lat, :long => location.lng) if options['location']
|
233
|
+
status = client.update(message, defaults)
|
251
234
|
say "Tweet created by @#{@rcfile.default_profile[0]} (#{time_ago_in_words(status.created_at)} ago)"
|
252
235
|
say
|
253
236
|
say "Run `#{$0} delete status` to delete."
|
@@ -266,17 +249,22 @@ module T
|
|
266
249
|
end
|
267
250
|
end
|
268
251
|
|
269
|
-
desc "timeline", "Returns the
|
270
|
-
method_option :number, :aliases => "-n", :type => :numeric, :default =>
|
252
|
+
desc "timeline [SCREEN_NAME]", "Returns the #{DEFAULT_NUM_RESULTS} most recent Tweets posted by a user."
|
253
|
+
method_option :number, :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS
|
271
254
|
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false
|
272
|
-
def timeline
|
273
|
-
|
274
|
-
|
275
|
-
|
255
|
+
def timeline(screen_name=nil)
|
256
|
+
defaults = {:include_entities => false}
|
257
|
+
defaults.merge!(:count => options['number']) if options['number']
|
258
|
+
if screen_name
|
259
|
+
screen_name = screen_name.strip_at
|
260
|
+
timeline = client.user_timeline(screen_name, defaults)
|
261
|
+
else
|
262
|
+
timeline = client.home_timeline(defaults)
|
263
|
+
end
|
276
264
|
timeline.reverse! if options['reverse']
|
277
265
|
run_pager
|
278
266
|
timeline.each do |status|
|
279
|
-
say "#{status.user.screen_name.rjust(
|
267
|
+
say "#{status.user.screen_name.rjust(MAX_SCREEN_NAME_SIZE)}: #{status.text} (#{time_ago_in_words(status.created_at)} ago)"
|
280
268
|
end
|
281
269
|
end
|
282
270
|
map %w(tl) => :timeline
|
@@ -310,6 +298,10 @@ module T
|
|
310
298
|
require 't/cli/list'
|
311
299
|
subcommand 'list', CLI::List
|
312
300
|
|
301
|
+
desc "search SUBCOMMAND ...ARGS", "Search through Tweets."
|
302
|
+
require 't/cli/search'
|
303
|
+
subcommand 'search', CLI::Search
|
304
|
+
|
313
305
|
desc "set SUBCOMMAND ...ARGS", "Change various account settings."
|
314
306
|
require 't/cli/set'
|
315
307
|
subcommand 'set', CLI::Set
|
data/lib/t/cli/follow.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
require 'retryable'
|
1
2
|
require 't/core_ext/enumerable'
|
2
3
|
require 't/core_ext/string'
|
4
|
+
require 't/collectable'
|
3
5
|
require 't/rcfile'
|
4
6
|
require 'thor'
|
5
7
|
require 'twitter'
|
@@ -7,6 +9,8 @@ require 'twitter'
|
|
7
9
|
module T
|
8
10
|
class CLI
|
9
11
|
class Follow < Thor
|
12
|
+
include T::Collectable
|
13
|
+
|
10
14
|
DEFAULT_HOST = 'api.twitter.com'
|
11
15
|
DEFAULT_PROTOCOL = 'https'
|
12
16
|
|
@@ -17,12 +21,53 @@ module T
|
|
17
21
|
@rcfile = RCFile.instance
|
18
22
|
end
|
19
23
|
|
24
|
+
desc "followers", "Follow all followers."
|
25
|
+
def followers
|
26
|
+
follower_ids = collect_with_cursor do |cursor|
|
27
|
+
client.follower_ids(:cursor => cursor)
|
28
|
+
end
|
29
|
+
friend_ids = collect_with_cursor do |cursor|
|
30
|
+
client.friend_ids(:cursor => cursor)
|
31
|
+
end
|
32
|
+
follow_ids = (follower_ids - friend_ids)
|
33
|
+
number = follow_ids.length
|
34
|
+
return say "@#{@rcfile.default_profile[0]} is already following all followers." if number.zero?
|
35
|
+
return unless yes? "Are you sure you want to follow #{number} #{number == 1 ? 'user' : 'users'}?"
|
36
|
+
screen_names = follow_ids.threaded_map do |follow_id|
|
37
|
+
retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
38
|
+
client.follow(follow_id, :include_entities => false)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
say "@#{@rcfile.default_profile[0]} is now following #{number} more #{number == 1 ? 'user' : 'users'}."
|
42
|
+
say
|
43
|
+
say "Run `#{$0} unfollow all followers` to stop."
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "listed LIST_NAME", "Follow all members of a list."
|
47
|
+
def listed(list_name)
|
48
|
+
list_member_collection = collect_with_cursor do |cursor|
|
49
|
+
client.list_members(list_name, :cursor => cursor, :skip_status => true, :include_entities => false)
|
50
|
+
end
|
51
|
+
number = list_member_collection.length
|
52
|
+
return say "@#{@rcfile.default_profile[0]} is already following all list members." if number.zero?
|
53
|
+
return unless yes? "Are you sure you want to follow #{number} #{number == 1 ? 'user' : 'users'}?"
|
54
|
+
list_member_collection.threaded_map do |list_member|
|
55
|
+
retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
56
|
+
client.follow(list_member.id, :include_entities => false)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
say "@#{@rcfile.default_profile[0]} is now following #{number} more #{number == 1 ? 'user' : 'users'}."
|
60
|
+
say
|
61
|
+
say "Run `#{$0} unfollow all listed #{list_name}` to stop."
|
62
|
+
end
|
63
|
+
|
20
64
|
desc "users SCREEN_NAME [SCREEN_NAME...]", "Allows you to start following users."
|
21
65
|
def users(screen_name, *screen_names)
|
22
66
|
screen_names.unshift(screen_name)
|
23
67
|
screen_names.threaded_map do |screen_name|
|
24
|
-
|
25
|
-
|
68
|
+
retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
69
|
+
client.follow(screen_name, :include_entities => false)
|
70
|
+
end
|
26
71
|
end
|
27
72
|
number = screen_names.length
|
28
73
|
say "@#{@rcfile.default_profile[0]} is now following #{number} more #{number == 1 ? 'user' : 'users'}."
|
@@ -30,10 +75,6 @@ module T
|
|
30
75
|
say "Run `#{$0} unfollow users #{screen_names.join(' ')}` to stop."
|
31
76
|
end
|
32
77
|
|
33
|
-
desc "all SUBCOMMAND ...ARGS", "Follow all users."
|
34
|
-
require 't/cli/follow/all'
|
35
|
-
subcommand 'all', CLI::Follow::All
|
36
|
-
|
37
78
|
private
|
38
79
|
|
39
80
|
def base_url
|
data/lib/t/cli/list.rb
CHANGED
@@ -10,6 +10,8 @@ module T
|
|
10
10
|
|
11
11
|
DEFAULT_HOST = 'api.twitter.com'
|
12
12
|
DEFAULT_PROTOCOL = 'https'
|
13
|
+
DEFAULT_NUM_RESULTS = 20
|
14
|
+
MAX_SCREEN_NAME_SIZE = 20
|
13
15
|
|
14
16
|
check_unknown_options!
|
15
17
|
|
@@ -21,23 +23,23 @@ module T
|
|
21
23
|
desc "create LIST_NAME [DESCRIPTION]", "Create a new list."
|
22
24
|
method_option :private, :aliases => "-p", :type => :boolean
|
23
25
|
def create(list_name, description="")
|
24
|
-
|
25
|
-
|
26
|
-
client.list_create(list_name,
|
26
|
+
defaults = description.blank? ? {} : {:description => description}
|
27
|
+
defaults.merge!(:mode => 'private') if options['private']
|
28
|
+
client.list_create(list_name, defaults)
|
27
29
|
say "@#{@rcfile.default_profile[0]} created the list \"#{list_name}\"."
|
28
30
|
end
|
29
31
|
|
30
32
|
desc "timeline LIST_NAME", "Show tweet timeline for members of the specified list."
|
31
|
-
method_option :number, :aliases => "-n", :type => :numeric, :default =>
|
33
|
+
method_option :number, :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS
|
32
34
|
method_option :reverse, :aliases => "-r", :type => :boolean, :default => false
|
33
35
|
def timeline(list_name)
|
34
|
-
|
35
|
-
|
36
|
-
timeline = client.list_timeline(list_name,
|
36
|
+
defaults = {:include_entities => false}
|
37
|
+
defaults.merge!(:per_page => options['number']) if options['number']
|
38
|
+
timeline = client.list_timeline(list_name, defaults)
|
37
39
|
timeline.reverse! if options['reverse']
|
38
40
|
run_pager
|
39
41
|
timeline.each do |status|
|
40
|
-
say "#{status.user.screen_name.rjust(
|
42
|
+
say "#{status.user.screen_name.rjust(MAX_SCREEN_NAME_SIZE)}: #{status.text} (#{time_ago_in_words(status.created_at)} ago)"
|
41
43
|
end
|
42
44
|
end
|
43
45
|
map %w(tl) => :timeline
|
data/lib/t/cli/list/add.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
+
require 'active_support/core_ext/array/grouping'
|
1
2
|
require 't/core_ext/string'
|
3
|
+
require 't/collectable'
|
2
4
|
require 't/rcfile'
|
3
5
|
require 'thor'
|
4
6
|
require 'twitter'
|
@@ -7,8 +9,11 @@ module T
|
|
7
9
|
class CLI
|
8
10
|
class List
|
9
11
|
class Add < Thor
|
12
|
+
include T::Collectable
|
13
|
+
|
10
14
|
DEFAULT_HOST = 'api.twitter.com'
|
11
15
|
DEFAULT_PROTOCOL = 'https'
|
16
|
+
MAX_USERS_PER_LIST = 500
|
12
17
|
|
13
18
|
check_unknown_options!
|
14
19
|
|
@@ -17,6 +22,99 @@ module T
|
|
17
22
|
@rcfile = RCFile.instance
|
18
23
|
end
|
19
24
|
|
25
|
+
desc "friends LIST_NAME", "Add all friends to a list."
|
26
|
+
def friends(list_name)
|
27
|
+
list_member_ids = collect_with_cursor do |cursor|
|
28
|
+
client.list_members(list_name, :cursor => cursor, :skip_status => true, :include_entities => false)
|
29
|
+
end
|
30
|
+
existing_list_members = list_member_ids.length
|
31
|
+
if existing_list_members >= MAX_USERS_PER_LIST
|
32
|
+
return say "The list \"#{list_name}\" are already contains the maximum of #{MAX_USERS_PER_LIST} members."
|
33
|
+
end
|
34
|
+
friend_ids = collect_with_cursor do |cursor|
|
35
|
+
client.friend_ids(:cursor => cursor)
|
36
|
+
end
|
37
|
+
list_member_ids_to_add = (friend_ids - list_member_ids)
|
38
|
+
number = list_member_ids_to_add.length
|
39
|
+
if number.zero?
|
40
|
+
return say "All of @#{@rcfile.default_profile[0]}'s friends are already members of the list \"#{list_name}\"."
|
41
|
+
elsif existing_list_members + number > MAX_USERS_PER_LIST
|
42
|
+
return unless yes? "Lists can't have more than #{MAX_USERS_PER_LIST} members. Do you want to add up to #{MAX_USERS_PER_LIST} friends to the list \"#{list_name}\"?"
|
43
|
+
else
|
44
|
+
return unless yes? "Are you sure you want to add #{number} #{number == 1 ? 'friend' : 'friends'} to the list \"#{list_name}\"?"
|
45
|
+
end
|
46
|
+
max_members_to_add = MAX_USERS_PER_LIST - existing_list_members
|
47
|
+
list_member_ids_to_add[0...max_members_to_add].in_groups_of(100, false) do |user_id_group|
|
48
|
+
client.list_add_members(list_name, user_id_group)
|
49
|
+
end
|
50
|
+
number_added = [number, max_members_to_add].min
|
51
|
+
say "@#{@rcfile.default_profile[0]} added #{number_added} #{number_added == 1 ? 'friend' : 'friends'} to the list \"#{list_name}\"."
|
52
|
+
say
|
53
|
+
say "Run `#{$0} list remove all friends #{list_name}` to undo."
|
54
|
+
end
|
55
|
+
|
56
|
+
desc "followers LIST_NAME", "Add all followers to a list."
|
57
|
+
def followers(list_name)
|
58
|
+
list_member_ids = collect_with_cursor do |cursor|
|
59
|
+
client.list_members(list_name, :cursor => cursor, :skip_status => true, :include_entities => false)
|
60
|
+
end
|
61
|
+
existing_list_members = list_member_ids.length
|
62
|
+
if existing_list_members >= MAX_USERS_PER_LIST
|
63
|
+
return say "The list \"#{list_name}\" are already contains the maximum of #{MAX_USERS_PER_LIST} members."
|
64
|
+
end
|
65
|
+
follower_ids = collect_with_cursor do |cursor|
|
66
|
+
followers = client.follower_ids(:cursor => cursor)
|
67
|
+
end
|
68
|
+
list_member_ids_to_add = (follower_ids - list_member_ids)
|
69
|
+
number = list_member_ids_to_add.length
|
70
|
+
if number.zero?
|
71
|
+
return say "All of @#{@rcfile.default_profile[0]}'s followers are already members of the list \"#{list_name}\"."
|
72
|
+
elsif existing_list_members + number > MAX_USERS_PER_LIST
|
73
|
+
return unless yes? "Lists can't have more than #{MAX_USERS_PER_LIST} members. Do you want to add up to #{MAX_USERS_PER_LIST} followers to the list \"#{list_name}\"?"
|
74
|
+
else
|
75
|
+
return unless yes? "Are you sure you want to add #{number} #{number == 1 ? 'follower' : 'followers'} to the list \"#{list_name}\"?"
|
76
|
+
end
|
77
|
+
max_members_to_add = MAX_USERS_PER_LIST - existing_list_members
|
78
|
+
list_member_ids_to_add[0...max_members_to_add].in_groups_of(100, false) do |user_id_group|
|
79
|
+
client.list_add_members(list_name, user_id_group)
|
80
|
+
end
|
81
|
+
number_added = [number, max_members_to_add].min
|
82
|
+
say "@#{@rcfile.default_profile[0]} added #{number_added} #{number_added == 1 ? 'follower' : 'followers'} to the list \"#{list_name}\"."
|
83
|
+
say
|
84
|
+
say "Run `#{$0} list remove all followers #{list_name}` to undo."
|
85
|
+
end
|
86
|
+
|
87
|
+
desc "listed FROM_LIST_NAME TO_LIST_NAME", "Add all list memebers to a list."
|
88
|
+
def listed(from_list_name, to_list_name)
|
89
|
+
to_list_members = collect_with_cursor do |cursor|
|
90
|
+
client.list_members(to_list_name, :cursor => cursor, :skip_status => true, :include_entities => false)
|
91
|
+
end
|
92
|
+
existing_list_members = to_list_members.length
|
93
|
+
if existing_list_members >= MAX_USERS_PER_LIST
|
94
|
+
return say "The list \"#{to_list_name}\" are already contains the maximum of #{MAX_USERS_PER_LIST} members."
|
95
|
+
end
|
96
|
+
from_list_members = collect_with_cursor do |cursor|
|
97
|
+
client.list_members(from_list_name, :cursor => cursor, :skip_status => true, :include_entities => false)
|
98
|
+
end
|
99
|
+
list_member_ids_to_add = (from_list_members.collect(&:id) - to_list_members.collect(&:id))
|
100
|
+
number = list_member_ids_to_add.length
|
101
|
+
if number.zero?
|
102
|
+
return say "All of the members of the list \"#{from_list_name}\" are already members of the list \"#{to_list_name}\"."
|
103
|
+
elsif existing_list_members + number > MAX_USERS_PER_LIST
|
104
|
+
return unless yes? "Lists can't have more than #{MAX_USERS_PER_LIST} members. Do you want to add up to #{MAX_USERS_PER_LIST} members to the list \"#{to_list_name}\"?"
|
105
|
+
else
|
106
|
+
return unless yes? "Are you sure you want to add #{number} #{number == 1 ? 'member' : 'members'} to the list \"#{to_list_name}\"?"
|
107
|
+
end
|
108
|
+
max_members_to_add = MAX_USERS_PER_LIST - existing_list_members
|
109
|
+
list_member_ids_to_add[0...max_members_to_add].in_groups_of(100, false) do |user_id_group|
|
110
|
+
client.list_add_members(to_list_name, user_id_group)
|
111
|
+
end
|
112
|
+
number_added = [number, max_members_to_add].min
|
113
|
+
say "@#{@rcfile.default_profile[0]} added #{number_added} #{number_added == 1 ? 'member' : 'members'} to the list \"#{to_list_name}\"."
|
114
|
+
say
|
115
|
+
say "Run `#{$0} list remove all listed #{from_list_name} #{to_list_name}` to undo."
|
116
|
+
end
|
117
|
+
|
20
118
|
desc "users LIST_NAME SCREEN_NAME [SCREEN_NAME...]", "Add users to a list."
|
21
119
|
def users(list_name, screen_name, *screen_names)
|
22
120
|
screen_names.unshift(screen_name)
|
@@ -28,10 +126,6 @@ module T
|
|
28
126
|
say "Run `#{$0} list remove users #{list_name} #{screen_names.join(' ')}` to undo."
|
29
127
|
end
|
30
128
|
|
31
|
-
desc "all SUBCOMMAND ...ARGS", "Add all users to a list."
|
32
|
-
require 't/cli/list/add/all'
|
33
|
-
subcommand 'all', CLI::List::Add::All
|
34
|
-
|
35
129
|
private
|
36
130
|
|
37
131
|
def base_url
|