twurl 0.9.4 → 0.9.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/CONTRIBUTING.md +25 -0
- data/INSTALL.md +36 -0
- data/README.md +73 -41
- data/lib/twurl.rb +1 -0
- data/lib/twurl/app_only_oauth_client.rb +78 -0
- data/lib/twurl/app_only_token_information_controller.rb +18 -0
- data/lib/twurl/cli.rb +17 -1
- data/lib/twurl/oauth_client.rb +62 -5
- data/lib/twurl/rcfile.rb +19 -0
- data/lib/twurl/request_controller.rb +16 -3
- data/lib/twurl/version.rb +1 -1
- data/twurl.gemspec +2 -3
- metadata +11 -7
- data/INSTALL +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 833ceb46ed18d449d282d4ef9ae820c31887ffe617a9d717f0319ba72071dc47
|
4
|
+
data.tar.gz: 59e6d7960c134eb4815108da8da7f7118c14e61cef3e6b322bb78ba2fff2b313
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d8bf943ed2cc90fb627edf90d4c0c991df8076e0c055c7b1be2ae8fd7b36f7f04f0ad2a273fb8d09489ae7beb08a60995119646ea366641b321e2e16dfb8f6e5
|
7
|
+
data.tar.gz: 6f22c2a87d1a6c0b1db8b696d0a3a79984b532497de618a2d17dc6f1677d91242818a9933e5e6255adb37cf19f049c8e4e72758b59011920b10ff6ab6941a3b1
|
data/.gitignore
CHANGED
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# How to Contribute
|
2
|
+
|
3
|
+
We'd love to get patches from you!
|
4
|
+
|
5
|
+
## Getting Started
|
6
|
+
|
7
|
+
We follow the [GitHub Flow Workflow](https://guides.github.com/introduction/flow/)
|
8
|
+
|
9
|
+
1. Fork the project
|
10
|
+
2. Check out the `master` branch
|
11
|
+
3. Create a feature branch
|
12
|
+
4. Write code and tests for your change
|
13
|
+
5. From your branch, make a pull request against `twitter/twurl/master`
|
14
|
+
6. Work with repo maintainers to get your change reviewed
|
15
|
+
7. Wait for your change to be pulled into `twitter/twurl/master`
|
16
|
+
8. Delete your feature branch
|
17
|
+
|
18
|
+
## License
|
19
|
+
|
20
|
+
By contributing your code, you agree to license your contribution under the
|
21
|
+
terms of the MIT License: https://github.com/twitter/twurl/blob/master/LICENSE
|
22
|
+
|
23
|
+
## Code of Conduct
|
24
|
+
|
25
|
+
Read our [Code of Conduct](CODE_OF_CONDUCT.md) for the project.
|
data/INSTALL.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Install
|
2
|
+
|
3
|
+
## Install with RubyGems (recommended)
|
4
|
+
|
5
|
+
```sh
|
6
|
+
# installing the latest release
|
7
|
+
$ gem install twurl
|
8
|
+
```
|
9
|
+
|
10
|
+
```sh
|
11
|
+
# verify installation
|
12
|
+
$ twurl -v
|
13
|
+
0.9.4
|
14
|
+
```
|
15
|
+
|
16
|
+
## Install from source
|
17
|
+
|
18
|
+
In case if you haven't installed `bundler` you need to install it first:
|
19
|
+
|
20
|
+
```sh
|
21
|
+
$ gem install bundler
|
22
|
+
```
|
23
|
+
|
24
|
+
```sh
|
25
|
+
$ git clone https://github.com/twitter/twurl
|
26
|
+
$ cd twurl
|
27
|
+
$ bundle install
|
28
|
+
```
|
29
|
+
|
30
|
+
If you don't want to install Twurl globally on your system, use `--path` [option](https://bundler.io/v2.0/bundle_install.html):
|
31
|
+
|
32
|
+
```
|
33
|
+
$ bundle install --path path/to/directory
|
34
|
+
$ bundle exec twurl -v
|
35
|
+
0.9.4
|
36
|
+
```
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
Twurl
|
2
|
-
|
1
|
+
# Twurl
|
2
|
+
|
3
|
+
[![MIT License](https://img.shields.io/apm/l/atomic-design-ui.svg?)](https://github.com/twitter/twurl/blob/master/LICENSE)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/twurl.svg)](https://badge.fury.io/rb/twurl)
|
3
5
|
|
4
6
|
Twurl is like curl, but tailored specifically for the Twitter API.
|
5
7
|
It knows how to grant an access token to a client application for
|
@@ -10,34 +12,29 @@ as defining aliases for common requests, as well as support for
|
|
10
12
|
multiple access tokens to easily switch between different client
|
11
13
|
applications and Twitter accounts.
|
12
14
|
|
15
|
+
## Installing Twurl
|
13
16
|
|
14
|
-
|
15
|
-
----------------
|
16
|
-
|
17
|
-
Twurl can be installed using ruby gems:
|
17
|
+
Twurl can be installed using RubyGems:
|
18
18
|
|
19
|
-
```
|
19
|
+
```sh
|
20
20
|
gem install twurl
|
21
21
|
```
|
22
22
|
|
23
|
+
## Getting Started
|
23
24
|
|
24
|
-
|
25
|
-
---------------
|
25
|
+
If you haven't already, the first thing to do is apply for a developer account to access Twitter APIs:
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
```
|
27
|
+
```text
|
30
28
|
https://developer.twitter.com/en/apply-for-access
|
31
29
|
```
|
32
|
-
|
33
|
-
After you have that access you can create a Twitter app and generate a consumer key and secret.
|
34
|
-
|
30
|
+
|
31
|
+
After you have that access you can create a Twitter app and generate a consumer key and secret.
|
35
32
|
|
36
33
|
When you have your consumer key and its secret you authorize
|
37
|
-
your Twitter account to make API requests with
|
34
|
+
your Twitter account to make API requests with that consumer key
|
38
35
|
and secret.
|
39
36
|
|
40
|
-
```
|
37
|
+
```sh
|
41
38
|
twurl authorize --consumer-key key \
|
42
39
|
--consumer-secret secret
|
43
40
|
```
|
@@ -47,14 +44,12 @@ Authenticate to Twitter, and then enter the returned PIN back into
|
|
47
44
|
the terminal. Assuming all that works well, you will be authorized
|
48
45
|
to make requests with the API. Twurl will tell you as much.
|
49
46
|
|
50
|
-
|
51
|
-
Making Requests
|
52
|
-
---------------
|
47
|
+
## Making Requests
|
53
48
|
|
54
49
|
The simplest request just requires that you specify the path you
|
55
50
|
want to request.
|
56
51
|
|
57
|
-
```
|
52
|
+
```sh
|
58
53
|
twurl /1.1/statuses/home_timeline.json
|
59
54
|
```
|
60
55
|
|
@@ -63,47 +58,80 @@ Similar to curl, a GET request is performed by default.
|
|
63
58
|
You can implicitly perform a POST request by passing the -d option,
|
64
59
|
which specifies POST parameters.
|
65
60
|
|
66
|
-
```
|
61
|
+
```sh
|
67
62
|
twurl -d 'status=Testing twurl' /1.1/statuses/update.json
|
68
63
|
```
|
69
64
|
|
70
65
|
You can explicitly specify what request method to perform with
|
71
66
|
the -X (or --request-method) option.
|
72
67
|
|
73
|
-
```
|
68
|
+
```sh
|
74
69
|
twurl -X POST /1.1/statuses/destroy/1234567890.json
|
75
70
|
```
|
76
71
|
|
72
|
+
## Using Bearer Tokens (Application-only authentication)
|
77
73
|
|
78
|
-
|
79
|
-
----------------
|
74
|
+
You can generate a bearer token using `--bearer` option in combination with the `authorize` subcommand.
|
80
75
|
|
76
|
+
```sh
|
77
|
+
twurl authorize --bearer --consumer-key key \
|
78
|
+
--consumer-secret secret
|
81
79
|
```
|
80
|
+
|
81
|
+
And then, you can make a request using a generated bearer token using `--bearer` request option.
|
82
|
+
|
83
|
+
```sh
|
84
|
+
twurl --bearer '/1.1/search/tweets.json?q=hello'
|
85
|
+
```
|
86
|
+
|
87
|
+
To list your generated bearer tokens, you can use the `bearer_tokens` subcommand.
|
88
|
+
|
89
|
+
```sh
|
90
|
+
twurl bearer_tokens
|
91
|
+
```
|
92
|
+
|
93
|
+
This will print a pair of consumer_key and its associated bearer token. Note, tokens are omitted from this output.
|
94
|
+
|
95
|
+
## Accessing Different Hosts
|
96
|
+
|
97
|
+
You can access different hosts for other Twitter APIs using the -H flag.
|
98
|
+
|
99
|
+
```sh
|
100
|
+
twurl -H "ads-api.twitter.com" "/5/accounts"
|
101
|
+
```
|
102
|
+
|
103
|
+
## Uploading Media
|
104
|
+
|
105
|
+
To upload binary files, you can format the call as a form post. Below, the binary is "/path/to/media.jpg" and the form field is "media":
|
106
|
+
|
107
|
+
```sh
|
108
|
+
twurl -H "upload.twitter.com" -X POST "/1.1/media/upload.json" --file "/path/to/media.jpg" --file-field "media"
|
109
|
+
```
|
110
|
+
|
111
|
+
## Creating aliases
|
112
|
+
|
113
|
+
```sh
|
82
114
|
twurl alias h /1.1/statuses/home_timeline.json
|
83
115
|
```
|
84
116
|
|
85
117
|
You can then use "h" in place of the full path.
|
86
118
|
|
87
|
-
```
|
119
|
+
```sh
|
88
120
|
twurl h
|
89
121
|
```
|
90
122
|
|
91
|
-
Paths that require additional options such as request parameters for example can
|
92
|
-
be used with aliases the same as with full explicit paths, just as you might
|
93
|
-
expect.
|
123
|
+
Paths that require additional options (such as request parameters, for example) can be used with aliases the same as with full explicit paths, just as you might expect.
|
94
124
|
|
95
|
-
```
|
125
|
+
```sh
|
96
126
|
twurl alias tweet /1.1/statuses/update.json
|
97
127
|
twurl tweet -d "status=Aliases in twurl are convenient"
|
98
128
|
```
|
99
129
|
|
130
|
+
## Changing your default profile
|
100
131
|
|
101
|
-
|
102
|
-
-----------------------------
|
103
|
-
|
104
|
-
The first time you authorize a client application to make requests on behalf of your account, twurl stores your access token information in its .twurlrc file. Subsequent requests will use this profile as the default profile. You can use the 'accounts' subcommand to see what client applications have been authorized for what user names:
|
132
|
+
The first time you authorize a client application to make requests on behalf of your account, twurl stores your access token information in its `~/.twurlrc` file. Subsequent requests will use this profile as the default profile. You can use the `accounts` subcommand to see what client applications have been authorized for what user names:
|
105
133
|
|
106
|
-
```
|
134
|
+
```sh
|
107
135
|
twurl accounts
|
108
136
|
noradio
|
109
137
|
HQsAGcBm5MQT4n6j7qVJw
|
@@ -112,9 +140,9 @@ twurl accounts
|
|
112
140
|
guT9RsJbNQgVe6AwoY9BA
|
113
141
|
```
|
114
142
|
|
115
|
-
Notice that one of those consumer keys is marked as the default. To change the default use the
|
143
|
+
Notice that one of those consumer keys is marked as the default. To change the default use the `set` subcommand, passing then either just the username, if it's unambiguous, or the username and consumer key pair if it isn't unambiguous:
|
116
144
|
|
117
|
-
```
|
145
|
+
```sh
|
118
146
|
twurl set default testiverse
|
119
147
|
twurl accounts
|
120
148
|
noradio
|
@@ -124,7 +152,7 @@ twurl accounts
|
|
124
152
|
guT9RsJbNQgVe6AwoY9BA (default)
|
125
153
|
```
|
126
154
|
|
127
|
-
```
|
155
|
+
```sh
|
128
156
|
twurl set default noradio HQsAGcBm5MQT4n6j7qVJw
|
129
157
|
twurl accounts
|
130
158
|
noradio
|
@@ -134,10 +162,14 @@ twurl accounts
|
|
134
162
|
guT9RsJbNQgVe6AwoY9BA
|
135
163
|
```
|
136
164
|
|
137
|
-
|
138
|
-
|
165
|
+
### Profiles and Bearer Tokens
|
166
|
+
|
167
|
+
While changing the default profile allows you to select which access token (OAuth1.0a) to use, bearer tokens don't link to any user profiles as the Application-only authentication doesn't require user context. That is, you can make an application-only request regardless of your default profile if you specify the `-c` (--consumer-key) option once you generate a bearer token with this consumer key. By default, twurl reads the current profile's consumer key and its associated bearer token from `~/.twurlrc` file.
|
168
|
+
|
169
|
+
## Contributors
|
170
|
+
|
171
|
+
Marcel Molina / @noradio
|
139
172
|
|
140
|
-
Marcel Molina <marcel@twitter.com> / @noradio
|
141
173
|
Erik Michaels-Ober / @sferik
|
142
174
|
|
143
175
|
and there are many [more](https://github.com/twitter/twurl/graphs/contributors)!
|
data/lib/twurl.rb
CHANGED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require_relative 'oauth_client'
|
3
|
+
|
4
|
+
module Twurl
|
5
|
+
class AppOnlyOAuthClient < Twurl::OAuthClient
|
6
|
+
|
7
|
+
AUTHORIZATION_FAILED_MESSAGE = "Authorization failed. Check that your consumer key and secret are correct."
|
8
|
+
|
9
|
+
attr_reader :consumer_key, :consumer_secret, :bearer_token
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
@consumer_key = options['consumer_key']
|
13
|
+
@consumer_secret = options['consumer_secret']
|
14
|
+
@bearer_token = options['bearer_token']
|
15
|
+
end
|
16
|
+
|
17
|
+
def save
|
18
|
+
self.class.rcfile.bearer_token(consumer_key, bearer_token)
|
19
|
+
end
|
20
|
+
|
21
|
+
def exchange_credentials_for_access_token
|
22
|
+
response = fetch_oauth2_token
|
23
|
+
if response.nil? || response[:access_token].nil?
|
24
|
+
raise Exception, AUTHORIZATION_FAILED_MESSAGE
|
25
|
+
end
|
26
|
+
@bearer_token = response[:access_token]
|
27
|
+
end
|
28
|
+
|
29
|
+
def perform_request_from_options(options, &block)
|
30
|
+
request = build_request_from_options(options)
|
31
|
+
request['user-agent'] = user_agent
|
32
|
+
request['authorization'] = "Bearer #{bearer_token}"
|
33
|
+
|
34
|
+
http_client.request(request, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def needs_to_authorize?
|
38
|
+
bearer_token.nil?
|
39
|
+
end
|
40
|
+
|
41
|
+
def request_data
|
42
|
+
{'grant_type' => 'client_credentials'}
|
43
|
+
end
|
44
|
+
|
45
|
+
def http_client
|
46
|
+
uri = URI.parse(Twurl.options.base_url)
|
47
|
+
http = if Twurl.options.proxy
|
48
|
+
proxy_uri = URI.parse(Twurl.options.proxy)
|
49
|
+
Net::HTTP.new(uri.host, uri.port, proxy_uri.host, proxy_uri.port)
|
50
|
+
else
|
51
|
+
Net::HTTP.new(uri.host, uri.port)
|
52
|
+
end
|
53
|
+
set_http_client_options(http)
|
54
|
+
end
|
55
|
+
|
56
|
+
def set_http_client_options(http)
|
57
|
+
http.set_debug_output(Twurl.options.debug_output_io) if Twurl.options.trace
|
58
|
+
http.read_timeout = http.open_timeout = Twurl.options.timeout || 60
|
59
|
+
http.open_timeout = Twurl.options.connection_timeout if Twurl.options.connection_timeout
|
60
|
+
# Only override if Net::HTTP support max_retries (since Ruby >= 2.5)
|
61
|
+
http.max_retries = 0 if http.respond_to?(:max_retries=)
|
62
|
+
if Twurl.options.ssl?
|
63
|
+
http.use_ssl = true
|
64
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
65
|
+
end
|
66
|
+
http
|
67
|
+
end
|
68
|
+
|
69
|
+
def fetch_oauth2_token
|
70
|
+
request = Net::HTTP::Post.new('/oauth2/token')
|
71
|
+
request.body = URI.encode_www_form(request_data)
|
72
|
+
request['user-agent'] = user_agent
|
73
|
+
request['authorization'] = "Basic #{Base64.strict_encode64("#{consumer_key}:#{consumer_secret}")}"
|
74
|
+
response = http_client.request(request).body
|
75
|
+
JSON.parse(response,:symbolize_names => true)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Twurl
|
2
|
+
class AppOnlyTokenInformationController < AbstractCommandController
|
3
|
+
NO_ISSUED_TOKENS_MESSAGE = "No issued application-only (Bearer) tokens"
|
4
|
+
|
5
|
+
def dispatch
|
6
|
+
rcfile = OAuthClient.rcfile
|
7
|
+
if rcfile.empty? || rcfile.bearer_tokens.nil?
|
8
|
+
CLI.puts NO_ISSUED_TOKENS_MESSAGE
|
9
|
+
else
|
10
|
+
tokens = rcfile.bearer_tokens
|
11
|
+
CLI.puts "[consumer_key: bearer_token]"
|
12
|
+
tokens.each_key do |consumer_key|
|
13
|
+
CLI.puts "#{consumer_key}: (omitted)"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/twurl/cli.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Twurl
|
2
2
|
class CLI
|
3
|
-
SUPPORTED_COMMANDS = %w(authorize accounts alias set)
|
3
|
+
SUPPORTED_COMMANDS = %w(authorize accounts bearer_tokens alias set)
|
4
4
|
DEFAULT_COMMAND = 'request'
|
5
5
|
PATH_PATTERN = /^\/\w+/
|
6
6
|
PROTOCOL_PATTERN = /^\w+:\/\//
|
@@ -28,6 +28,8 @@ module Twurl
|
|
28
28
|
AuthorizationController
|
29
29
|
when 'accounts'
|
30
30
|
AccountInformationController
|
31
|
+
when 'bearer_tokens'
|
32
|
+
AppOnlyTokenInformationController
|
31
33
|
when 'alias'
|
32
34
|
AliasesController
|
33
35
|
when 'set'
|
@@ -87,8 +89,10 @@ Supported Commands: #{SUPPORTED_COMMANDS.sort.join(', ')}
|
|
87
89
|
file
|
88
90
|
filefield
|
89
91
|
base64
|
92
|
+
json_format
|
90
93
|
timeout
|
91
94
|
connection_timeout
|
95
|
+
app_only
|
92
96
|
end
|
93
97
|
end
|
94
98
|
|
@@ -327,6 +331,12 @@ Supported Commands: #{SUPPORTED_COMMANDS.sort.join(', ')}
|
|
327
331
|
end
|
328
332
|
end
|
329
333
|
|
334
|
+
def json_format
|
335
|
+
on('-j', '--json-pretty', 'Format response body to JSON pretty style') do |json_format|
|
336
|
+
options.json_format = true
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
330
340
|
def timeout
|
331
341
|
on('--timeout [sec]', Integer, 'Number of seconds to wait for the request to be read (default: 60)') do |timeout|
|
332
342
|
options.timeout = timeout
|
@@ -338,6 +348,12 @@ Supported Commands: #{SUPPORTED_COMMANDS.sort.join(', ')}
|
|
338
348
|
options.connection_timeout = connection_timeout
|
339
349
|
end
|
340
350
|
end
|
351
|
+
|
352
|
+
def app_only
|
353
|
+
on('--bearer', "Use application-only authentication (Bearer Token)") do |app_only|
|
354
|
+
options.app_only = true
|
355
|
+
end
|
356
|
+
end
|
341
357
|
end
|
342
358
|
end
|
343
359
|
|
data/lib/twurl/oauth_client.rb
CHANGED
@@ -13,13 +13,23 @@ module Twurl
|
|
13
13
|
load_client_for_username_and_consumer_key(options.username, options.consumer_key)
|
14
14
|
elsif options.username
|
15
15
|
load_client_for_username(options.username)
|
16
|
+
elsif options.command == 'authorize' && options.app_only
|
17
|
+
load_client_for_app_only_auth(options, options.consumer_key)
|
16
18
|
elsif options.command == 'authorize'
|
17
19
|
load_new_client_from_options(options)
|
20
|
+
elsif options.command == 'request' && has_oauth_options?(options)
|
21
|
+
load_new_client_from_oauth_options(options)
|
22
|
+
elsif options.command == 'request' && options.app_only && options.consumer_key
|
23
|
+
load_client_for_non_profile_app_only_auth(options)
|
18
24
|
else
|
19
|
-
load_default_client
|
25
|
+
load_default_client(options)
|
20
26
|
end
|
21
27
|
end
|
22
28
|
|
29
|
+
def has_oauth_options?(options)
|
30
|
+
(options.consumer_key && options.consumer_secret && options.access_token && options.token_secret) ? true : false
|
31
|
+
end
|
32
|
+
|
23
33
|
def load_client_for_username_and_consumer_key(username, consumer_key)
|
24
34
|
user_profiles = rcfile[username]
|
25
35
|
if user_profiles && attributes = user_profiles[consumer_key]
|
@@ -45,9 +55,52 @@ module Twurl
|
|
45
55
|
new(options.oauth_client_options.merge('password' => options.password))
|
46
56
|
end
|
47
57
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
58
|
+
def load_new_client_from_oauth_options(options)
|
59
|
+
new(options.oauth_client_options.merge(
|
60
|
+
'token' => options.access_token,
|
61
|
+
'secret' => options.token_secret
|
62
|
+
)
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
def load_client_for_app_only_auth(options, consumer_key)
|
67
|
+
if options.command == 'authorize'
|
68
|
+
AppOnlyOAuthClient.new(options)
|
69
|
+
else
|
70
|
+
AppOnlyOAuthClient.new(
|
71
|
+
options.oauth_client_options.merge(
|
72
|
+
'bearer_token' => rcfile.bearer_tokens.to_hash[consumer_key]
|
73
|
+
)
|
74
|
+
)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def load_client_for_non_profile_app_only_auth(options)
|
79
|
+
AppOnlyOAuthClient.new(
|
80
|
+
options.oauth_client_options.merge(
|
81
|
+
'bearer_token' => rcfile.bearer_tokens.to_hash[options.consumer_key]
|
82
|
+
)
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
def load_default_client(options)
|
87
|
+
return if options.command == 'bearer_tokens'
|
88
|
+
|
89
|
+
exception_message = "You must authorize first."
|
90
|
+
app_only_exception_message = "To use --bearer option, you need to authorize (OAuth1.0a) and create at least one user profile (~/.twurlrc):\n\n" \
|
91
|
+
"twurl authorize -c key -s secret\n" \
|
92
|
+
"\nor, you can specify issued token's consumer_key directly:\n" \
|
93
|
+
"(to see your issued tokens: 'twurl bearer_tokens')\n\n" \
|
94
|
+
"twurl --bearer -c key '/path/to/api'"
|
95
|
+
|
96
|
+
raise Exception, "#{options.app_only ? app_only_exception_message : exception_message}" unless rcfile.default_profile
|
97
|
+
if options.app_only
|
98
|
+
raise Exception, "No available bearer token found for consumer_key:#{rcfile.default_profile_consumer_key}" \
|
99
|
+
unless rcfile.has_bearer_token_for_consumer_key?(rcfile.default_profile_consumer_key)
|
100
|
+
load_client_for_app_only_auth(options, rcfile.default_profile_consumer_key)
|
101
|
+
else
|
102
|
+
load_client_for_username_and_consumer_key(*rcfile.default_profile)
|
103
|
+
end
|
51
104
|
end
|
52
105
|
end
|
53
106
|
|
@@ -74,7 +127,7 @@ module Twurl
|
|
74
127
|
:copy => Net::HTTP::Copy
|
75
128
|
}
|
76
129
|
|
77
|
-
def
|
130
|
+
def build_request_from_options(options, &block)
|
78
131
|
request_class = METHODS.fetch(options.request_method.to_sym)
|
79
132
|
request = request_class.new(options.path, options.headers)
|
80
133
|
|
@@ -122,7 +175,11 @@ module Twurl
|
|
122
175
|
end.join("&")
|
123
176
|
end
|
124
177
|
end
|
178
|
+
request
|
179
|
+
end
|
125
180
|
|
181
|
+
def perform_request_from_options(options, &block)
|
182
|
+
request = build_request_from_options(options)
|
126
183
|
request.oauth!(consumer.http, consumer, access_token)
|
127
184
|
request['user-agent'] = user_agent
|
128
185
|
consumer.http.request(request, &block)
|
data/lib/twurl/rcfile.rb
CHANGED
@@ -52,6 +52,11 @@ module Twurl
|
|
52
52
|
configuration['default_profile']
|
53
53
|
end
|
54
54
|
|
55
|
+
def default_profile_consumer_key
|
56
|
+
username, consumer_key = configuration['default_profile']
|
57
|
+
consumer_key
|
58
|
+
end
|
59
|
+
|
55
60
|
def default_profile=(profile)
|
56
61
|
configuration['default_profile'] = [profile.username, profile.consumer_key]
|
57
62
|
end
|
@@ -70,6 +75,16 @@ module Twurl
|
|
70
75
|
data['aliases']
|
71
76
|
end
|
72
77
|
|
78
|
+
def bearer_token(consumer_key, bearer_token)
|
79
|
+
data['bearer_tokens'] ||= {}
|
80
|
+
data['bearer_tokens'][consumer_key] = bearer_token
|
81
|
+
save
|
82
|
+
end
|
83
|
+
|
84
|
+
def bearer_tokens
|
85
|
+
data['bearer_tokens']
|
86
|
+
end
|
87
|
+
|
73
88
|
def alias_from_options(options)
|
74
89
|
options.subcommands.each do |potential_alias|
|
75
90
|
if path = alias_from_name(potential_alias)
|
@@ -87,6 +102,10 @@ module Twurl
|
|
87
102
|
!user_profiles.nil? && !user_profiles[consumer_key].nil?
|
88
103
|
end
|
89
104
|
|
105
|
+
def has_bearer_token_for_consumer_key?(consumer_key)
|
106
|
+
bearer_tokens.nil? ? false : bearer_tokens.to_hash.has_key?(consumer_key)
|
107
|
+
end
|
108
|
+
|
90
109
|
def <<(oauth_client)
|
91
110
|
client_from_file = self[oauth_client.username] || {}
|
92
111
|
client_from_file[oauth_client.consumer_key] = oauth_client.to_hash
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module Twurl
|
2
2
|
class RequestController < AbstractCommandController
|
3
|
-
NO_URI_MESSAGE
|
3
|
+
NO_URI_MESSAGE = 'No URI specified'
|
4
|
+
INVALID_URI_MESSAGE = 'Invalid URI detected'
|
4
5
|
READ_TIMEOUT_MESSAGE = 'A timeout occurred (Net::ReadTimeout). ' \
|
5
6
|
'Please try again or increase the value using --timeout option.'
|
6
7
|
OPEN_TIMEOUT_MESSAGE = 'A timeout occurred (Net::OpenTimeout). ' \
|
@@ -10,19 +11,31 @@ module Twurl
|
|
10
11
|
raise Exception, "You need to authorize first."
|
11
12
|
end
|
12
13
|
options.path ||= OAuthClient.rcfile.alias_from_options(options)
|
14
|
+
raise Exception, NO_URI_MESSAGE if options.path.empty?
|
13
15
|
perform_request
|
14
16
|
end
|
15
17
|
|
16
18
|
def perform_request
|
17
19
|
client.perform_request_from_options(options) { |response|
|
18
|
-
response.read_body { |
|
20
|
+
response.read_body { |body|
|
21
|
+
CLI.print options.json_format ? JsonFormatter.format(body) : body
|
22
|
+
}
|
19
23
|
}
|
20
24
|
rescue URI::InvalidURIError
|
21
|
-
CLI.puts
|
25
|
+
CLI.puts INVALID_URI_MESSAGE
|
22
26
|
rescue Net::ReadTimeout
|
23
27
|
CLI.puts READ_TIMEOUT_MESSAGE
|
24
28
|
rescue Net::OpenTimeout
|
25
29
|
CLI.puts OPEN_TIMEOUT_MESSAGE
|
26
30
|
end
|
27
31
|
end
|
32
|
+
|
33
|
+
class JsonFormatter
|
34
|
+
def self.format(string)
|
35
|
+
json = JSON.parse(string)
|
36
|
+
(json.is_a?(Array) || json.is_a?(Hash)) ? JSON.pretty_generate(json) : string
|
37
|
+
rescue JSON::ParserError, TypeError
|
38
|
+
string
|
39
|
+
end
|
40
|
+
end
|
28
41
|
end
|
data/lib/twurl/version.rb
CHANGED
@@ -2,7 +2,7 @@ module Twurl
|
|
2
2
|
class Version
|
3
3
|
MAJOR = 0 unless defined? Twurl::Version::MAJOR
|
4
4
|
MINOR = 9 unless defined? Twurl::Version::MINOR
|
5
|
-
PATCH =
|
5
|
+
PATCH = 5 unless defined? Twurl::Version::PATCH
|
6
6
|
PRE = nil unless defined? Twurl::Version::PRE # Time.now.to_i.to_s
|
7
7
|
|
8
8
|
class << self
|
data/twurl.gemspec
CHANGED
@@ -5,11 +5,10 @@ require 'twurl/version'
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.add_dependency 'oauth', '~> 0.4'
|
8
|
-
spec.authors = ["Marcel Molina", "Erik Michaels-Ober"]
|
8
|
+
spec.authors = ["Marcel Molina", "Erik Michaels-Ober", "@TwitterDev team"]
|
9
9
|
spec.description = %q{Curl for the Twitter API}
|
10
|
-
spec.email = ['marcel@twitter.com']
|
11
10
|
spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
-
spec.extra_rdoc_files = %w(CODE_OF_CONDUCT.md INSTALL LICENSE README.md)
|
11
|
+
spec.extra_rdoc_files = %w(CODE_OF_CONDUCT.md CONTRIBUTING.md INSTALL.md LICENSE README.md)
|
13
12
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.start_with?('test/') }
|
14
13
|
spec.homepage = 'http://github.com/twitter/twurl'
|
15
14
|
spec.licenses = ['MIT']
|
metadata
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: twurl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marcel Molina
|
8
8
|
- Erik Michaels-Ober
|
9
|
+
- "@TwitterDev team"
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
|
-
date: 2019-12-
|
13
|
+
date: 2019-12-26 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: oauth
|
@@ -26,14 +27,14 @@ dependencies:
|
|
26
27
|
- !ruby/object:Gem::Version
|
27
28
|
version: '0.4'
|
28
29
|
description: Curl for the Twitter API
|
29
|
-
email:
|
30
|
-
- marcel@twitter.com
|
30
|
+
email:
|
31
31
|
executables:
|
32
32
|
- twurl
|
33
33
|
extensions: []
|
34
34
|
extra_rdoc_files:
|
35
35
|
- CODE_OF_CONDUCT.md
|
36
|
-
-
|
36
|
+
- CONTRIBUTING.md
|
37
|
+
- INSTALL.md
|
37
38
|
- LICENSE
|
38
39
|
- README.md
|
39
40
|
files:
|
@@ -42,8 +43,9 @@ files:
|
|
42
43
|
- ".gitignore"
|
43
44
|
- ".travis.yml"
|
44
45
|
- CODE_OF_CONDUCT.md
|
46
|
+
- CONTRIBUTING.md
|
45
47
|
- Gemfile
|
46
|
-
- INSTALL
|
48
|
+
- INSTALL.md
|
47
49
|
- LICENSE
|
48
50
|
- README.md
|
49
51
|
- Rakefile
|
@@ -52,6 +54,8 @@ files:
|
|
52
54
|
- lib/twurl/abstract_command_controller.rb
|
53
55
|
- lib/twurl/account_information_controller.rb
|
54
56
|
- lib/twurl/aliases_controller.rb
|
57
|
+
- lib/twurl/app_only_oauth_client.rb
|
58
|
+
- lib/twurl/app_only_token_information_controller.rb
|
55
59
|
- lib/twurl/authorization_controller.rb
|
56
60
|
- lib/twurl/cli.rb
|
57
61
|
- lib/twurl/configuration_controller.rb
|
@@ -85,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
85
89
|
- !ruby/object:Gem::Version
|
86
90
|
version: '0'
|
87
91
|
requirements: []
|
88
|
-
rubygems_version: 3.0.
|
92
|
+
rubygems_version: 3.0.3
|
89
93
|
signing_key:
|
90
94
|
specification_version: 4
|
91
95
|
summary: Curl for the Twitter API
|
data/INSTALL
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
+-----------------------+
|
2
|
-
| Install with RubyGems |
|
3
|
-
+-----------------------+
|
4
|
-
|
5
|
-
sudo gem i twurl --source http://rubygems.org
|
6
|
-
|
7
|
-
+---------------------+
|
8
|
-
| Build from source |
|
9
|
-
+---------------------+
|
10
|
-
|
11
|
-
rake build
|
12
|
-
|
13
|
-
+---------------------+
|
14
|
-
| Install from source |
|
15
|
-
+---------------------+
|
16
|
-
|
17
|
-
rake install
|
18
|
-
|
19
|
-
+--------------+
|
20
|
-
| Dependencies |
|
21
|
-
+--------------+
|
22
|
-
|
23
|
-
sudo gem i oauth
|