twitter_labs_api 0.3.0 → 0.5.2
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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +32 -0
- data/.rspec +1 -0
- data/CHANGELOG.md +22 -0
- data/Gemfile +10 -3
- data/Gemfile.lock +105 -12
- data/README.md +33 -8
- data/bin/specs +1 -0
- data/lib/twitter_labs_api.rb +5 -97
- data/lib/twitter_labs_api/api_error.rb +13 -0
- data/lib/twitter_labs_api/client.rb +72 -0
- data/lib/twitter_labs_api/resources/tweet.rb +57 -0
- data/lib/twitter_labs_api/resources/user.rb +48 -0
- data/lib/twitter_labs_api/version.rb +2 -2
- data/twitter-labs-api.gemspec +3 -0
- metadata +37 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 697a03319c55056717774438db0f412e52df2337e52452bfecc0c9f5f00c99f2
|
|
4
|
+
data.tar.gz: d841c0f5c73763593663180cae40f1528b21037708fbc9b05f0c4fcc89d83720
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d4f956e69a646de92cd9c79139bae452091603630da9e6596e004d0a6f084c8be362bb083a01aa2ccb66cb4385dcfdc099d35dd8f2b91c291f9a19b93395f774
|
|
7
|
+
data.tar.gz: 88bb52e555879a7c003f4006f3d4f10c34bd509dfd083ac06856006a3e2a9d2f3b57dcd1a0e5d5e93df3c15a92bd8b723c0b362a40b0a287c639cfe234b68aa1
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
|
2
|
+
# They are provided by a third-party and are governed by
|
|
3
|
+
# separate terms of service, privacy policy, and support
|
|
4
|
+
# documentation.
|
|
5
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
|
6
|
+
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
|
7
|
+
|
|
8
|
+
name: Ruby
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
push:
|
|
12
|
+
branches: [ master ]
|
|
13
|
+
pull_request:
|
|
14
|
+
branches: [ master ]
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
test:
|
|
18
|
+
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v2
|
|
23
|
+
- name: Set up Ruby
|
|
24
|
+
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
|
25
|
+
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
|
26
|
+
uses: ruby/setup-ruby@v1
|
|
27
|
+
with:
|
|
28
|
+
ruby-version: 2.7
|
|
29
|
+
- name: Install dependencies
|
|
30
|
+
run: bundle install
|
|
31
|
+
- name: Run tests
|
|
32
|
+
run: bundle exec rspec
|
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--require spec_helper
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
# 0.5.2 - 28 July 2020
|
|
2
|
+
|
|
3
|
+
- Add `search`
|
|
4
|
+
- Finish `rdoc` coverage
|
|
5
|
+
|
|
6
|
+
# 0.5.1 - 28 July 2020
|
|
7
|
+
|
|
8
|
+
- Add `User-Agent` request header
|
|
9
|
+
- Add `hide_reply`
|
|
10
|
+
|
|
11
|
+
# 0.5.0 - 27 July 2020
|
|
12
|
+
|
|
13
|
+
- Refactor structure to prepare for adding additional API endpoints
|
|
14
|
+
- Add `get_users_by_username`
|
|
15
|
+
- Add some test coverage
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# 0.4.0 - 24 July 2020
|
|
19
|
+
|
|
20
|
+
- Better exception-handling: when API returns an error, a `TwitterLabsAPI::APIError` is thrown. It has the `Net::HTTP` response as an attribute so that the error can be handled properly. See the README for an example.
|
|
21
|
+
|
|
22
|
+
|
|
1
23
|
# 0.3.0 - 28 April 2020
|
|
2
24
|
|
|
3
25
|
- Fix: namespace of error class
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,41 +1,134 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
twitter_labs_api (0.
|
|
4
|
+
twitter_labs_api (0.5.2)
|
|
5
|
+
activesupport (~> 6.0)
|
|
6
|
+
httplog (~> 1.4)
|
|
5
7
|
|
|
6
8
|
GEM
|
|
7
9
|
remote: https://rubygems.org/
|
|
8
10
|
specs:
|
|
9
|
-
activesupport (6.0.
|
|
11
|
+
activesupport (6.0.3.2)
|
|
10
12
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
11
13
|
i18n (>= 0.7, < 2)
|
|
12
14
|
minitest (~> 5.1)
|
|
13
15
|
tzinfo (~> 1.1)
|
|
14
|
-
zeitwerk (~> 2.
|
|
16
|
+
zeitwerk (~> 2.2, >= 2.2.2)
|
|
17
|
+
addressable (2.7.0)
|
|
18
|
+
public_suffix (>= 2.0.2, < 5.0)
|
|
19
|
+
alias (0.2.3)
|
|
20
|
+
amazing_print (1.2.1)
|
|
21
|
+
binding.repl (3.0.0)
|
|
22
|
+
boson (1.3.0)
|
|
23
|
+
boson-more (0.3.1)
|
|
24
|
+
boson (>= 1.3.0)
|
|
25
|
+
cd (1.0.1)
|
|
26
|
+
clipboard (1.0.6)
|
|
27
|
+
coderay (1.1.3)
|
|
15
28
|
concurrent-ruby (1.1.6)
|
|
16
|
-
|
|
29
|
+
crack (0.4.3)
|
|
30
|
+
safe_yaml (~> 1.0.0)
|
|
31
|
+
debugging (1.1.1)
|
|
32
|
+
binding.repl (~> 3.0)
|
|
33
|
+
paint (>= 0.9, < 3.0)
|
|
34
|
+
diff-lcs (1.4.4)
|
|
35
|
+
every_day_irb (2.1.0)
|
|
36
|
+
cd (~> 1.0)
|
|
37
|
+
fancy_irb (1.2.1)
|
|
38
|
+
paint (>= 0.9, < 3.0)
|
|
39
|
+
unicode-display_width (~> 1.1)
|
|
40
|
+
ffi (1.13.1)
|
|
41
|
+
g (1.7.2)
|
|
42
|
+
hashdiff (1.0.1)
|
|
43
|
+
hirb (0.7.3)
|
|
44
|
+
httplog (1.4.3)
|
|
17
45
|
rack (>= 1.0)
|
|
18
46
|
rainbow (>= 2.0.0)
|
|
19
|
-
i18n (1.
|
|
47
|
+
i18n (1.8.5)
|
|
20
48
|
concurrent-ruby (~> 1.0)
|
|
21
|
-
|
|
22
|
-
|
|
49
|
+
interactive_editor (0.0.11)
|
|
50
|
+
spoon (>= 0.0.1)
|
|
51
|
+
irbtools (1.7.1)
|
|
52
|
+
alias (~> 0.2.3)
|
|
53
|
+
binding.repl (~> 3.0)
|
|
54
|
+
boson (~> 1.3.0)
|
|
55
|
+
boson-more (~> 0.3.0)
|
|
56
|
+
clipboard (~> 1.0.5)
|
|
57
|
+
coderay (~> 1.1.0)
|
|
58
|
+
debugging (~> 1.0)
|
|
59
|
+
every_day_irb (>= 1.7.1)
|
|
60
|
+
fancy_irb (>= 0.7.3)
|
|
61
|
+
g (>= 1.7.2)
|
|
62
|
+
hirb (~> 0.7, >= 0.7.3)
|
|
63
|
+
interactive_editor (>= 0.0.10)
|
|
64
|
+
method_locator (>= 0.0.4)
|
|
65
|
+
method_source (>= 0.8.2)
|
|
66
|
+
methodfinder (~> 2.0)
|
|
67
|
+
ori (~> 0.1.0)
|
|
68
|
+
os (~> 0.9)
|
|
69
|
+
paint (>= 0.8.7)
|
|
70
|
+
ruby_engine (~> 1.0)
|
|
71
|
+
ruby_info (~> 1.0)
|
|
72
|
+
ruby_version (~> 1.0)
|
|
73
|
+
wirb (>= 1.0.3)
|
|
74
|
+
method_locator (0.0.4)
|
|
75
|
+
method_source (1.0.0)
|
|
76
|
+
methodfinder (2.2.1)
|
|
77
|
+
minitest (5.14.1)
|
|
78
|
+
ori (0.1.0)
|
|
79
|
+
os (0.9.6)
|
|
80
|
+
paint (2.2.0)
|
|
81
|
+
pry (0.13.1)
|
|
82
|
+
coderay (~> 1.1)
|
|
83
|
+
method_source (~> 1.0)
|
|
84
|
+
public_suffix (4.0.5)
|
|
85
|
+
rack (2.2.3)
|
|
23
86
|
rainbow (3.0.0)
|
|
24
87
|
rake (13.0.1)
|
|
88
|
+
rspec (3.9.0)
|
|
89
|
+
rspec-core (~> 3.9.0)
|
|
90
|
+
rspec-expectations (~> 3.9.0)
|
|
91
|
+
rspec-mocks (~> 3.9.0)
|
|
92
|
+
rspec-core (3.9.2)
|
|
93
|
+
rspec-support (~> 3.9.3)
|
|
94
|
+
rspec-expectations (3.9.2)
|
|
95
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
96
|
+
rspec-support (~> 3.9.0)
|
|
97
|
+
rspec-mocks (3.9.1)
|
|
98
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
99
|
+
rspec-support (~> 3.9.0)
|
|
100
|
+
rspec-support (3.9.3)
|
|
101
|
+
ruby_engine (1.0.1)
|
|
102
|
+
ruby_info (1.0.1)
|
|
103
|
+
ruby_version (1.0.2)
|
|
104
|
+
safe_yaml (1.0.5)
|
|
105
|
+
spoon (0.0.6)
|
|
106
|
+
ffi
|
|
25
107
|
thread_safe (0.3.6)
|
|
26
|
-
tzinfo (1.2.
|
|
108
|
+
tzinfo (1.2.7)
|
|
27
109
|
thread_safe (~> 0.1)
|
|
28
|
-
|
|
110
|
+
unicode-display_width (1.7.0)
|
|
111
|
+
webmock (3.8.3)
|
|
112
|
+
addressable (>= 2.3.6)
|
|
113
|
+
crack (>= 0.3.2)
|
|
114
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
|
115
|
+
wirb (2.2.1)
|
|
116
|
+
paint (>= 0.9, < 3.0)
|
|
117
|
+
zeitwerk (2.4.0)
|
|
29
118
|
|
|
30
119
|
PLATFORMS
|
|
31
120
|
ruby
|
|
32
121
|
|
|
33
122
|
DEPENDENCIES
|
|
34
|
-
|
|
123
|
+
amazing_print
|
|
35
124
|
bundler (~> 2.0)
|
|
36
125
|
httplog
|
|
37
|
-
|
|
126
|
+
irbtools
|
|
127
|
+
pry
|
|
128
|
+
rake
|
|
129
|
+
rspec
|
|
38
130
|
twitter_labs_api!
|
|
131
|
+
webmock
|
|
39
132
|
|
|
40
133
|
BUNDLED WITH
|
|
41
|
-
2.
|
|
134
|
+
2.1.4
|
data/README.md
CHANGED
|
@@ -5,9 +5,9 @@ A basic implementation of a Twitter Labs API client as a handy Ruby [gem](https:
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
7
|
### Prerequisite
|
|
8
|
-
All one needs is a Twitter [bearer token](https://developer.twitter.com/en/docs/basics/authentication/oauth-2-0/bearer-tokens) to get started.
|
|
8
|
+
All one needs is a Twitter [bearer token](https://developer.twitter.com/en/docs/basics/authentication/oauth-2-0/bearer-tokens) to get started. The bearer token is available on the 'Tokens and Keys' page within your app's dashboard on the [Twitter for Developers](https://developer.twitter.com/) site.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Alternatively, one can get a bearer token using [this method](https://www.rubydoc.info/gems/twitter/Twitter/REST/Client#bearer_token%3F-instance_method) from https://github.com/sferik/twitter.
|
|
11
11
|
|
|
12
12
|
### Setup
|
|
13
13
|
|
|
@@ -19,7 +19,7 @@ gem install twitter_labs_api
|
|
|
19
19
|
|
|
20
20
|
#### Getting a Tweet by ID
|
|
21
21
|
```ruby
|
|
22
|
-
|
|
22
|
+
require `twitter_labs_api`
|
|
23
23
|
|
|
24
24
|
api = TwitterLabsAPI.new(bearer_token: 'YOUR-BEARER-TOKEN')
|
|
25
25
|
|
|
@@ -40,17 +40,42 @@ api.get_tweet(id: '1235508591232090112', tweet_fields: my_fields)
|
|
|
40
40
|
>> {"author_id"=>"229708614", "created_at"=>"2020-03-05T10:12:57.000Z", "id"=>"1235508591232090112", "text"=>"Hot take: coronavirus will not boost remote work in the long run because spur-of-the-moment work-from-home for in-person companies is likely to be a shitshow."}
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
#### API Errors
|
|
44
|
+
|
|
45
|
+
Sometimes the API will respond with an error, for example, `429 Too Many Requests`. The gem will throw an error with the `Net::HTTP` response as an attribute for proper exception-handling by the consuming app:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
def my_twitter_request
|
|
49
|
+
api.get_tweet(id: '1235508591232090112', tweet_fields: my_fields)
|
|
50
|
+
|
|
51
|
+
rescue TwitterLabsAPI::APIError => e
|
|
52
|
+
puts e.msg # 429 Too Many Requests
|
|
53
|
+
puts e.response # <Net::HTTPTooManyRequests 429 Too Many Requests readbody=true>
|
|
54
|
+
# do something with the Net::HTTP response...
|
|
55
|
+
end
|
|
56
|
+
```
|
|
57
|
+
|
|
43
58
|
### Status
|
|
44
59
|
Currently, the following endpoints are implemented:
|
|
45
60
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
- `TwitterLabsAPI#
|
|
49
|
-
- `TwitterLabsAPI#
|
|
61
|
+
#### Tweets
|
|
62
|
+
|
|
63
|
+
- `TwitterLabsAPI#get_tweet` ([docs](https://developer.twitter.com/en/docs/labs/tweets-and-users/api-reference/get-tweets-id)) - Retrieve a single Tweet object with an `id`
|
|
64
|
+
- `TwitterLabsAPI#get_tweets` ([docs](https://developer.twitter.com/en/docs/labs/tweets-and-users/api-reference/get-tweets)) - Retrieve multiple Tweets with a collection of `ids`
|
|
65
|
+
- `TwitterLabsAPI#hide_reply` ([docs](https://developer.twitter.com/en/docs/labs/hide-replies/api-reference/put-hidden)) - Hide a reply by referencing it's `id`; must be in a conversation belonging to the authenticating user
|
|
66
|
+
- `TwitterLabsAPI#search` ([docs](https://developer.twitter.com/en/docs/labs/recent-search/api-reference/get-recent-search)) - Returns Tweets from the last 7 days that match a search query.
|
|
67
|
+
|
|
68
|
+
#### Users
|
|
69
|
+
|
|
70
|
+
- `TwitterLabsAPI#get_user` ([docs](https://developer.twitter.com/en/docs/labs/tweets-and-users/api-reference/get-users-id)) - Retrieve a single user object with an `id`
|
|
71
|
+
- `TwitterLabsAPI#get_users` ([docs](https://developer.twitter.com/en/docs/labs/tweets-and-users/api-reference/get-users)) - Retrieve multiple user objects with a collection of `ids`
|
|
72
|
+
- `TwitterLabsAPI#get_users_by_username` ([docs](https://developer.twitter.com/en/docs/labs/tweets-and-users/api-reference/get-users)) - Retrieve multiple user objects with a collection of `usernames`
|
|
50
73
|
|
|
51
74
|
## Roadmap
|
|
52
75
|
|
|
53
|
-
|
|
76
|
+
Currently focused on implementing support for all v2 endpoints; if there is enough interest, I will add v1 endpoint support as well.
|
|
77
|
+
|
|
78
|
+
And of course, contributions are welcome :)
|
|
54
79
|
|
|
55
80
|
## Dependencies
|
|
56
81
|
|
data/bin/specs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
bundle exec rspec
|
data/lib/twitter_labs_api.rb
CHANGED
|
@@ -2,105 +2,13 @@ require 'json'
|
|
|
2
2
|
require 'net/http'
|
|
3
3
|
require 'uri'
|
|
4
4
|
require 'active_support/core_ext/hash/indifferent_access'
|
|
5
|
-
|
|
6
|
-
DEFAULT_TWEET_FIELDS = %w[id author_id created_at lang public_metrics].freeze
|
|
7
|
-
DEFAULT_USER_FIELDS = %w[name username].freeze
|
|
5
|
+
require_relative 'twitter_labs_api/client'
|
|
8
6
|
|
|
9
7
|
# A basic implementation of a Twitter Labs API client.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def initialize(bearer_token:, debug: false)
|
|
16
|
-
@bearer_token = bearer_token
|
|
17
|
-
@debug = debug
|
|
18
|
-
require 'httplog' if debug
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# @param [String] :id the ID of the requested Tweet
|
|
22
|
-
# @param [Array<String>] :tweet_fields (["id", "author_id", "created_at", "lang", "public_metrics"]) the list of fields to retrieve for the given tweet
|
|
23
|
-
def get_tweet(id:, tweet_fields: DEFAULT_TWEET_FIELDS)
|
|
24
|
-
url = "https://api.twitter.com/labs/2/tweets/#{id}"
|
|
25
|
-
params = {
|
|
26
|
-
'tweet.fields' => tweet_fields.join(',')
|
|
27
|
-
}.compact
|
|
28
|
-
|
|
29
|
-
make_request(url: url, params: params)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# @param [Array<String>] :ids the collection of requested Tweet IDs
|
|
33
|
-
# @param [Array<String>] :tweet_fields (["id", "author_id", "created_at", "lang", "public_metrics"]) the list of fields to retrieve for the given tweet
|
|
34
|
-
def get_tweets(ids:, tweet_fields: DEFAULT_TWEET_FIELDS)
|
|
35
|
-
url = 'https://api.twitter.com/labs/2/tweets'
|
|
36
|
-
params = {
|
|
37
|
-
'ids' => ids.join(','),
|
|
38
|
-
'tweet.fields' => tweet_fields.join(',')
|
|
39
|
-
}.compact
|
|
40
|
-
|
|
41
|
-
make_request(url: url, params: params, is_collection: true)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# @param [String] :id the ID of the requested User
|
|
45
|
-
# @param [Array<String>] :user_fields (["name", "username"]) the list of fields to retrieve for the given User
|
|
46
|
-
def get_user(id:, user_fields: DEFAULT_USER_FIELDS)
|
|
47
|
-
url = "https://api.twitter.com/labs/2/users/#{id}"
|
|
48
|
-
params = {
|
|
49
|
-
'user.fields' => user_fields.join(',')
|
|
50
|
-
}.compact
|
|
51
|
-
|
|
52
|
-
make_request(url: url, params: params)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# @param [Array<String>] :ids the collection of requested User IDs
|
|
56
|
-
# @param [Array<String>] :user_fields (["name", "username"]) the list of fields to retrieve for the given User
|
|
57
|
-
def get_users(ids:, user_fields: DEFAULT_USER_FIELDS)
|
|
58
|
-
url = 'https://api.twitter.com/labs/2/users'
|
|
59
|
-
params = {
|
|
60
|
-
'ids' => ids.join(','),
|
|
61
|
-
'user.fields' => user_fields.join(',')
|
|
62
|
-
}.compact
|
|
63
|
-
|
|
64
|
-
make_request(url: url, params: params, is_collection: true)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
private
|
|
68
|
-
|
|
69
|
-
def make_request(url:, params: {}, is_collection: false)
|
|
70
|
-
uri = URI.parse(url)
|
|
71
|
-
uri.query = URI.encode_www_form(params)
|
|
72
|
-
request = Net::HTTP::Get.new(uri)
|
|
73
|
-
request['Authorization'] = "Bearer #{bearer_token}"
|
|
74
|
-
req_options = { use_ssl: uri.scheme == 'https' }
|
|
75
|
-
|
|
76
|
-
self.api_response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
|
|
77
|
-
http.request(request)
|
|
8
|
+
module TwitterLabsAPI
|
|
9
|
+
class << self
|
|
10
|
+
def new(bearer_token:, debug: false)
|
|
11
|
+
Client.new(bearer_token: bearer_token, debug: debug)
|
|
78
12
|
end
|
|
79
|
-
|
|
80
|
-
raise TwitterLabsAPIError("#{api_response.code} #{api_response.msg}") unless api_response.is_a?(Net::HTTPSuccess)
|
|
81
|
-
|
|
82
|
-
self.parsed_response = JSON.parse(api_response.body)
|
|
83
|
-
|
|
84
|
-
handle_api_error if error_response?
|
|
85
|
-
|
|
86
|
-
is_collection ? handle_collection : handle_single
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def error_response?
|
|
90
|
-
parsed_response.key?('errors')
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def handle_single
|
|
94
|
-
parsed_response['data'].with_indifferent_access
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def handle_collection
|
|
98
|
-
parsed_response['data'].map(&:with_indifferent_access)
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def handle_api_error
|
|
102
|
-
error = parsed_response['errors'].first
|
|
103
|
-
|
|
104
|
-
raise TwitterLabsAPIError, "#{error['title']}: #{error['detail']} #{error['type']}"
|
|
105
13
|
end
|
|
106
14
|
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module TwitterLabsAPI
|
|
2
|
+
class APIError < StandardError
|
|
3
|
+
DEFAULT_MESSAGE = 'Twitter Labs API error, check the response attribute'.freeze
|
|
4
|
+
|
|
5
|
+
attr_reader :response
|
|
6
|
+
|
|
7
|
+
def initialize(msg = DEFAULT_MESSAGE, response = nil)
|
|
8
|
+
@response = response
|
|
9
|
+
|
|
10
|
+
super(msg)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require_relative 'api_error'
|
|
2
|
+
require_relative 'resources/tweet'
|
|
3
|
+
require_relative 'resources/user'
|
|
4
|
+
|
|
5
|
+
module TwitterLabsAPI
|
|
6
|
+
class Client
|
|
7
|
+
include Resources::Tweet
|
|
8
|
+
include Resources::User
|
|
9
|
+
|
|
10
|
+
attr_accessor :bearer_token, :debug, :api_response, :parsed_response
|
|
11
|
+
|
|
12
|
+
def initialize(bearer_token:, debug: false)
|
|
13
|
+
@bearer_token = bearer_token
|
|
14
|
+
@debug = debug
|
|
15
|
+
require 'httplog' if debug
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def make_request(url:, params: {}, is_collection: false, method: :get)
|
|
21
|
+
uri = URI.parse(url)
|
|
22
|
+
uri.query = URI.encode_www_form(params)
|
|
23
|
+
request = http_adapter(method).new(uri)
|
|
24
|
+
request['Authorization'] = "Bearer #{bearer_token}"
|
|
25
|
+
request['User-Agent'] = "twitter_labs_api gem #{TwitterLabsAPI::VERSION}"
|
|
26
|
+
req_options = { use_ssl: uri.scheme == 'https' }
|
|
27
|
+
|
|
28
|
+
self.api_response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
|
|
29
|
+
http.request(request)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
raise_http_error unless api_response.is_a?(Net::HTTPSuccess)
|
|
33
|
+
|
|
34
|
+
self.parsed_response = JSON.parse(api_response.body)
|
|
35
|
+
|
|
36
|
+
handle_api_error if error_response?
|
|
37
|
+
|
|
38
|
+
is_collection ? handle_collection : handle_single
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def http_adapter(method)
|
|
42
|
+
case method
|
|
43
|
+
when :put
|
|
44
|
+
Net::HTTP::Put
|
|
45
|
+
else
|
|
46
|
+
Net::HTTP::Get
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def error_response?
|
|
51
|
+
parsed_response.key?('errors')
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def handle_single
|
|
55
|
+
parsed_response['data'].with_indifferent_access
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def handle_collection
|
|
59
|
+
parsed_response['data'].map(&:with_indifferent_access)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def raise_http_error
|
|
63
|
+
raise(APIError.new("#{api_response.code} #{api_response.msg}", api_response))
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def handle_api_error
|
|
67
|
+
error = parsed_response['errors'].first
|
|
68
|
+
|
|
69
|
+
raise APIError.new("#{error['title']}: #{error['detail']} #{error['type']}", api_response)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module TwitterLabsAPI
|
|
2
|
+
module Resources
|
|
3
|
+
module Tweet
|
|
4
|
+
DEFAULT_TWEET_FIELDS = %w[id author_id created_at lang public_metrics].freeze
|
|
5
|
+
|
|
6
|
+
# Returns a variety of information about a single Tweet specified by the requested ID.
|
|
7
|
+
# @param [String] :id the ID of the requested Tweet
|
|
8
|
+
# @param [Array<String>] :tweet_fields (["id", "author_id", "created_at", "lang", "public_metrics"]) the list of fields to retrieve for the given tweet
|
|
9
|
+
# @return Hash an object with requested tweet fields
|
|
10
|
+
def get_tweet(id:, tweet_fields: DEFAULT_TWEET_FIELDS)
|
|
11
|
+
url = "https://api.twitter.com/labs/2/tweets/#{id}"
|
|
12
|
+
params = {
|
|
13
|
+
'tweet.fields' => tweet_fields.join(',')
|
|
14
|
+
}.compact
|
|
15
|
+
|
|
16
|
+
make_request(url: url, params: params)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Returns a variety of information about the Tweet specified by the requested ID or list of IDs.
|
|
20
|
+
# @param [Array<String>] :ids the collection of requested Tweet IDs
|
|
21
|
+
# @param [Array<String>] :tweet_fields (["id", "author_id", "created_at", "lang", "public_metrics"]) the list of fields to retrieve for the given tweet
|
|
22
|
+
# @return [Array<Hash>] of tweet objects with the requested tweet fields
|
|
23
|
+
def get_tweets(ids:, tweet_fields: DEFAULT_TWEET_FIELDS)
|
|
24
|
+
url = 'https://api.twitter.com/labs/2/tweets'
|
|
25
|
+
params = {
|
|
26
|
+
'ids' => ids.join(','),
|
|
27
|
+
'tweet.fields' => tweet_fields.join(',')
|
|
28
|
+
}.compact
|
|
29
|
+
|
|
30
|
+
make_request(url: url, params: params, is_collection: true)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Hides or unhides a reply to a Tweet.
|
|
34
|
+
# @param [String] :id the ID of the requested Tweet; must belong to a conversation by the authenticated user
|
|
35
|
+
# @return boolean indicating the hidden status of the requested tweet
|
|
36
|
+
def hide_reply(id:)
|
|
37
|
+
url = "https://api.twitter.com/labs/2/tweets/#{id}/hidden"
|
|
38
|
+
|
|
39
|
+
make_request(url: url, method: :put)[:hidden]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# The Labs recent search endpoint returns Tweets from the last 7 days that match a search query.
|
|
43
|
+
# @param [String] :query the search query
|
|
44
|
+
# @param [Array<String>] :tweet_fields (["id", "author_id", "created_at", "lang", "public_metrics"]) the list of fields to retrieve for the given tweet
|
|
45
|
+
# @param [Array<String>] :tweet_fields (["id", "author_id", "created_at", "lang", "public_metrics"]) the list of fields to retrieve for the given tweet
|
|
46
|
+
def search(query:, tweet_fields: DEFAULT_TWEET_FIELDS)
|
|
47
|
+
url = "https://api.twitter.com/labs/2/tweets/search"
|
|
48
|
+
params = {
|
|
49
|
+
'query' => query,
|
|
50
|
+
'tweet.fields' => tweet_fields.join(',')
|
|
51
|
+
}.compact
|
|
52
|
+
|
|
53
|
+
make_request(url: url, params: params, is_collection: true)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module TwitterLabsAPI
|
|
2
|
+
module Resources
|
|
3
|
+
module User
|
|
4
|
+
DEFAULT_USER_FIELDS = %w[id name username].freeze
|
|
5
|
+
|
|
6
|
+
# Returns a variety of information about a single more user specified by the requested ID.
|
|
7
|
+
# @param [String] :id the ID of the requested User
|
|
8
|
+
# @param [Array<String>] :user_fields (["name", "username"]) the list of fields to retrieve for the given User
|
|
9
|
+
# @return Hash an object with requested user fields
|
|
10
|
+
def get_user(id:, user_fields: DEFAULT_USER_FIELDS)
|
|
11
|
+
url = "https://api.twitter.com/labs/2/users/#{id}"
|
|
12
|
+
params = {
|
|
13
|
+
'user.fields' => user_fields.join(',')
|
|
14
|
+
}.compact
|
|
15
|
+
|
|
16
|
+
make_request(url: url, params: params)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Returns a variety of information about one or more Users specified by the requested IDs.
|
|
20
|
+
# @param [Array<String>] :ids the collection of requested User IDs
|
|
21
|
+
# @param [Array<String>] :user_fields (["name", "username"]) the list of fields to retrieve for the given User
|
|
22
|
+
# @return [Array<Hash>] of user objects with the requested user fields
|
|
23
|
+
def get_users(ids:, user_fields: DEFAULT_USER_FIELDS)
|
|
24
|
+
url = 'https://api.twitter.com/labs/2/users'
|
|
25
|
+
params = {
|
|
26
|
+
'ids' => ids.join(','),
|
|
27
|
+
'user.fields' => user_fields.join(',')
|
|
28
|
+
}.compact
|
|
29
|
+
|
|
30
|
+
make_request(url: url, params: params, is_collection: true)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Returns a variety of information about one or more Users specified by the requested usernames.
|
|
34
|
+
# @param [Array<String>] :usernames the collection of requested Usernames
|
|
35
|
+
# @param [Array<String>] :user_fields (["name", "username"]) the list of fields to retrieve for the given User
|
|
36
|
+
# @return [Array<Hash>] of user objects with the requested user fields
|
|
37
|
+
def get_users_by_usernames(usernames:, user_fields: DEFAULT_USER_FIELDS)
|
|
38
|
+
url = 'https://api.twitter.com/labs/2/users/by'
|
|
39
|
+
params = {
|
|
40
|
+
'usernames' => usernames.join(','),
|
|
41
|
+
'user.fields' => user_fields.join(',')
|
|
42
|
+
}.compact
|
|
43
|
+
|
|
44
|
+
make_request(url: url, params: params, is_collection: true)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
VERSION = '0.
|
|
1
|
+
module TwitterLabsAPI
|
|
2
|
+
VERSION = '0.5.2'.freeze
|
|
3
3
|
end
|
data/twitter-labs-api.gemspec
CHANGED
|
@@ -25,6 +25,9 @@ Gem::Specification.new do |spec|
|
|
|
25
25
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
26
26
|
spec.require_paths = ['lib']
|
|
27
27
|
|
|
28
|
+
spec.add_dependency 'activesupport', '~> 6.0'
|
|
29
|
+
spec.add_dependency 'httplog', '~> 1.4'
|
|
30
|
+
|
|
28
31
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
|
29
32
|
spec.add_development_dependency 'rake', '~> 13.0'
|
|
30
33
|
end
|
metadata
CHANGED
|
@@ -1,15 +1,43 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: twitter_labs_api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- tomholford
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2020-
|
|
11
|
+
date: 2020-07-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activesupport
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '6.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '6.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: httplog
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '1.4'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '1.4'
|
|
13
41
|
- !ruby/object:Gem::Dependency
|
|
14
42
|
name: bundler
|
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -45,7 +73,9 @@ executables: []
|
|
|
45
73
|
extensions: []
|
|
46
74
|
extra_rdoc_files: []
|
|
47
75
|
files:
|
|
76
|
+
- ".github/workflows/ruby.yml"
|
|
48
77
|
- ".gitignore"
|
|
78
|
+
- ".rspec"
|
|
49
79
|
- CHANGELOG.md
|
|
50
80
|
- Gemfile
|
|
51
81
|
- Gemfile.lock
|
|
@@ -54,7 +84,12 @@ files:
|
|
|
54
84
|
- Rakefile
|
|
55
85
|
- bin/console
|
|
56
86
|
- bin/setup
|
|
87
|
+
- bin/specs
|
|
57
88
|
- lib/twitter_labs_api.rb
|
|
89
|
+
- lib/twitter_labs_api/api_error.rb
|
|
90
|
+
- lib/twitter_labs_api/client.rb
|
|
91
|
+
- lib/twitter_labs_api/resources/tweet.rb
|
|
92
|
+
- lib/twitter_labs_api/resources/user.rb
|
|
58
93
|
- lib/twitter_labs_api/version.rb
|
|
59
94
|
- twitter-labs-api.gemspec
|
|
60
95
|
homepage: https://github.com/tomholford/twitter-labs-api
|