twitter_labs_api 0.4.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +32 -0
- data/.rspec +1 -0
- data/CHANGELOG.md +46 -0
- data/Gemfile +10 -4
- data/Gemfile.lock +92 -5
- data/README.md +25 -14
- data/bin/specs +1 -0
- data/lib/twitter_labs_api.rb +5 -111
- data/lib/twitter_labs_api/api_error.rb +13 -0
- data/lib/twitter_labs_api/client.rb +73 -0
- data/lib/twitter_labs_api/resources/tweet.rb +58 -0
- data/lib/twitter_labs_api/resources/user.rb +49 -0
- data/lib/twitter_labs_api/services/params_service.rb +15 -0
- data/lib/twitter_labs_api/version.rb +2 -2
- data/twitter-labs-api.gemspec +3 -0
- metadata +38 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 308afe5c20914abca1ed575f09b1e5924206034eae405886944d495fb6e4d3da
|
4
|
+
data.tar.gz: 5b931944828a477b39d0360d492c2fb6215b13ec12f8b953ac01533bcf822b44
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12be24ffd5dfbc7b81b5bba6665cd264c39a8d9cbe828977b569285093b37e00036950db0a1b94c50fb287847897a6d8ba356bcb8b950c33197c4bda888a2a5a
|
7
|
+
data.tar.gz: b44d15ee5acc7a147ea3c2208c242cd45b6d5966cfbac0a2c2946b85594f73263853c4b0b45d9125567d20ffb295e33c45ae6491a2d4a0a31535a94a00b77ef7
|
@@ -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,49 @@
|
|
1
|
+
# 0.7.0 - 28 August 2020
|
2
|
+
|
3
|
+
- Refactor interface for requesting custom response fields
|
4
|
+
|
5
|
+
## Breaking Change
|
6
|
+
|
7
|
+
Heads up, the argument to request fields was previously called `tweet_fields`; it is now called `fields`.
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
api.get_tweet(id: '123455683780', fields: requested_fields)
|
11
|
+
```
|
12
|
+
|
13
|
+
The `fields` parameter now supports multiple different data types (instead of just Tweet attributes). So for example, to request the URL of an embedded media item:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
requested_fields = { tweet: %w[id username], media: %w[url] }
|
17
|
+
|
18
|
+
api.get_tweet(id: my_id, fields: requested_fields)
|
19
|
+
```
|
20
|
+
|
21
|
+
# 0.6.0 - 28 July 2020
|
22
|
+
|
23
|
+
- Fix: broken dependency
|
24
|
+
|
25
|
+
# 0.5.2 - 28 July 2020
|
26
|
+
|
27
|
+
- Add `search`
|
28
|
+
- Finish `rdoc` coverage
|
29
|
+
|
30
|
+
# 0.5.1 - 28 July 2020
|
31
|
+
|
32
|
+
- Add `User-Agent` request header
|
33
|
+
- Add `hide_reply`
|
34
|
+
|
35
|
+
# 0.5.0 - 27 July 2020
|
36
|
+
|
37
|
+
- Refactor structure to prepare for adding additional API endpoints
|
38
|
+
- Add `get_users_by_username`
|
39
|
+
- Add some test coverage
|
40
|
+
|
41
|
+
|
42
|
+
# 0.4.0 - 24 July 2020
|
43
|
+
|
44
|
+
- 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.
|
45
|
+
|
46
|
+
|
1
47
|
# 0.3.0 - 28 April 2020
|
2
48
|
|
3
49
|
- Fix: namespace of error class
|
data/Gemfile
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
|
3
|
+
group :development, :test do
|
4
|
+
gem 'amazing_print'
|
5
|
+
gem 'httplog'
|
6
|
+
gem 'irbtools'
|
7
|
+
gem 'pry'
|
8
|
+
gem 'rake'
|
9
|
+
gem 'rspec'
|
10
|
+
gem 'webmock'
|
11
|
+
end
|
4
12
|
|
5
|
-
|
6
|
-
gem 'httplog'
|
7
|
-
gem 'pry'
|
13
|
+
gemspec
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
twitter_labs_api (0.
|
4
|
+
twitter_labs_api (0.7.0)
|
5
|
+
activesupport (~> 6.0)
|
6
|
+
httplog (~> 1.4)
|
5
7
|
|
6
8
|
GEM
|
7
9
|
remote: https://rubygems.org/
|
@@ -12,36 +14,121 @@ GEM
|
|
12
14
|
minitest (~> 5.1)
|
13
15
|
tzinfo (~> 1.1)
|
14
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)
|
15
27
|
coderay (1.1.3)
|
16
|
-
concurrent-ruby (1.1.
|
17
|
-
|
28
|
+
concurrent-ruby (1.1.7)
|
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)
|
18
45
|
rack (>= 1.0)
|
19
46
|
rainbow (>= 2.0.0)
|
20
47
|
i18n (1.8.5)
|
21
48
|
concurrent-ruby (~> 1.0)
|
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)
|
22
75
|
method_source (1.0.0)
|
76
|
+
methodfinder (2.2.1)
|
23
77
|
minitest (5.14.1)
|
78
|
+
ori (0.1.0)
|
79
|
+
os (0.9.6)
|
80
|
+
paint (2.2.0)
|
24
81
|
pry (0.13.1)
|
25
82
|
coderay (~> 1.1)
|
26
83
|
method_source (~> 1.0)
|
84
|
+
public_suffix (4.0.5)
|
27
85
|
rack (2.2.3)
|
28
86
|
rainbow (3.0.0)
|
29
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
|
30
107
|
thread_safe (0.3.6)
|
31
108
|
tzinfo (1.2.7)
|
32
109
|
thread_safe (~> 0.1)
|
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)
|
33
117
|
zeitwerk (2.4.0)
|
34
118
|
|
35
119
|
PLATFORMS
|
36
120
|
ruby
|
37
121
|
|
38
122
|
DEPENDENCIES
|
39
|
-
|
123
|
+
amazing_print
|
40
124
|
bundler (~> 2.0)
|
41
125
|
httplog
|
126
|
+
irbtools
|
42
127
|
pry
|
43
|
-
rake
|
128
|
+
rake
|
129
|
+
rspec
|
44
130
|
twitter_labs_api!
|
131
|
+
webmock
|
45
132
|
|
46
133
|
BUNDLED WITH
|
47
134
|
2.1.4
|
data/README.md
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
# twitter-labs-api
|
2
2
|
|
3
|
+
[![Labs v2](https://img.shields.io/static/v1?label=Twitter%20API&message=Developer%20Labs%20v2&color=192430&style=flat&logo=Twitter)](https://developer.twitter.com/en/docs/labs/overview/versioning)
|
4
|
+
|
3
5
|
A basic implementation of a Twitter Labs API client as a handy Ruby [gem](https://rubygems.org/gems/twitter_labs_api). This project uses the v2 endpoints announced [here](https://twittercommunity.com/t/releasing-a-new-version-of-labs-endpoints/134219/3).
|
4
6
|
|
5
7
|
## Usage
|
6
8
|
|
7
9
|
### 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.
|
10
|
+
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
11
|
|
10
|
-
|
12
|
+
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
13
|
|
12
14
|
### Setup
|
13
15
|
|
@@ -19,7 +21,7 @@ gem install twitter_labs_api
|
|
19
21
|
|
20
22
|
#### Getting a Tweet by ID
|
21
23
|
```ruby
|
22
|
-
|
24
|
+
require `twitter_labs_api`
|
23
25
|
|
24
26
|
api = TwitterLabsAPI.new(bearer_token: 'YOUR-BEARER-TOKEN')
|
25
27
|
|
@@ -30,19 +32,18 @@ api.get_tweet(id: '1234671272602193920')
|
|
30
32
|
|
31
33
|
#### Specifying which fields to include in the response
|
32
34
|
|
33
|
-
By default, the gem requests the 'default' fields for each entity. See the [API Reference](https://developer.twitter.com/en/docs/labs/tweets-and-users/api-reference) for available fields. One can customize the response payload
|
35
|
+
By default, the gem requests the 'default' fields for each entity. See the [API Reference](https://developer.twitter.com/en/docs/labs/tweets-and-users/api-reference) for available fields. One can customize the response payload, depending on the requested resource.
|
34
36
|
|
37
|
+
For example, to request the URL of a Tweet's embedded media item:
|
35
38
|
```ruby
|
36
|
-
|
37
|
-
|
38
|
-
api.get_tweet(id: '1235508591232090112', tweet_fields: my_fields)
|
39
|
+
requested_fields = { tweet: %w[id username], media: %w[url] }
|
39
40
|
|
40
|
-
|
41
|
+
api.get_tweet(id: my_id, fields: requested_fields)
|
41
42
|
```
|
42
43
|
|
43
44
|
#### API Errors
|
44
45
|
|
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
|
+
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
|
|
47
48
|
```ruby
|
48
49
|
def my_twitter_request
|
@@ -58,14 +59,24 @@ end
|
|
58
59
|
### Status
|
59
60
|
Currently, the following endpoints are implemented:
|
60
61
|
|
61
|
-
|
62
|
-
|
63
|
-
- `TwitterLabsAPI#
|
64
|
-
- `TwitterLabsAPI#
|
62
|
+
#### Tweets
|
63
|
+
|
64
|
+
- `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`
|
65
|
+
- `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`
|
66
|
+
- `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
|
67
|
+
- `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.
|
68
|
+
|
69
|
+
#### Users
|
70
|
+
|
71
|
+
- `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`
|
72
|
+
- `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`
|
73
|
+
- `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`
|
65
74
|
|
66
75
|
## Roadmap
|
67
76
|
|
68
|
-
|
77
|
+
Currently focused on implementing support for all v2 endpoints; if there is enough interest, I will add v1 endpoint support as well.
|
78
|
+
|
79
|
+
And of course, contributions are welcome :)
|
69
80
|
|
70
81
|
## Dependencies
|
71
82
|
|
data/bin/specs
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
bundle exec rspec
|
data/lib/twitter_labs_api.rb
CHANGED
@@ -2,119 +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
|
-
DEFAULT_MESSAGE = 'Twitter Labs API error, check the response attribute'.freeze
|
15
|
-
|
16
|
-
attr_reader :response
|
17
|
-
|
18
|
-
def initialize(msg = DEFAULT_MESSAGE, response = nil)
|
19
|
-
@response = response
|
20
|
-
|
21
|
-
super(msg)
|
8
|
+
module TwitterLabsAPI
|
9
|
+
class << self
|
10
|
+
def new(bearer_token:, debug: false)
|
11
|
+
Client.new(bearer_token: bearer_token, debug: debug)
|
22
12
|
end
|
23
13
|
end
|
24
|
-
|
25
|
-
def initialize(bearer_token:, debug: false)
|
26
|
-
@bearer_token = bearer_token
|
27
|
-
@debug = debug
|
28
|
-
require 'httplog' if debug
|
29
|
-
end
|
30
|
-
|
31
|
-
# @param [String] :id the ID of the requested Tweet
|
32
|
-
# @param [Array<String>] :tweet_fields (["id", "author_id", "created_at", "lang", "public_metrics"]) the list of fields to retrieve for the given tweet
|
33
|
-
def get_tweet(id:, tweet_fields: DEFAULT_TWEET_FIELDS)
|
34
|
-
url = "https://api.twitter.com/labs/2/tweets/#{id}"
|
35
|
-
params = {
|
36
|
-
'tweet.fields' => tweet_fields.join(',')
|
37
|
-
}.compact
|
38
|
-
|
39
|
-
make_request(url: url, params: params)
|
40
|
-
end
|
41
|
-
|
42
|
-
# @param [Array<String>] :ids the collection of requested Tweet IDs
|
43
|
-
# @param [Array<String>] :tweet_fields (["id", "author_id", "created_at", "lang", "public_metrics"]) the list of fields to retrieve for the given tweet
|
44
|
-
def get_tweets(ids:, tweet_fields: DEFAULT_TWEET_FIELDS)
|
45
|
-
url = 'https://api.twitter.com/labs/2/tweets'
|
46
|
-
params = {
|
47
|
-
'ids' => ids.join(','),
|
48
|
-
'tweet.fields' => tweet_fields.join(',')
|
49
|
-
}.compact
|
50
|
-
|
51
|
-
make_request(url: url, params: params, is_collection: true)
|
52
|
-
end
|
53
|
-
|
54
|
-
# @param [String] :id the ID of the requested User
|
55
|
-
# @param [Array<String>] :user_fields (["name", "username"]) the list of fields to retrieve for the given User
|
56
|
-
def get_user(id:, user_fields: DEFAULT_USER_FIELDS)
|
57
|
-
url = "https://api.twitter.com/labs/2/users/#{id}"
|
58
|
-
params = {
|
59
|
-
'user.fields' => user_fields.join(',')
|
60
|
-
}.compact
|
61
|
-
|
62
|
-
make_request(url: url, params: params)
|
63
|
-
end
|
64
|
-
|
65
|
-
# @param [Array<String>] :ids the collection of requested User IDs
|
66
|
-
# @param [Array<String>] :user_fields (["name", "username"]) the list of fields to retrieve for the given User
|
67
|
-
def get_users(ids:, user_fields: DEFAULT_USER_FIELDS)
|
68
|
-
url = 'https://api.twitter.com/labs/2/users'
|
69
|
-
params = {
|
70
|
-
'ids' => ids.join(','),
|
71
|
-
'user.fields' => user_fields.join(',')
|
72
|
-
}.compact
|
73
|
-
|
74
|
-
make_request(url: url, params: params, is_collection: true)
|
75
|
-
end
|
76
|
-
|
77
|
-
private
|
78
|
-
|
79
|
-
def make_request(url:, params: {}, is_collection: false)
|
80
|
-
uri = URI.parse(url)
|
81
|
-
uri.query = URI.encode_www_form(params)
|
82
|
-
request = Net::HTTP::Get.new(uri)
|
83
|
-
request['Authorization'] = "Bearer #{bearer_token}"
|
84
|
-
req_options = { use_ssl: uri.scheme == 'https' }
|
85
|
-
|
86
|
-
self.api_response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
|
87
|
-
http.request(request)
|
88
|
-
end
|
89
|
-
|
90
|
-
raise_http_error unless api_response.is_a?(Net::HTTPSuccess)
|
91
|
-
|
92
|
-
self.parsed_response = JSON.parse(api_response.body)
|
93
|
-
|
94
|
-
handle_api_error if error_response?
|
95
|
-
|
96
|
-
is_collection ? handle_collection : handle_single
|
97
|
-
end
|
98
|
-
|
99
|
-
def error_response?
|
100
|
-
parsed_response.key?('errors')
|
101
|
-
end
|
102
|
-
|
103
|
-
def handle_single
|
104
|
-
parsed_response['data'].with_indifferent_access
|
105
|
-
end
|
106
|
-
|
107
|
-
def handle_collection
|
108
|
-
parsed_response['data'].map(&:with_indifferent_access)
|
109
|
-
end
|
110
|
-
|
111
|
-
def raise_http_error
|
112
|
-
raise(APIError.new("#{api_response.code} #{api_response.msg}", api_response))
|
113
|
-
end
|
114
|
-
|
115
|
-
def handle_api_error
|
116
|
-
error = parsed_response['errors'].first
|
117
|
-
|
118
|
-
raise APIError.new("#{error['title']}: #{error['detail']} #{error['type']}", api_response)
|
119
|
-
end
|
120
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,73 @@
|
|
1
|
+
require_relative 'version'
|
2
|
+
require_relative 'api_error'
|
3
|
+
require_relative 'resources/tweet'
|
4
|
+
require_relative 'resources/user'
|
5
|
+
|
6
|
+
module TwitterLabsAPI
|
7
|
+
class Client
|
8
|
+
include Resources::Tweet
|
9
|
+
include Resources::User
|
10
|
+
|
11
|
+
attr_accessor :bearer_token, :debug, :api_response, :parsed_response
|
12
|
+
|
13
|
+
def initialize(bearer_token:, debug: false)
|
14
|
+
@bearer_token = bearer_token
|
15
|
+
@debug = debug
|
16
|
+
require 'httplog' if debug
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def make_request(url:, params: {}, is_collection: false, method: :get)
|
22
|
+
uri = URI.parse(url)
|
23
|
+
uri.query = URI.encode_www_form(params)
|
24
|
+
request = http_adapter(method).new(uri)
|
25
|
+
request['Authorization'] = "Bearer #{bearer_token}"
|
26
|
+
request['User-Agent'] = "twitter_labs_api gem #{TwitterLabsAPI::VERSION}"
|
27
|
+
req_options = { use_ssl: uri.scheme == 'https' }
|
28
|
+
|
29
|
+
self.api_response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
|
30
|
+
http.request(request)
|
31
|
+
end
|
32
|
+
|
33
|
+
raise_http_error unless api_response.is_a?(Net::HTTPSuccess)
|
34
|
+
|
35
|
+
self.parsed_response = JSON.parse(api_response.body)
|
36
|
+
|
37
|
+
handle_api_error if error_response?
|
38
|
+
|
39
|
+
is_collection ? handle_collection : handle_single
|
40
|
+
end
|
41
|
+
|
42
|
+
def http_adapter(method)
|
43
|
+
case method
|
44
|
+
when :put
|
45
|
+
Net::HTTP::Put
|
46
|
+
else
|
47
|
+
Net::HTTP::Get
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def error_response?
|
52
|
+
parsed_response.key?('errors')
|
53
|
+
end
|
54
|
+
|
55
|
+
def handle_single
|
56
|
+
parsed_response['data'].with_indifferent_access
|
57
|
+
end
|
58
|
+
|
59
|
+
def handle_collection
|
60
|
+
parsed_response['data'].map(&:with_indifferent_access)
|
61
|
+
end
|
62
|
+
|
63
|
+
def raise_http_error
|
64
|
+
raise(APIError.new("#{api_response.code} #{api_response.msg}", api_response))
|
65
|
+
end
|
66
|
+
|
67
|
+
def handle_api_error
|
68
|
+
error = parsed_response['errors'].first
|
69
|
+
|
70
|
+
raise APIError.new("#{error['title']}: #{error['detail']} #{error['type']}", api_response)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module TwitterLabsAPI
|
2
|
+
module Resources
|
3
|
+
module Tweet
|
4
|
+
DEFAULT_TWEET_FIELDS = %w[id author_id created_at lang public_metrics].freeze
|
5
|
+
DEFAULT_FIELDS = { tweet: DEFAULT_TWEET_FIELDS }.freeze
|
6
|
+
|
7
|
+
# Returns a variety of information about a single Tweet specified by the requested ID.
|
8
|
+
# @param [String] :id the ID of the requested Tweet
|
9
|
+
# @param [Hash] :fields a hash for fields to include in the response payload;
|
10
|
+
#
|
11
|
+
# e.g.: { tweet: %w[id username], media: %w[url] }
|
12
|
+
# @return Hash an object with requested tweet fields
|
13
|
+
def get_tweet(id:, fields: DEFAULT_FIELDS)
|
14
|
+
url = "https://api.twitter.com/labs/2/tweets/#{id}"
|
15
|
+
params = ParamsService.from_fields(fields)
|
16
|
+
|
17
|
+
make_request(url: url, params: params)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns a variety of information about the Tweet specified by the requested ID or list of IDs.
|
21
|
+
# @param [Array<String>] :ids the collection of requested Tweet IDs
|
22
|
+
# @param [Hash] :fields a hash for fields to include in the response payload;
|
23
|
+
#
|
24
|
+
# e.g.: { tweet: %w[id username], media: %w[url] }
|
25
|
+
# @return [Array<Hash>] of tweet objects with the requested tweet fields
|
26
|
+
def get_tweets(ids:, fields: DEFAULT_FIELDS)
|
27
|
+
url = 'https://api.twitter.com/labs/2/tweets'
|
28
|
+
params = ParamsService.from_fields(fields)
|
29
|
+
params.merge!({ ids: ids.join(',') })
|
30
|
+
|
31
|
+
make_request(url: url, params: params, is_collection: true)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Hides or unhides a reply to a Tweet.
|
35
|
+
# @param [String] :id the ID of the requested Tweet; must belong to a conversation by the authenticated user
|
36
|
+
# @return boolean indicating the hidden status of the requested tweet
|
37
|
+
def hide_reply(id:)
|
38
|
+
url = "https://api.twitter.com/labs/2/tweets/#{id}/hidden"
|
39
|
+
|
40
|
+
make_request(url: url, method: :put)[:hidden]
|
41
|
+
end
|
42
|
+
|
43
|
+
# The Labs recent search endpoint returns Tweets from the last 7 days that match a search query.
|
44
|
+
# @param [String] :query the search query
|
45
|
+
# @param [Hash] :fields a hash for fields to include in the response payload;
|
46
|
+
#
|
47
|
+
# e.g.: { tweet: %w[id username], media: %w[url] }
|
48
|
+
# @return [Array<Hash>] of tweet objects with the requested tweet fields
|
49
|
+
def search(query:, fields: DEFAULT_FIELDS)
|
50
|
+
url = 'https://api.twitter.com/labs/2/tweets/search'
|
51
|
+
params = ParamsService.from_fields(fields)
|
52
|
+
params.merge!({ query: query })
|
53
|
+
|
54
|
+
make_request(url: url, params: params, is_collection: true)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module TwitterLabsAPI
|
2
|
+
module Resources
|
3
|
+
module User
|
4
|
+
DEFAULT_USER_FIELDS = %w[id name username].freeze
|
5
|
+
DEFAULT_FIELDS = { user: DEFAULT_USER_FIELDS }.freeze
|
6
|
+
|
7
|
+
# Returns a variety of information about a single more user specified by the requested ID.
|
8
|
+
# @param [String] :id the ID of the requested User
|
9
|
+
# @param [Hash] :fields a hash for fields to include in the response payload;
|
10
|
+
#
|
11
|
+
# e.g.: { user: %w[id name username] }
|
12
|
+
# @return Hash an object with requested user fields
|
13
|
+
def get_user(id:, fields: DEFAULT_FIELDS)
|
14
|
+
url = "https://api.twitter.com/labs/2/users/#{id}"
|
15
|
+
params = ParamsService.from_fields(fields)
|
16
|
+
|
17
|
+
make_request(url: url, params: params)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns a variety of information about one or more Users specified by the requested IDs.
|
21
|
+
# @param [Array<String>] :ids the collection of requested User IDs
|
22
|
+
# @param [Hash] :fields a hash for fields to include in the response payload;
|
23
|
+
#
|
24
|
+
# e.g.: { user: %w[id name username] }
|
25
|
+
# @return [Array<Hash>] of user objects with the requested user fields
|
26
|
+
def get_users(ids:, fields: DEFAULT_FIELDS)
|
27
|
+
url = 'https://api.twitter.com/labs/2/users'
|
28
|
+
params = ParamsService.from_fields(fields)
|
29
|
+
params.merge!({ ids: ids.join(',') })
|
30
|
+
|
31
|
+
make_request(url: url, params: params, is_collection: true)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns a variety of information about one or more Users specified by the requested usernames.
|
35
|
+
# @param [Array<String>] :usernames the collection of requested Usernames
|
36
|
+
# @param [Hash] :fields a hash for fields to include in the response payload;
|
37
|
+
#
|
38
|
+
# e.g.: { user: %w[id name username] }
|
39
|
+
# @return [Array<Hash>] of user objects with the requested user fields
|
40
|
+
def get_users_by_usernames(usernames:, fields: DEFAULT_FIELDS)
|
41
|
+
url = 'https://api.twitter.com/labs/2/users/by'
|
42
|
+
params = ParamsService.from_fields(fields)
|
43
|
+
params.merge!({ usernames: usernames.join(',') })
|
44
|
+
|
45
|
+
make_request(url: url, params: params, is_collection: true)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class ParamsService
|
2
|
+
# Convert user-passed fields hash to query params
|
3
|
+
# @param [Hash] :fields A hash of requested fields; see API Reference for details
|
4
|
+
# @return [Hash] A hash of query params for consumption by API client
|
5
|
+
# @example
|
6
|
+
# input: { tweet: %w[id username], media: ['url'] }
|
7
|
+
# output: { 'tweet.fields' => 'id,username', 'media.fields' => 'url }
|
8
|
+
def self.from_fields(fields)
|
9
|
+
fields.keys.inject({}) do |memo, key|
|
10
|
+
memo["#{key}.fields"] = fields[key].join(',')
|
11
|
+
|
12
|
+
memo
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = '0.
|
1
|
+
module TwitterLabsAPI
|
2
|
+
VERSION = '0.7.0'.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.7.0
|
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-08-27 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,13 @@ 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
|
93
|
+
- lib/twitter_labs_api/services/params_service.rb
|
58
94
|
- lib/twitter_labs_api/version.rb
|
59
95
|
- twitter-labs-api.gemspec
|
60
96
|
homepage: https://github.com/tomholford/twitter-labs-api
|