twttr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twttr
4
+ class Client
5
+ module Endpoint
6
+ module V2
7
+ V2_PATH = '/2'
8
+ end
9
+ end
10
+ end
11
+ end
12
+
13
+ require_relative 'v2/users'
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'twttr/client/endpoint/v2'
4
+
5
+ module Twttr
6
+ class Client
7
+ #  Namespace to keep endpoints organized by version and domain.
8
+ module Endpoint
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Twttr
4
+ class Client
5
+ #  Client rrrors namesace
6
+ module Error
7
+ #  HTTP related errors
8
+ class HTTPError < StandardError
9
+ def initialize(msg = 'HTTP Error')
10
+ super
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -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
@@ -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
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'twttr/model/user'
4
+
5
+ module Twttr
6
+ # Ruby representations of Twitter entities
7
+ module Model
8
+ end
9
+ end
data/lib/twttr.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'twttr/client'
4
+ require 'twttr/model'
5
+
6
+ #  Library namespace
7
+ module Twttr
8
+ end
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: []