t 0.2.1 → 0.3.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 +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
|

|
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
|