t 0.9.9 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://secure.travis-ci.org/sferik/t.png?branch=master)][travis]
|
7
|
+
[![Dependency Status](https://gemnasium.com/sferik/t.png?travis)][gemnasium]
|
8
|
+
[![Click here to make a donation to T](http://www.pledgie.com/campaigns/17330.png)][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'
|