t 0.9.9 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +51 -20
- data/bin/t +2 -1
- data/lib/t.rb +0 -7
- data/lib/t/cli.rb +188 -241
- data/lib/t/collectable.rb +11 -3
- data/lib/t/core_ext/string.rb +1 -1
- data/lib/t/delete.rb +36 -30
- data/lib/t/list.rb +19 -82
- data/lib/t/printable.rb +43 -38
- data/lib/t/rcfile.rb +76 -70
- data/lib/t/search.rb +57 -44
- data/lib/t/set.rb +3 -3
- data/lib/t/stream.rb +9 -9
- data/lib/t/{format_helpers.rb → utils.rb} +40 -3
- data/lib/t/version.rb +3 -3
- data/spec/cli_spec.rb +918 -436
- data/spec/delete_spec.rb +4 -4
- data/spec/fixtures/sferik.json +46 -62
- data/spec/fixtures/status_no_attributes.json +4 -4
- data/spec/fixtures/status_no_country.json +4 -4
- data/spec/fixtures/status_no_full_name.json +4 -4
- data/spec/fixtures/status_no_locality.json +4 -4
- data/spec/fixtures/status_no_street_address.json +4 -4
- data/spec/fixtures/users.json +105 -75
- data/spec/fixtures/users_list.json +105 -75
- data/spec/helper.rb +0 -1
- data/spec/list_spec.rb +125 -49
- data/spec/rcfile_spec.rb +28 -27
- data/spec/search_spec.rb +272 -249
- data/spec/set_spec.rb +24 -24
- data/spec/{format_helpers_spec.rb → utils_spec.rb} +7 -7
- data/t.gemspec +3 -5
- metadata +12 -54
- data/lib/t/authorizable.rb +0 -38
- data/lib/t/core_ext/enumerable.rb +0 -19
- data/spec/t_spec.rb +0 -31
data/README.md
CHANGED
@@ -2,7 +2,10 @@
|
|
2
2
|
|
3
3
|
[icon]: https://github.com/sferik/t/raw/master/icon/t.png
|
4
4
|
|
5
|
-
# Twitter CLI
|
5
|
+
# Twitter CLI
|
6
|
+
[][travis]
|
7
|
+
[][gemnasium]
|
8
|
+
[][pledgie]
|
6
9
|
|
7
10
|
### A command-line power tool for Twitter.
|
8
11
|
|
@@ -15,28 +18,53 @@ offers vastly more commands and capabilities than are available via SMS.
|
|
15
18
|
[sms]: https://support.twitter.com/articles/14020-twitter-sms-command
|
16
19
|
|
17
20
|
## Installation
|
18
|
-
|
21
|
+
|
22
|
+
First, make sure you have Ruby installed.
|
23
|
+
|
24
|
+
**On a Mac**, open `/Applications/Utilities/Terminal.app` and type:
|
25
|
+
|
26
|
+
ruby -v
|
27
|
+
|
28
|
+
If the output looks something like this, you're in good shape:
|
29
|
+
|
30
|
+
ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin12.0.0]
|
31
|
+
|
32
|
+
If the output looks more like this, you need to [install Ruby][ruby]:
|
33
|
+
|
34
|
+
ruby: command not found
|
35
|
+
|
36
|
+
**On Windows**, you can install Ruby with [RubyInstaller][].
|
37
|
+
|
38
|
+
Once you've verified that Ruby is installed:
|
39
|
+
|
40
|
+
gem install t
|
41
|
+
|
42
|
+
[ruby]: http://www.ruby-lang.org/en/downloads/
|
43
|
+
[rubyinstaller]: http://rubyinstaller.org/
|
19
44
|
|
20
45
|
## Configuration
|
21
46
|
|
22
|
-
Twitter requires OAuth for most of its functionality, so you'll need
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
47
|
+
Twitter requires OAuth for most of its functionality, so you'll need a
|
48
|
+
registered Twitter application. If you've never registered a Twitter
|
49
|
+
application before, it's easy! Just sign-in using your Twitter account and the
|
50
|
+
fill out the short form at <http://dev.twitter.com/apps/new>. If you've
|
51
|
+
previously registered a Twitter application, it should be listed at
|
52
|
+
<http://dev.twitter.com/apps>. Once you've registered an application, make sure
|
53
|
+
to set your application's Access Level to "Read, Write and Access direct
|
54
|
+
messages", otherwise you'll receive an error that looks like this:
|
27
55
|
|
28
56
|
Read-only application cannot POST
|
29
57
|
|
30
|
-
|
31
|
-
|
58
|
+
Now, you're ready to authorize a Twitter account with your application. To
|
59
|
+
proceed, type the following command at the prompt and follow the instructions:
|
32
60
|
|
33
|
-
t authorize
|
61
|
+
t authorize
|
34
62
|
|
35
|
-
This command
|
36
|
-
|
37
|
-
you should now be authorized to use
|
38
|
-
accounts, simply repeat the last step,
|
39
|
-
user.
|
63
|
+
This command will direct you to a URL where you can sign-in to Twitter,
|
64
|
+
authorize the application, and then enter the returned PIN back into the
|
65
|
+
terminal. If you type the PIN correctly, you should now be authorized to use
|
66
|
+
`t` as that user. To authorize multiple accounts, simply repeat the last step,
|
67
|
+
signing into Twitter as a different user.
|
40
68
|
|
41
69
|
You can see a list of all the accounts you've authorized by typing the command:
|
42
70
|
|
@@ -108,14 +136,17 @@ example, send a user a direct message only if he already follows you:
|
|
108
136
|
t lists -l
|
109
137
|
|
110
138
|
### List all your friends, in long format, ordered by number of followers
|
111
|
-
t friends -
|
139
|
+
t friends -l --sort=followers
|
112
140
|
|
113
141
|
### List all your leaders (people you follow who don't follow you back)
|
114
|
-
t leaders -
|
142
|
+
t leaders -l --sort=followers
|
115
143
|
|
116
144
|
### Unfollow everyone you follow who doesn't follow you back
|
117
145
|
t leaders | xargs t unfollow
|
118
146
|
|
147
|
+
### Unfollow 10 people who haven't tweeted in the longest time
|
148
|
+
t followings -l --sort=tweeted | head -10 | awk '{print $1}' | xargs t unfollow
|
149
|
+
|
119
150
|
### Twitter roulette: randomly follow someone who follows you (who you don't already follow)
|
120
151
|
t groupies | shuf | head -1 | xargs t follow
|
121
152
|
|
@@ -154,12 +185,12 @@ example, send a user a direct message only if he already follows you:
|
|
154
185
|
|
155
186
|
## Features
|
156
187
|
* Deep search: Instead of using the Twitter Search API, [which only only goes
|
157
|
-
back 6-9 days][
|
188
|
+
back 6-9 days][search], `t search` fetches up to 3,200 tweets via the REST API
|
158
189
|
and then checks each one against a regular expression.
|
159
|
-
*
|
190
|
+
* Multi-threaded: Whenever possible, Twitter API requests are made in parallel,
|
160
191
|
resulting in faster performance for bulk operations.
|
161
192
|
* Designed for Unix: Output is designed to be piped to other Unix utilities,
|
162
|
-
like grep, cut, awk, bc, wc, and xargs for advanced text processing.
|
193
|
+
like grep, comm, cut, awk, bc, wc, and xargs for advanced text processing.
|
163
194
|
* Generate spreadsheets: Convert the output of any command to CSV format simply
|
164
195
|
by adding the `--csv` flag.
|
165
196
|
* 95% C0 Code Coverage: Well tested, with a 2.5:1 test-to-code ratio.
|
data/bin/t
CHANGED
@@ -18,7 +18,8 @@ rescue OAuth::Unauthorized
|
|
18
18
|
pute "Authorization failed"
|
19
19
|
exit 1
|
20
20
|
rescue Twitter::Error::Unauthorized => error
|
21
|
-
pute
|
21
|
+
pute error.message
|
22
|
+
pute "Run `#{$0} authorize` to authorize."
|
22
23
|
exit 1
|
23
24
|
rescue Twitter::Error => error
|
24
25
|
pute error.message
|
data/lib/t.rb
CHANGED
@@ -1,16 +1,9 @@
|
|
1
|
-
require 'active_support/string_inquirer'
|
2
1
|
require 't/cli'
|
3
2
|
require 'time'
|
4
3
|
|
5
4
|
module T
|
6
5
|
class << self
|
7
6
|
|
8
|
-
attr_reader :env
|
9
|
-
|
10
|
-
def env=(environment)
|
11
|
-
@env = ActiveSupport::StringInquirer.new(environment)
|
12
|
-
end
|
13
|
-
|
14
7
|
# Convert time to local time by applying the `utc_offset` setting.
|
15
8
|
def local_time(time)
|
16
9
|
utc_offset ? (time.utc + utc_offset) : time.localtime
|
data/lib/t/cli.rb
CHANGED
@@ -1,29 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'oauth'
|
1
3
|
require 'thor'
|
2
4
|
require 'twitter'
|
5
|
+
require 't/collectable'
|
6
|
+
require 't/delete'
|
7
|
+
require 't/list'
|
8
|
+
require 't/printable'
|
9
|
+
require 't/rcfile'
|
10
|
+
require 't/requestable'
|
11
|
+
require 't/search'
|
12
|
+
require 't/set'
|
13
|
+
require 't/stream'
|
14
|
+
require 't/utils'
|
3
15
|
|
4
16
|
module T
|
5
|
-
autoload :Authorizable, 't/authorizable'
|
6
|
-
autoload :Collectable, 't/collectable'
|
7
|
-
autoload :Delete, 't/delete'
|
8
|
-
autoload :FormatHelpers, 't/format_helpers'
|
9
|
-
autoload :List, 't/list'
|
10
|
-
autoload :Printable, 't/printable'
|
11
|
-
autoload :RCFile, 't/rcfile'
|
12
|
-
autoload :Requestable, 't/requestable'
|
13
|
-
autoload :Search, 't/search'
|
14
|
-
autoload :Set, 't/set'
|
15
|
-
autoload :Stream, 't/stream'
|
16
|
-
autoload :Version, 't/version'
|
17
17
|
class CLI < Thor
|
18
|
-
include T::Authorizable
|
19
18
|
include T::Collectable
|
20
19
|
include T::Printable
|
21
20
|
include T::Requestable
|
22
|
-
include T::
|
21
|
+
include T::Utils
|
23
22
|
|
23
|
+
DEFAULT_HOST = 'api.twitter.com'
|
24
|
+
DEFAULT_PROTOCOL = 'https'
|
24
25
|
DEFAULT_NUM_RESULTS = 20
|
25
|
-
MAX_SCREEN_NAME_SIZE = 20
|
26
|
-
MAX_USERS_PER_REQUEST = 100
|
27
26
|
DIRECT_MESSAGE_HEADINGS = ["ID", "Posted at", "Screen name", "Text"]
|
28
27
|
TREND_HEADINGS = ["WOEID", "Parent ID", "Type", "Name", "Country"]
|
29
28
|
|
@@ -32,11 +31,11 @@ module T
|
|
32
31
|
option "host", :aliases => "-H", :type => :string, :default => DEFAULT_HOST, :desc => "Twitter API server"
|
33
32
|
option "no-color", :aliases => "-N", :type => :boolean, :desc => "Disable colorization in output"
|
34
33
|
option "no-ssl", :aliases => "-U", :type => :boolean, :default => false, :desc => "Disable SSL"
|
35
|
-
option "profile", :aliases => "-P", :type => :string, :default => File.join(File.expand_path("~"), RCFile::FILE_NAME), :desc => "Path to RC file", :banner => "FILE"
|
34
|
+
option "profile", :aliases => "-P", :type => :string, :default => File.join(File.expand_path("~"), T::RCFile::FILE_NAME), :desc => "Path to RC file", :banner => "FILE"
|
36
35
|
|
37
36
|
def initialize(*)
|
37
|
+
@rcfile = T::RCFile.instance
|
38
38
|
super
|
39
|
-
@rcfile = RCFile.instance
|
40
39
|
end
|
41
40
|
|
42
41
|
desc "accounts", "List accounts"
|
@@ -51,63 +50,75 @@ module T
|
|
51
50
|
end
|
52
51
|
|
53
52
|
desc "authorize", "Allows an application to request user authorization"
|
54
|
-
method_option "consumer-key", :aliases => "-c", :required => true, :desc => "This can be found at https://dev.twitter.com/apps", :banner => "KEY"
|
55
|
-
method_option "consumer-secret", :aliases => "-s", :required => true, :desc => "This can be found at https://dev.twitter.com/apps", :banner => "SECRET"
|
56
53
|
method_option "display-url", :aliases => "-d", :type => :boolean, :default => false, :desc => "Display the authorization URL instead of attempting to open it."
|
57
|
-
method_option "prompt", :aliases => "-p", :type => :boolean, :default => true
|
58
54
|
def authorize
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
say "
|
63
|
-
say "
|
64
|
-
say "
|
65
|
-
say " 2.
|
66
|
-
say "
|
67
|
-
say "
|
55
|
+
@rcfile.path = options['profile'] if options['profile']
|
56
|
+
if @rcfile.empty?
|
57
|
+
say "Welcome! Before you can use t, you'll first need to register an"
|
58
|
+
say "application with Twitter. Just follow the steps below:"
|
59
|
+
say " 1. Sign in to the Twitter Developer site and click"
|
60
|
+
say " \"Create a new application\"."
|
61
|
+
say " 2. Complete the required fields and submit the form."
|
62
|
+
say " Note: Your application must have a unique name."
|
63
|
+
say " We recommend: \"<your handle>/t\"."
|
64
|
+
say " 3. Go to the Settings tab of your application, and change the"
|
65
|
+
say " Access setting to \"Read, Write and Access direct messages\"."
|
66
|
+
say " 4. Go to the Details tab to view the consumer key and secret,"
|
67
|
+
say " which you'll need to copy and paste below when prompted."
|
68
68
|
say
|
69
|
-
ask "Press [Enter] to open the Twitter
|
69
|
+
ask "Press [Enter] to open the Twitter Developer site."
|
70
|
+
say
|
71
|
+
else
|
72
|
+
say "It looks like you've already registered an application with Twitter."
|
73
|
+
say "To authorize a new account, just follow the steps below:"
|
74
|
+
say " 1. Sign in to the Twitter Developer site."
|
75
|
+
say " 2. Select the application for which you'd like to authorize an account."
|
76
|
+
say " 3. Copy and paste the consumer key and secret below when prompted."
|
77
|
+
say
|
78
|
+
ask "Press [Enter] to open the Twitter Developer site."
|
70
79
|
say
|
71
80
|
end
|
72
81
|
require 'launchy'
|
82
|
+
Launchy.open("https://dev.twitter.com/apps", :dry_run => options['display-url'])
|
83
|
+
key = ask "Enter your consumer key:"
|
84
|
+
secret = ask "Enter your consumer secret:"
|
85
|
+
consumer = OAuth::Consumer.new(key, secret, :site => base_url)
|
86
|
+
request_token = consumer.get_request_token
|
87
|
+
url = generate_authorize_url(consumer, request_token)
|
88
|
+
say
|
89
|
+
say "In a moment, you will be directed to the Twitter app authorization page."
|
90
|
+
say "Perform the following steps to complete the authorization process:"
|
91
|
+
say " 1. Sign in to Twitter."
|
92
|
+
say " 2. Press \"Authorize app\"."
|
93
|
+
say " 3. Copy and paste the supplied PIN below when prompted."
|
94
|
+
say
|
95
|
+
ask "Press [Enter] to open the Twitter app authorization page."
|
96
|
+
say
|
73
97
|
Launchy.open(url, :dry_run => options['display-url'])
|
74
|
-
pin = ask "
|
98
|
+
pin = ask "Enter the supplied PIN:"
|
75
99
|
access_token = request_token.get_access_token(:oauth_verifier => pin.chomp)
|
76
100
|
oauth_response = access_token.get('/1/account/verify_credentials.json')
|
77
101
|
screen_name = oauth_response.body.match(/"screen_name"\s*:\s*"(.*?)"/).captures.first
|
78
|
-
@rcfile.path = options['profile'] if options['profile']
|
79
102
|
@rcfile[screen_name] = {
|
80
|
-
|
103
|
+
key => {
|
81
104
|
'username' => screen_name,
|
82
|
-
'consumer_key' =>
|
83
|
-
'consumer_secret' =>
|
105
|
+
'consumer_key' => key,
|
106
|
+
'consumer_secret' => secret,
|
84
107
|
'token' => access_token.token,
|
85
108
|
'secret' => access_token.secret,
|
86
109
|
}
|
87
110
|
}
|
88
|
-
@rcfile.active_profile = {'username' => screen_name, 'consumer_key' =>
|
111
|
+
@rcfile.active_profile = {'username' => screen_name, 'consumer_key' => key}
|
89
112
|
say "Authorization successful."
|
90
113
|
end
|
91
114
|
|
92
115
|
desc "block USER [USER...]", "Block users."
|
93
116
|
method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify input as Twitter user IDs instead of screen names."
|
94
117
|
def block(user, *users)
|
95
|
-
users.unshift(user)
|
96
|
-
|
97
|
-
if options['id']
|
98
|
-
users.map!(&:to_i)
|
99
|
-
else
|
100
|
-
users.map!(&:strip_ats)
|
101
|
-
end
|
102
|
-
require 't/core_ext/enumerable'
|
103
|
-
require 'retryable'
|
104
|
-
users = users.threaded_map do |user|
|
105
|
-
retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
106
|
-
client.block(user)
|
107
|
-
end
|
118
|
+
users, number = fetch_users(users.unshift(user), options) do |users|
|
119
|
+
client.block(users)
|
108
120
|
end
|
109
|
-
number
|
110
|
-
say "@#{@rcfile.active_profile[0]} blocked #{number} #{number == 1 ? 'user' : 'users'}."
|
121
|
+
say "@#{@rcfile.active_profile[0]} blocked #{pluralize(number, 'user')}."
|
111
122
|
say
|
112
123
|
say "Run `#{File.basename($0)} delete block #{users.map{|user| "@#{user.screen_name}"}.join(' ')}` to unblock."
|
113
124
|
end
|
@@ -139,13 +150,13 @@ module T
|
|
139
150
|
print_table_with_headings(array, DIRECT_MESSAGE_HEADINGS, format)
|
140
151
|
else
|
141
152
|
direct_messages.each do |direct_message|
|
142
|
-
|
153
|
+
print_message(direct_message.sender.screen_name, direct_message.text)
|
143
154
|
end
|
144
155
|
end
|
145
156
|
end
|
146
157
|
map %w(directmessages dms) => :direct_messages
|
147
158
|
|
148
|
-
desc "direct_messages_sent", "Returns the #{DEFAULT_NUM_RESULTS} most recent Direct Messages sent
|
159
|
+
desc "direct_messages_sent", "Returns the #{DEFAULT_NUM_RESULTS} most recent Direct Messages you've sent."
|
149
160
|
method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
150
161
|
method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
151
162
|
method_option "number", :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS, :desc => "Limit the number of results."
|
@@ -172,7 +183,7 @@ module T
|
|
172
183
|
print_table_with_headings(array, DIRECT_MESSAGE_HEADINGS, format)
|
173
184
|
else
|
174
185
|
direct_messages.each do |direct_message|
|
175
|
-
|
186
|
+
print_message(direct_message.recipient.screen_name, direct_message.text)
|
176
187
|
end
|
177
188
|
end
|
178
189
|
end
|
@@ -180,15 +191,10 @@ module T
|
|
180
191
|
|
181
192
|
desc "groupies [USER]", "Returns the list of people who follow you but you don't follow back."
|
182
193
|
method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
183
|
-
method_option "favorites", :aliases => "-v", :type => :boolean, :default => false, :desc => "Sort by number of favorites."
|
184
|
-
method_option "followers", :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
|
185
|
-
method_option "friends", :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
|
186
194
|
method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
187
|
-
method_option "listed", :aliases => "-d", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
|
188
195
|
method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
189
|
-
method_option "posted", :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
|
190
196
|
method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
191
|
-
method_option "
|
197
|
+
method_option "sort", :aliases => "-s", :type => :string, :enum => %w(favorites followers friends listed screen_name since tweets tweeted), :default => "screen_name", :desc => "Specify the order of the results.", :banner => "ORDER"
|
192
198
|
method_option "unsorted", :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
|
193
199
|
def groupies(user=nil)
|
194
200
|
if user
|
@@ -199,21 +205,21 @@ module T
|
|
199
205
|
user.strip_ats
|
200
206
|
end
|
201
207
|
end
|
202
|
-
follower_ids =
|
203
|
-
|
208
|
+
follower_ids = Thread.new do
|
209
|
+
collect_with_cursor do |cursor|
|
210
|
+
client.follower_ids(user, :cursor => cursor)
|
211
|
+
end
|
204
212
|
end
|
205
|
-
following_ids =
|
206
|
-
|
213
|
+
following_ids = Thread.new do
|
214
|
+
collect_with_cursor do |cursor|
|
215
|
+
client.friend_ids(user, :cursor => cursor)
|
216
|
+
end
|
207
217
|
end
|
208
|
-
disciple_ids = (follower_ids - following_ids)
|
209
|
-
require 'active_support/core_ext/array/grouping'
|
210
|
-
require 't/core_ext/enumerable'
|
218
|
+
disciple_ids = (follower_ids.value - following_ids.value)
|
211
219
|
require 'retryable'
|
212
|
-
users =
|
213
|
-
|
214
|
-
|
215
|
-
end
|
216
|
-
end.flatten
|
220
|
+
users = retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
221
|
+
client.users(disciple_ids)
|
222
|
+
end
|
217
223
|
print_users(users)
|
218
224
|
end
|
219
225
|
map %w(disciples) => :groupies
|
@@ -228,25 +234,14 @@ module T
|
|
228
234
|
user.strip_ats
|
229
235
|
end
|
230
236
|
direct_message = client.direct_message_create(user, message)
|
231
|
-
say "Direct Message sent from @#{@rcfile.active_profile[0]} to @#{direct_message.recipient.screen_name}
|
237
|
+
say "Direct Message sent from @#{@rcfile.active_profile[0]} to @#{direct_message.recipient.screen_name}."
|
232
238
|
end
|
233
239
|
map %w(d m) => :dm
|
234
240
|
|
235
241
|
desc "does_contain [USER/]LIST USER", "Find out whether a list contains a user."
|
236
242
|
method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
237
243
|
def does_contain(list, user=nil)
|
238
|
-
owner, list = list
|
239
|
-
if list.nil?
|
240
|
-
list = owner
|
241
|
-
owner = @rcfile.active_profile[0]
|
242
|
-
else
|
243
|
-
require 't/core_ext/string'
|
244
|
-
owner = if options['id']
|
245
|
-
client.user(owner.to_i).screen_name
|
246
|
-
else
|
247
|
-
owner.strip_ats
|
248
|
-
end
|
249
|
-
end
|
244
|
+
owner, list = extract_owner(list, options)
|
250
245
|
if user.nil?
|
251
246
|
user = @rcfile.active_profile[0]
|
252
247
|
else
|
@@ -258,9 +253,9 @@ module T
|
|
258
253
|
end
|
259
254
|
end
|
260
255
|
if client.list_member?(owner, list, user)
|
261
|
-
say "Yes,
|
256
|
+
say "Yes, #{list} contains @#{user}."
|
262
257
|
else
|
263
|
-
say "No,
|
258
|
+
say "No, #{list} does not contain @#{user}."
|
264
259
|
exit 1
|
265
260
|
end
|
266
261
|
end
|
@@ -297,15 +292,12 @@ module T
|
|
297
292
|
def favorite(status_id, *status_ids)
|
298
293
|
status_ids.unshift(status_id)
|
299
294
|
status_ids.map!(&:to_i)
|
300
|
-
require 't/core_ext/enumerable'
|
301
295
|
require 'retryable'
|
302
|
-
favorites =
|
303
|
-
|
304
|
-
client.favorite(status_id)
|
305
|
-
end
|
296
|
+
favorites = retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
297
|
+
client.favorite(status_ids)
|
306
298
|
end
|
307
299
|
number = favorites.length
|
308
|
-
say "@#{@rcfile.active_profile[0]} favorited #{number
|
300
|
+
say "@#{@rcfile.active_profile[0]} favorited #{pluralize(number, 'tweet')}."
|
309
301
|
say
|
310
302
|
say "Run `#{File.basename($0)} delete favorite #{status_ids.join(' ')}` to unfavorite."
|
311
303
|
end
|
@@ -337,37 +329,20 @@ module T
|
|
337
329
|
desc "follow USER [USER...]", "Allows you to start following users."
|
338
330
|
method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify input as Twitter user IDs instead of screen names."
|
339
331
|
def follow(user, *users)
|
340
|
-
users.unshift(user)
|
341
|
-
|
342
|
-
if options['id']
|
343
|
-
users.map!(&:to_i)
|
344
|
-
else
|
345
|
-
users.map!(&:strip_ats)
|
346
|
-
end
|
347
|
-
require 't/core_ext/enumerable'
|
348
|
-
require 'retryable'
|
349
|
-
users = users.threaded_map do |user|
|
350
|
-
retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
351
|
-
client.follow(user)
|
352
|
-
end
|
332
|
+
users, number = fetch_users(users.unshift(user), options) do |users|
|
333
|
+
client.follow(users)
|
353
334
|
end
|
354
|
-
number
|
355
|
-
say "@#{@rcfile.active_profile[0]} is now following #{number} more #{number == 1 ? 'user' : 'users'}."
|
335
|
+
say "@#{@rcfile.active_profile[0]} is now following #{pluralize(number, 'more user')}."
|
356
336
|
say
|
357
337
|
say "Run `#{File.basename($0)} unfollow #{users.map{|user| "@#{user.screen_name}"}.join(' ')}` to stop."
|
358
338
|
end
|
359
339
|
|
360
340
|
desc "followings [USER]", "Returns a list of the people you follow on Twitter."
|
361
341
|
method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
362
|
-
method_option "favorites", :aliases => "-v", :type => :boolean, :default => false, :desc => "Sort by number of favorites."
|
363
|
-
method_option "followers", :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
|
364
|
-
method_option "friends", :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
|
365
342
|
method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
366
|
-
method_option "listed", :aliases => "-d", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
|
367
343
|
method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
368
|
-
method_option "posted", :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
|
369
344
|
method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
370
|
-
method_option "
|
345
|
+
method_option "sort", :aliases => "-s", :type => :string, :enum => %w(favorites followers friends listed screen_name since tweets tweeted), :default => "screen_name", :desc => "Specify the order of the results.", :banner => "ORDER"
|
371
346
|
method_option "unsorted", :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
|
372
347
|
def followings(user=nil)
|
373
348
|
if user
|
@@ -381,28 +356,19 @@ module T
|
|
381
356
|
following_ids = collect_with_cursor do |cursor|
|
382
357
|
client.friend_ids(user, :cursor => cursor)
|
383
358
|
end
|
384
|
-
require 'active_support/core_ext/array/grouping'
|
385
|
-
require 't/core_ext/enumerable'
|
386
359
|
require 'retryable'
|
387
|
-
users =
|
388
|
-
|
389
|
-
|
390
|
-
end
|
391
|
-
end.flatten
|
360
|
+
users = retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
361
|
+
client.users(following_ids)
|
362
|
+
end
|
392
363
|
print_users(users)
|
393
364
|
end
|
394
365
|
|
395
366
|
desc "followers [USER]", "Returns a list of the people who follow you on Twitter."
|
396
367
|
method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
397
|
-
method_option "favorites", :aliases => "-v", :type => :boolean, :default => false, :desc => "Sort by number of favorites."
|
398
|
-
method_option "followers", :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
|
399
|
-
method_option "friends", :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
|
400
368
|
method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
401
|
-
method_option "listed", :aliases => "-d", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
|
402
369
|
method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
403
|
-
method_option "posted", :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
|
404
370
|
method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
405
|
-
method_option "
|
371
|
+
method_option "sort", :aliases => "-s", :type => :string, :enum => %w(favorites followers friends listed screen_name since tweets tweeted), :default => "screen_name", :desc => "Specify the order of the results.", :banner => "ORDER"
|
406
372
|
method_option "unsorted", :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
|
407
373
|
def followers(user=nil)
|
408
374
|
if user
|
@@ -416,28 +382,19 @@ module T
|
|
416
382
|
follower_ids = collect_with_cursor do |cursor|
|
417
383
|
client.follower_ids(user, :cursor => cursor)
|
418
384
|
end
|
419
|
-
require 'active_support/core_ext/array/grouping'
|
420
|
-
require 't/core_ext/enumerable'
|
421
385
|
require 'retryable'
|
422
|
-
users =
|
423
|
-
|
424
|
-
|
425
|
-
end
|
426
|
-
end.flatten
|
386
|
+
users = retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
387
|
+
client.users(follower_ids)
|
388
|
+
end
|
427
389
|
print_users(users)
|
428
390
|
end
|
429
391
|
|
430
392
|
desc "friends [USER]", "Returns the list of people who you follow and follow you back."
|
431
393
|
method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
432
|
-
method_option "favorites", :aliases => "-v", :type => :boolean, :default => false, :desc => "Sort by number of favorites."
|
433
|
-
method_option "followers", :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
|
434
|
-
method_option "friends", :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
|
435
394
|
method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
436
|
-
method_option "listed", :aliases => "-d", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
|
437
395
|
method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
438
|
-
method_option "posted", :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
|
439
396
|
method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
440
|
-
method_option "
|
397
|
+
method_option "sort", :aliases => "-s", :type => :string, :enum => %w(favorites followers friends listed screen_name since tweets tweeted), :default => "screen_name", :desc => "Specify the order of the results.", :banner => "ORDER"
|
441
398
|
method_option "unsorted", :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
|
442
399
|
def friends(user=nil)
|
443
400
|
if user
|
@@ -448,35 +405,30 @@ module T
|
|
448
405
|
user.strip_ats
|
449
406
|
end
|
450
407
|
end
|
451
|
-
following_ids =
|
452
|
-
|
408
|
+
following_ids = Thread.new do
|
409
|
+
collect_with_cursor do |cursor|
|
410
|
+
client.friend_ids(user, :cursor => cursor)
|
411
|
+
end
|
453
412
|
end
|
454
|
-
follower_ids =
|
455
|
-
|
413
|
+
follower_ids = Thread.new do
|
414
|
+
collect_with_cursor do |cursor|
|
415
|
+
client.follower_ids(user, :cursor => cursor)
|
416
|
+
end
|
456
417
|
end
|
457
|
-
friend_ids = (following_ids & follower_ids)
|
458
|
-
require 'active_support/core_ext/array/grouping'
|
459
|
-
require 't/core_ext/enumerable'
|
418
|
+
friend_ids = (following_ids.value & follower_ids.value)
|
460
419
|
require 'retryable'
|
461
|
-
users =
|
462
|
-
|
463
|
-
|
464
|
-
end
|
465
|
-
end.flatten
|
420
|
+
users = retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
421
|
+
client.users(friend_ids)
|
422
|
+
end
|
466
423
|
print_users(users)
|
467
424
|
end
|
468
425
|
|
469
426
|
desc "leaders [USER]", "Returns the list of people who you follow but don't follow you back."
|
470
427
|
method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
471
|
-
method_option "favorites", :aliases => "-v", :type => :boolean, :default => false, :desc => "Sort by number of favorites."
|
472
|
-
method_option "followers", :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
|
473
|
-
method_option "friends", :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
|
474
428
|
method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
475
|
-
method_option "listed", :aliases => "-d", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
|
476
429
|
method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
477
|
-
method_option "posted", :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
|
478
430
|
method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
479
|
-
method_option "
|
431
|
+
method_option "sort", :aliases => "-s", :type => :string, :enum => %w(favorites followers friends listed screen_name since tweets tweeted), :default => "screen_name", :desc => "Specify the order of the results.", :banner => "ORDER"
|
480
432
|
method_option "unsorted", :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
|
481
433
|
def leaders(user=nil)
|
482
434
|
if user
|
@@ -487,21 +439,21 @@ module T
|
|
487
439
|
user.strip_ats
|
488
440
|
end
|
489
441
|
end
|
490
|
-
following_ids =
|
491
|
-
|
442
|
+
following_ids = Thread.new do
|
443
|
+
collect_with_cursor do |cursor|
|
444
|
+
client.friend_ids(user, :cursor => cursor)
|
445
|
+
end
|
492
446
|
end
|
493
|
-
follower_ids =
|
494
|
-
|
447
|
+
follower_ids = Thread.new do
|
448
|
+
collect_with_cursor do |cursor|
|
449
|
+
client.follower_ids(user, :cursor => cursor)
|
450
|
+
end
|
495
451
|
end
|
496
|
-
leader_ids = (following_ids - follower_ids)
|
497
|
-
require 'active_support/core_ext/array/grouping'
|
498
|
-
require 't/core_ext/enumerable'
|
452
|
+
leader_ids = (following_ids.value - follower_ids.value)
|
499
453
|
require 'retryable'
|
500
|
-
users =
|
501
|
-
|
502
|
-
|
503
|
-
end
|
504
|
-
end.flatten
|
454
|
+
users = retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
455
|
+
client.users(leader_ids)
|
456
|
+
end
|
505
457
|
print_users(users)
|
506
458
|
end
|
507
459
|
|
@@ -509,11 +461,8 @@ module T
|
|
509
461
|
method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
510
462
|
method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
511
463
|
method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
512
|
-
method_option "members", :aliases => "-m", :type => :boolean, :default => false, :desc => "Sort by number of members."
|
513
|
-
method_option "mode", :aliases => "-o", :type => :boolean, :default => false, :desc => "Sort by mode."
|
514
|
-
method_option "posted", :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter list was posted."
|
515
464
|
method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
516
|
-
method_option "
|
465
|
+
method_option "sort", :aliases => "-s", :type => :string, :enum => %w(members mode posted slug subscribers), :default => "slug", :desc => "Specify the order of the results.", :banner => "ORDER"
|
517
466
|
method_option "unsorted", :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
|
518
467
|
def lists(user=nil)
|
519
468
|
if user
|
@@ -593,11 +542,7 @@ module T
|
|
593
542
|
status = client.status(status_id.to_i, :include_my_retweet => false)
|
594
543
|
users = Array(status.from_user)
|
595
544
|
if options['all']
|
596
|
-
|
597
|
-
major, minor, patch = RUBY_VERSION.split('.')
|
598
|
-
$KCODE='u' if major.to_i == 1 && minor.to_i < 9
|
599
|
-
require 'twitter-text'
|
600
|
-
users += Twitter::Extractor.extract_mentioned_screen_names(status.full_text)
|
545
|
+
users += extract_mentioned_screen_names(status.full_text)
|
601
546
|
users.uniq!
|
602
547
|
end
|
603
548
|
require 't/core_ext/string'
|
@@ -605,7 +550,7 @@ module T
|
|
605
550
|
opts = {:in_reply_to_status_id => status.id, :trim_user => true}
|
606
551
|
opts.merge!(:lat => location.lat, :long => location.lng) if options['location']
|
607
552
|
reply = client.update("#{users.join(' ')} #{message}", opts)
|
608
|
-
say "Reply
|
553
|
+
say "Reply posted by @#{@rcfile.active_profile[0]} to #{users.join(' ')}."
|
609
554
|
say
|
610
555
|
say "Run `#{File.basename($0)} delete status #{reply.id}` to delete."
|
611
556
|
end
|
@@ -613,22 +558,10 @@ module T
|
|
613
558
|
desc "report_spam USER [USER...]", "Report users for spam."
|
614
559
|
method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify input as Twitter user IDs instead of screen names."
|
615
560
|
def report_spam(user, *users)
|
616
|
-
users.unshift(user)
|
617
|
-
|
618
|
-
if options['id']
|
619
|
-
users.map!(&:to_i)
|
620
|
-
else
|
621
|
-
users.map!(&:strip_ats)
|
622
|
-
end
|
623
|
-
require 't/core_ext/enumerable'
|
624
|
-
require 'retryable'
|
625
|
-
users = users.threaded_map do |user|
|
626
|
-
retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
627
|
-
client.report_spam(user)
|
628
|
-
end
|
561
|
+
users, number = fetch_users(users.unshift(user), options) do |users|
|
562
|
+
client.report_spam(users)
|
629
563
|
end
|
630
|
-
number
|
631
|
-
say "@#{@rcfile.active_profile[0]} reported #{number} #{number == 1 ? 'user' : 'users'}."
|
564
|
+
say "@#{@rcfile.active_profile[0]} reported #{pluralize(number, 'user')}."
|
632
565
|
end
|
633
566
|
map %w(report reportspam spam) => :report_spam
|
634
567
|
|
@@ -636,15 +569,12 @@ module T
|
|
636
569
|
def retweet(status_id, *status_ids)
|
637
570
|
status_ids.unshift(status_id)
|
638
571
|
status_ids.map!(&:to_i)
|
639
|
-
require 't/core_ext/enumerable'
|
640
572
|
require 'retryable'
|
641
|
-
retweets =
|
642
|
-
|
643
|
-
client.retweet(status_id, :trim_user => true)
|
644
|
-
end
|
573
|
+
retweets = retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
574
|
+
client.retweet(status_ids, :trim_user => true)
|
645
575
|
end
|
646
576
|
number = retweets.length
|
647
|
-
say "@#{@rcfile.active_profile[0]} retweeted #{number
|
577
|
+
say "@#{@rcfile.active_profile[0]} retweeted #{pluralize(number, 'tweet')}."
|
648
578
|
say
|
649
579
|
say "Run `#{File.basename($0)} delete status #{retweets.map(&:id).join(' ')}` to undo."
|
650
580
|
end
|
@@ -657,17 +587,21 @@ module T
|
|
657
587
|
method_option "number", :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS, :desc => "Limit the number of results."
|
658
588
|
method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
659
589
|
def retweets(user=nil)
|
660
|
-
|
590
|
+
count = options['number'] || DEFAULT_NUM_RESULTS
|
591
|
+
statuses = if user
|
661
592
|
require 't/core_ext/string'
|
662
593
|
user = if options['id']
|
663
594
|
user.to_i
|
664
595
|
else
|
665
596
|
user.strip_ats
|
666
597
|
end
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
598
|
+
collect_with_count(count) do |opts|
|
599
|
+
client.retweeted_by_user(user, opts)
|
600
|
+
end
|
601
|
+
else
|
602
|
+
collect_with_count(count) do |opts|
|
603
|
+
client.retweeted_by_me(opts)
|
604
|
+
end
|
671
605
|
end
|
672
606
|
print_statuses(statuses)
|
673
607
|
end
|
@@ -683,12 +617,12 @@ module T
|
|
683
617
|
def status(status_id)
|
684
618
|
status = client.status(status_id.to_i, :include_my_retweet => false)
|
685
619
|
location = if status.place
|
686
|
-
if status.place.name && status.place.attributes && status.place.attributes[
|
687
|
-
[status.place.name, status.place.attributes[
|
688
|
-
elsif status.place.name && status.place.attributes && status.place.attributes[
|
689
|
-
[status.place.name, status.place.attributes[
|
690
|
-
elsif status.place.full_name && status.place.attributes && status.place.attributes[
|
691
|
-
[status.place.full_name, status.place.attributes[
|
620
|
+
if status.place.name && status.place.attributes && status.place.attributes[:street_address] && status.place.attributes[:locality] && status.place.attributes[:region] && status.place.country
|
621
|
+
[status.place.name, status.place.attributes[:street_address], status.place.attributes[:locality], status.place.attributes[:region], status.place.country].join(", ")
|
622
|
+
elsif status.place.name && status.place.attributes && status.place.attributes[:locality] && status.place.attributes[:region] && status.place.country
|
623
|
+
[status.place.name, status.place.attributes[:locality], status.place.attributes[:region], status.place.country].join(", ")
|
624
|
+
elsif status.place.full_name && status.place.attributes && status.place.attributes[:region] && status.place.country
|
625
|
+
[status.place.full_name, status.place.attributes[:region], status.place.country].join(", ")
|
692
626
|
elsif status.place.full_name && status.place.country
|
693
627
|
[status.place.full_name, status.place.country].join(", ")
|
694
628
|
elsif status.place.full_name
|
@@ -721,16 +655,11 @@ module T
|
|
721
655
|
|
722
656
|
desc "suggest [USER]", "Returns a listing of Twitter users' accounts you might enjoy following."
|
723
657
|
method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
724
|
-
method_option "favorites", :aliases => "-v", :type => :boolean, :default => false, :desc => "Sort by number of favorites."
|
725
|
-
method_option "followers", :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
|
726
|
-
method_option "friends", :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
|
727
658
|
method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify user via ID instead of screen name."
|
728
|
-
method_option "listed", :aliases => "-d", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
|
729
659
|
method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
730
660
|
method_option "number", :aliases => "-n", :type => :numeric, :default => DEFAULT_NUM_RESULTS, :desc => "Limit the number of results."
|
731
|
-
method_option "posted", :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
|
732
661
|
method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
733
|
-
method_option "
|
662
|
+
method_option "sort", :aliases => "-s", :type => :string, :enum => %w(favorites followers friends listed screen_name since tweets tweeted), :default => "screen_name", :desc => "Specify the order of the results.", :banner => "ORDER"
|
734
663
|
method_option "unsorted", :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
|
735
664
|
def suggest(user=nil)
|
736
665
|
if user
|
@@ -813,22 +742,10 @@ module T
|
|
813
742
|
desc "unfollow USER [USER...]", "Allows you to stop following users."
|
814
743
|
method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify input as Twitter user IDs instead of screen names."
|
815
744
|
def unfollow(user, *users)
|
816
|
-
users.unshift(user)
|
817
|
-
|
818
|
-
if options['id']
|
819
|
-
users.map!(&:to_i)
|
820
|
-
else
|
821
|
-
users.map!(&:strip_ats)
|
745
|
+
users, number = fetch_users(users.unshift(user), options) do |users|
|
746
|
+
client.unfollow(users)
|
822
747
|
end
|
823
|
-
|
824
|
-
require 'retryable'
|
825
|
-
users = users.threaded_map do |user|
|
826
|
-
retryable(:tries => 3, :on => Twitter::Error::ServerError, :sleep => 0) do
|
827
|
-
client.unfollow(user)
|
828
|
-
end
|
829
|
-
end
|
830
|
-
number = users.length
|
831
|
-
say "@#{@rcfile.active_profile[0]} is no longer following #{number} #{number == 1 ? 'user' : 'users'}."
|
748
|
+
say "@#{@rcfile.active_profile[0]} is no longer following #{pluralize(number, 'user')}."
|
832
749
|
say
|
833
750
|
say "Run `#{File.basename($0)} follow #{users.map{|user| "@#{user.screen_name}"}.join(' ')}` to follow again."
|
834
751
|
end
|
@@ -839,7 +756,7 @@ module T
|
|
839
756
|
opts = {:trim_user => true}
|
840
757
|
opts.merge!(:lat => location.lat, :long => location.lng) if options['location']
|
841
758
|
status = client.update(message, opts)
|
842
|
-
say "Tweet
|
759
|
+
say "Tweet posted by @#{@rcfile.active_profile[0]}."
|
843
760
|
say
|
844
761
|
say "Run `#{File.basename($0)} delete status #{status.id}` to delete."
|
845
762
|
end
|
@@ -847,15 +764,10 @@ module T
|
|
847
764
|
|
848
765
|
desc "users USER [USER...]", "Returns a list of users you specify."
|
849
766
|
method_option "csv", :aliases => "-c", :type => :boolean, :default => false, :desc => "Output in CSV format."
|
850
|
-
method_option "favorites", :aliases => "-v", :type => :boolean, :default => false, :desc => "Sort by number of favorites."
|
851
|
-
method_option "followers", :aliases => "-f", :type => :boolean, :default => false, :desc => "Sort by number of followers."
|
852
|
-
method_option "friends", :aliases => "-e", :type => :boolean, :default => false, :desc => "Sort by number of friends."
|
853
767
|
method_option "id", :aliases => "-i", :type => "boolean", :default => false, :desc => "Specify input as Twitter user IDs instead of screen names."
|
854
|
-
method_option "listed", :aliases => "-d", :type => :boolean, :default => false, :desc => "Sort by number of list memberships."
|
855
768
|
method_option "long", :aliases => "-l", :type => :boolean, :default => false, :desc => "Output in long format."
|
856
|
-
method_option "posted", :aliases => "-p", :type => :boolean, :default => false, :desc => "Sort by the time when Twitter account was posted."
|
857
769
|
method_option "reverse", :aliases => "-r", :type => :boolean, :default => false, :desc => "Reverse the order of the sort."
|
858
|
-
method_option "
|
770
|
+
method_option "sort", :aliases => "-s", :type => :string, :enum => %w(favorites followers friends listed screen_name since tweets tweeted), :default => "screen_name", :desc => "Specify the order of the results.", :banner => "ORDER"
|
859
771
|
method_option "unsorted", :aliases => "-u", :type => :boolean, :default => false, :desc => "Output is not sorted."
|
860
772
|
def users(user, *users)
|
861
773
|
users.unshift(user)
|
@@ -872,6 +784,7 @@ module T
|
|
872
784
|
|
873
785
|
desc "version", "Show version."
|
874
786
|
def version
|
787
|
+
require 't/version'
|
875
788
|
say T::Version
|
876
789
|
end
|
877
790
|
map %w(-v --version) => :version
|
@@ -930,6 +843,40 @@ module T
|
|
930
843
|
|
931
844
|
private
|
932
845
|
|
846
|
+
def extract_mentioned_screen_names(text)
|
847
|
+
valid_mention_preceding_chars = /(?:[^a-zA-Z0-9_!#\$%&*@@]|^|RT:?)/o
|
848
|
+
at_signs = /[@@]/
|
849
|
+
valid_mentions = /
|
850
|
+
(#{valid_mention_preceding_chars}) # $1: Preceeding character
|
851
|
+
(#{at_signs}) # $2: At mark
|
852
|
+
([a-zA-Z0-9_]{1,20}) # $3: Screen name
|
853
|
+
/ox
|
854
|
+
|
855
|
+
return [] if text !~ at_signs
|
856
|
+
|
857
|
+
text.to_s.scan(valid_mentions).map do |before, at, screen_name|
|
858
|
+
screen_name
|
859
|
+
end
|
860
|
+
end
|
861
|
+
|
862
|
+
def base_url
|
863
|
+
"#{protocol}://#{host}"
|
864
|
+
end
|
865
|
+
|
866
|
+
def generate_authorize_url(consumer, request_token)
|
867
|
+
request = consumer.create_signed_request(:get, consumer.authorize_path, request_token, pin_auth_parameters)
|
868
|
+
params = request['Authorization'].sub(/^OAuth\s+/, '').split(/,\s+/).map do |param|
|
869
|
+
key, value = param.split('=')
|
870
|
+
value =~ /"(.*?)"/
|
871
|
+
"#{key}=#{CGI::escape($1)}"
|
872
|
+
end.join('&')
|
873
|
+
"#{base_url}#{request.path}?#{params}"
|
874
|
+
end
|
875
|
+
|
876
|
+
def pin_auth_parameters
|
877
|
+
{:oauth_callback => 'oob'}
|
878
|
+
end
|
879
|
+
|
933
880
|
def location
|
934
881
|
return @location if @location
|
935
882
|
require 'geokit'
|