twttr 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/twttr/client/config.rb +15 -0
- data/lib/twttr/client/endpoint/v2/users/follows.rb +47 -0
- data/lib/twttr/client/endpoint/v2/users.rb +43 -0
- data/lib/twttr/client/endpoint/v2.rb +13 -0
- data/lib/twttr/client/endpoint.rb +11 -0
- data/lib/twttr/client/error.rb +15 -0
- data/lib/twttr/client/oauth_request.rb +84 -0
- data/lib/twttr/client.rb +51 -0
- data/lib/twttr/model/user.rb +27 -0
- data/lib/twttr/model.rb +9 -0
- data/lib/twttr.rb +8 -0
- metadata +82 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0e5bcf2caf8b35685648f7ea2f2e3ded03f29a2d645c3fefd0dd201c40a29063
|
4
|
+
data.tar.gz: e4abe1c561235477fa18e53a99712f6c81dff499a084597f05c076a7db47bdc1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ba330361302ab0f8cb28ab17235baae529674fd02b766a6e55c5e48b10fd960d682b4efcb05d594216cf40288606fb87f77b50fbe8f2ef66cfde75d2a075165a
|
7
|
+
data.tar.gz: '04890ed05e20b2e710d3a3f7697b21823542da9e8e88e35e9361a73251d89b1a18a12a0dddd3dbf031cf390a3d344894b775cb80daf1b37978993c8e2d3ad6f5'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Twttr
|
4
|
+
class Client
|
5
|
+
# Client Configuration
|
6
|
+
class Config
|
7
|
+
attr_accessor :consumer_key, :consumer_secret, :access_token, :access_token_secret
|
8
|
+
attr_reader :user_fields
|
9
|
+
|
10
|
+
def user_fields=(fields)
|
11
|
+
@user_fields = fields.join(',')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Twttr
|
4
|
+
class Client
|
5
|
+
module Endpoint
|
6
|
+
module V2
|
7
|
+
module Users
|
8
|
+
# Twitter API V2 User Follow related endpoints
|
9
|
+
# https://developer.twitter.com/en/docs/twitter-api/users/follows/api-reference
|
10
|
+
module Follows
|
11
|
+
FOLLOWING_PATH = "#{Users::USER_PATH}/following"
|
12
|
+
|
13
|
+
# GET /2/users/:id/following
|
14
|
+
# https://developer.twitter.com/en/docs/twitter-api/users/follows/api-reference/get-users-id-following
|
15
|
+
#
|
16
|
+
# @param user_id [String] The user ID whose following you would like to retrieve.
|
17
|
+
# @param max_results [Integer] Max number of results per peage.
|
18
|
+
# @param pagination_token [String] Initial page pagination token.
|
19
|
+
# @yield [Array<Twttr::Model::User>] Users followed by page.
|
20
|
+
# @return [Array<Twttr::Model::User>] Users followed.
|
21
|
+
# @return [String,NilClass] Pagination token.
|
22
|
+
def following(user_id, max_results: nil, pagination_token: nil) # rubocop:disable Metrics/MethodLength
|
23
|
+
loop do
|
24
|
+
response = get(FOLLOWING_PATH, params: { user_id: user_id },
|
25
|
+
query_params: {
|
26
|
+
'user.fields': config.user_fields,
|
27
|
+
max_results: max_results,
|
28
|
+
pagination_token: pagination_token
|
29
|
+
}.compact)
|
30
|
+
|
31
|
+
users = response['data'].map { |v| Model::User.new(v, self) }
|
32
|
+
|
33
|
+
pagination_token = response['meta']['pagination_token']
|
34
|
+
|
35
|
+
return users, pagination_token unless block_given?
|
36
|
+
|
37
|
+
yield users, pagination_token
|
38
|
+
|
39
|
+
break if pagination_token.nil?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Twttr
|
4
|
+
class Client
|
5
|
+
module Endpoint
|
6
|
+
module V2
|
7
|
+
# Twitter API V2 Users related endpoints
|
8
|
+
# https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference
|
9
|
+
module Users
|
10
|
+
ME_PATH = "#{V2::V2_PATH}/users/me"
|
11
|
+
USERS_PATH = "#{V2::V2_PATH}/users"
|
12
|
+
USER_BY_USERNAME_PATH = "#{V2::V2_PATH}/users/by/username/%<username>s"
|
13
|
+
USER_PATH = "#{V2::V2_PATH}/users/%<user_id>s"
|
14
|
+
|
15
|
+
def me
|
16
|
+
response = get(ME_PATH, query_params: { 'user.fields': config.user_fields })
|
17
|
+
Model::User.new(response['data'], self)
|
18
|
+
end
|
19
|
+
|
20
|
+
def user(user_id)
|
21
|
+
response = get(USER_PATH, params: { user_id: user_id },
|
22
|
+
query_params: { 'user.fields': config.user_fields })
|
23
|
+
Model::User.new(response['data'], self)
|
24
|
+
end
|
25
|
+
|
26
|
+
def user_by_username(username)
|
27
|
+
response = get(USER_BY_USERNAME_PATH, params: { username: username },
|
28
|
+
query_params: { 'user.fields': config.user_fields })
|
29
|
+
Model::User.new(response['data'], self)
|
30
|
+
end
|
31
|
+
|
32
|
+
def users(user_ids)
|
33
|
+
response = get(USERS_PATH,
|
34
|
+
query_params: { ids: user_ids.join(','), 'user.fields': config.user_fields })
|
35
|
+
response['data'].map { |v| Model::User.new(v, self) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
require_relative 'users/follows'
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Twttr
|
4
|
+
class Client
|
5
|
+
# OAuth helper methods
|
6
|
+
class OAuthRequest
|
7
|
+
attr_reader :response
|
8
|
+
|
9
|
+
def initialize(uri, config)
|
10
|
+
@uri = uri
|
11
|
+
@config = config
|
12
|
+
@request = Net::HTTP::Get.new(uri)
|
13
|
+
@request['Authorization'] = authorization_header
|
14
|
+
@response = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.get(uri, config)
|
18
|
+
new(uri, config).perform
|
19
|
+
end
|
20
|
+
|
21
|
+
def perform
|
22
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
|
23
|
+
# :nocov:
|
24
|
+
self.response = http.request request
|
25
|
+
# :nocov:
|
26
|
+
end
|
27
|
+
|
28
|
+
raise Error::HTTPError, response.message unless response.instance_of?(Net::HTTPOK)
|
29
|
+
|
30
|
+
response
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_reader :config, :request, :uri
|
36
|
+
attr_writer :response
|
37
|
+
|
38
|
+
def oauth_params
|
39
|
+
@oauth_params ||= {
|
40
|
+
'oauth_consumer_key' => CGI.escape(config.consumer_key),
|
41
|
+
'oauth_nonce' => CGI.escape(SecureRandom.urlsafe_base64(32)),
|
42
|
+
'oauth_signature_method' => CGI.escape('HMAC-SHA1'),
|
43
|
+
'oauth_timestamp' => CGI.escape(Time.now.to_i.to_s),
|
44
|
+
'oauth_token' => CGI.escape(config.access_token),
|
45
|
+
'oauth_version' => CGI.escape('1.0')
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
def request_params
|
50
|
+
@request_params if defined?(@request_params)
|
51
|
+
|
52
|
+
query_params = {}
|
53
|
+
uri.query_params.each_pair do |key, value|
|
54
|
+
query_params[CGI.escape(key)] = CGI.escape(value)
|
55
|
+
end
|
56
|
+
|
57
|
+
@request_params = oauth_params.merge(query_params)
|
58
|
+
end
|
59
|
+
|
60
|
+
def authorization_header
|
61
|
+
oauth_params['oauth_signature'] = oauth_signature
|
62
|
+
|
63
|
+
serialized_params = oauth_params.keys.sort.map { |k| "#{k}=\"#{oauth_params[k]}\"" }.join(',')
|
64
|
+
|
65
|
+
"OAuth #{serialized_params}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def oauth_signature
|
69
|
+
signature_params = request_params.keys.sort.map { |k| "#{k}=#{request_params[k]}" }.join('&')
|
70
|
+
|
71
|
+
base_string = "#{request.method}&#{CGI.escape(request.uri.to_s.sub(/\?.*$/,
|
72
|
+
''))}&#{CGI.escape(signature_params)}"
|
73
|
+
|
74
|
+
auth_code(base_string)
|
75
|
+
end
|
76
|
+
|
77
|
+
def auth_code(base_string)
|
78
|
+
signin_key = "#{CGI.escape(config.consumer_secret)}&#{CGI.escape(config.access_token_secret)}"
|
79
|
+
|
80
|
+
CGI.escape(Base64.encode64(OpenSSL::HMAC.digest('sha1', signin_key, base_string).to_s).chomp)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/twttr/client.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require 'json'
|
5
|
+
require 'net/http'
|
6
|
+
require 'oauth'
|
7
|
+
require 'oauth/request_proxy/net_http'
|
8
|
+
require 'ostruct'
|
9
|
+
require 'securerandom'
|
10
|
+
require 'uri/query_params'
|
11
|
+
|
12
|
+
require 'twttr/client/config'
|
13
|
+
require 'twttr/client/endpoint'
|
14
|
+
require 'twttr/client/error'
|
15
|
+
require 'twttr/client/oauth_request'
|
16
|
+
|
17
|
+
require 'uri/generic'
|
18
|
+
|
19
|
+
module Twttr
|
20
|
+
# Twitter API Client
|
21
|
+
class Client
|
22
|
+
include Twttr::Client::Endpoint::V2::Users
|
23
|
+
include Twttr::Client::Endpoint::V2::Users::Follows
|
24
|
+
|
25
|
+
attr_reader :config
|
26
|
+
|
27
|
+
BASE_URL = 'https://api.twitter.com'
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@config = Config.new
|
31
|
+
yield config if block_given?
|
32
|
+
end
|
33
|
+
|
34
|
+
def get(path, params: {}, query_params: {})
|
35
|
+
uri = uri_for(path, params)
|
36
|
+
uri.query = URI.encode_www_form(query_params.compact) unless query_params.compact.empty?
|
37
|
+
|
38
|
+
response = OAuthRequest.get(uri, config)
|
39
|
+
|
40
|
+
JSON.parse(response.body)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def uri_for(path, params = {})
|
46
|
+
return URI.parse("#{BASE_URL}#{path}") if params.empty?
|
47
|
+
|
48
|
+
URI.parse("#{BASE_URL}#{path}" % params) # rubocop:disable Style/FormatString
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Twttr
|
4
|
+
module Model
|
5
|
+
# Twitter User representation
|
6
|
+
# https://developer.twitter.com/en/docs/twitter-api/data-dictionary/object-model/user
|
7
|
+
class User
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def_delegators :@data, :created_at, :description, :entities, :id, :location, :name, :pinned_tweet_id,
|
11
|
+
:profile_image_url, :protected, :public_metrics, :url, :username, :verified, :withheld
|
12
|
+
|
13
|
+
def initialize(data, client = nil)
|
14
|
+
@data = JSON.parse(data.to_json, object_class: OpenStruct)
|
15
|
+
@client = client
|
16
|
+
end
|
17
|
+
|
18
|
+
def following(pagination_token: nil, &block)
|
19
|
+
client.following(id, pagination_token: pagination_token, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :client
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/twttr/model.rb
ADDED
data/lib/twttr.rb
ADDED
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: twttr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Roberto Decurnex
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-12-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: oauth
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.5.8
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.5.8
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: uri-query_params
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.7.2
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.7.2
|
41
|
+
description: Modular Twitter API interface, initially targeting Twitter API v2
|
42
|
+
email: roberto@decurnex.io
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- lib/twttr.rb
|
48
|
+
- lib/twttr/client.rb
|
49
|
+
- lib/twttr/client/config.rb
|
50
|
+
- lib/twttr/client/endpoint.rb
|
51
|
+
- lib/twttr/client/endpoint/v2.rb
|
52
|
+
- lib/twttr/client/endpoint/v2/users.rb
|
53
|
+
- lib/twttr/client/endpoint/v2/users/follows.rb
|
54
|
+
- lib/twttr/client/error.rb
|
55
|
+
- lib/twttr/client/oauth_request.rb
|
56
|
+
- lib/twttr/model.rb
|
57
|
+
- lib/twttr/model/user.rb
|
58
|
+
homepage: https://rubygems.org/gems/twttr
|
59
|
+
licenses:
|
60
|
+
- MIT
|
61
|
+
metadata:
|
62
|
+
rubygems_mfa_required: 'true'
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '2.7'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements: []
|
78
|
+
rubygems_version: 3.2.33
|
79
|
+
signing_key:
|
80
|
+
specification_version: 4
|
81
|
+
summary: Twitter API v2 Interface
|
82
|
+
test_files: []
|