x 0.9.1 → 0.10.0
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/CHANGELOG.md +4 -1
- data/lib/x/client.rb +3 -0
- data/lib/x/connection.rb +21 -7
- data/lib/x/errors/error.rb +1 -4
- data/lib/x/errors/internal_server_error.rb +5 -0
- data/lib/x/errors/network_error.rb +2 -2
- data/lib/x/errors/payload_too_large_error.rb +5 -0
- data/lib/x/media_upload.rb +125 -0
- data/lib/x/redirect_handler.rb +1 -3
- data/lib/x/request_builder.rb +7 -0
- data/lib/x/response_handler.rb +13 -4
- data/lib/x/version.rb +1 -1
- data/sig/x.rbs +86 -11
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4a01e8a21951f3b998cfae0253c4d35c5412ac39f94681283b9059d4a196651e
|
|
4
|
+
data.tar.gz: 5b4a5bb02d86b27391b005acb329893025222c20e9d0c99b88ed0d7e6322dc5e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8972eafdb4c041a61258f409fef5a93dc0ff56b70e5c8dd384e126652c1ab32a4e52dc56bd253d7c204ea6313e3ac321e93c7245c722c0d5491b59443c63435a
|
|
7
|
+
data.tar.gz: 4a5dfb958b61b78cdcf9312250d3ba00182894241da1c3e9114f08f1029fb174be07043f607a864986e3d09d6d308dfac4d13549e2483d5746235b34c4dd05a2
|
data/CHANGELOG.md
CHANGED
data/lib/x/client.rb
CHANGED
|
@@ -19,6 +19,9 @@ module X
|
|
|
19
19
|
def_delegators :@request_builder, :content_type=, :user_agent=
|
|
20
20
|
def_delegators :@response_handler, :array_class, :object_class
|
|
21
21
|
def_delegators :@response_handler, :array_class=, :object_class=
|
|
22
|
+
alias_method :base_url, :base_uri
|
|
23
|
+
alias_method :base_url=, :base_uri=
|
|
24
|
+
attr_accessor :authenticator, :connection, :request_builder, :redirect_handler, :response_handler
|
|
22
25
|
|
|
23
26
|
def initialize(bearer_token: nil,
|
|
24
27
|
api_key: nil, api_key_secret: nil, access_token: nil, access_token_secret: nil,
|
data/lib/x/connection.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require "forwardable"
|
|
2
2
|
require "net/http"
|
|
3
|
+
require "openssl"
|
|
3
4
|
require "uri"
|
|
4
5
|
require_relative "errors/network_error"
|
|
5
6
|
|
|
@@ -16,8 +17,10 @@ module X
|
|
|
16
17
|
DEFAULT_WRITE_TIMEOUT = 60 # seconds
|
|
17
18
|
NETWORK_ERRORS = [
|
|
18
19
|
Errno::ECONNREFUSED,
|
|
20
|
+
Errno::ECONNRESET,
|
|
19
21
|
Net::OpenTimeout,
|
|
20
|
-
Net::ReadTimeout
|
|
22
|
+
Net::ReadTimeout,
|
|
23
|
+
OpenSSL::SSL::SSLError
|
|
21
24
|
].freeze
|
|
22
25
|
|
|
23
26
|
attr_reader :base_uri, :proxy_uri, :http_client
|
|
@@ -39,9 +42,9 @@ module X
|
|
|
39
42
|
end
|
|
40
43
|
|
|
41
44
|
def send_request(request)
|
|
42
|
-
@http_client.request(request)
|
|
45
|
+
response = @http_client.request(request)
|
|
43
46
|
rescue *NETWORK_ERRORS => e
|
|
44
|
-
raise NetworkError
|
|
47
|
+
raise NetworkError.new("Network error: #{e.message}", response: response)
|
|
45
48
|
end
|
|
46
49
|
|
|
47
50
|
def base_uri=(base_url)
|
|
@@ -56,6 +59,17 @@ module X
|
|
|
56
59
|
@http_client.instance_variable_get(:@debug_output)
|
|
57
60
|
end
|
|
58
61
|
|
|
62
|
+
def configuration
|
|
63
|
+
{
|
|
64
|
+
base_url: base_uri.to_s,
|
|
65
|
+
open_timeout: open_timeout,
|
|
66
|
+
read_timeout: read_timeout,
|
|
67
|
+
write_timeout: write_timeout,
|
|
68
|
+
proxy_url: proxy_uri.to_s,
|
|
69
|
+
debug_output: debug_output
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
|
|
59
73
|
private
|
|
60
74
|
|
|
61
75
|
def apply_http_client_settings(open_timeout:, read_timeout:, write_timeout:, debug_output:)
|
|
@@ -84,15 +98,15 @@ module X
|
|
|
84
98
|
end
|
|
85
99
|
|
|
86
100
|
def build_http_client(host:, port:)
|
|
87
|
-
if @proxy_uri
|
|
88
|
-
Net::HTTP.new(host, port)
|
|
89
|
-
else
|
|
101
|
+
if defined?(@proxy_uri)
|
|
90
102
|
Net::HTTP.new(host, port, @proxy_uri&.host, @proxy_uri&.port, @proxy_uri&.user, @proxy_uri&.password)
|
|
103
|
+
else
|
|
104
|
+
Net::HTTP.new(host, port)
|
|
91
105
|
end
|
|
92
106
|
end
|
|
93
107
|
|
|
94
108
|
def conditionally_apply_http_client_settings
|
|
95
|
-
if @http_client
|
|
109
|
+
if defined?(@http_client)
|
|
96
110
|
settings = current_http_client_settings
|
|
97
111
|
yield
|
|
98
112
|
apply_http_client_settings(**settings)
|
data/lib/x/errors/error.rb
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
require "json"
|
|
2
|
-
require "net/http"
|
|
3
2
|
|
|
4
3
|
module X
|
|
5
4
|
# Base error class
|
|
6
5
|
class Error < ::StandardError
|
|
7
|
-
JSON_CONTENT_TYPE_REGEXP = %r{application/(problem\+|)json}
|
|
8
|
-
|
|
9
6
|
attr_reader :object
|
|
10
7
|
|
|
11
8
|
def initialize(msg, response:)
|
|
12
|
-
@object = JSON.parse(response.body
|
|
9
|
+
@object = JSON.parse(response.body) if response&.body && !response.body.empty?
|
|
13
10
|
super(msg)
|
|
14
11
|
end
|
|
15
12
|
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
require "securerandom"
|
|
2
|
+
|
|
3
|
+
module X
|
|
4
|
+
# Helper module for uploading images and videos
|
|
5
|
+
module MediaUpload
|
|
6
|
+
extend self
|
|
7
|
+
|
|
8
|
+
MAX_RETRIES = 3
|
|
9
|
+
BYTES_PER_MB = 1_048_576
|
|
10
|
+
MEDIA_CATEGORIES = %w[dm_gif dm_image dm_video subtitles tweet_gif tweet_image tweet_video].freeze
|
|
11
|
+
DM_GIF, DM_IMAGE, DM_VIDEO, SUBTITLES, TWEET_GIF, TWEET_IMAGE, TWEET_VIDEO = MEDIA_CATEGORIES
|
|
12
|
+
DEFAULT_MIME_TYPE = "application/octet-stream".freeze
|
|
13
|
+
MIME_TYPES = %w[image/gif image/jpeg video/mp4 image/png application/x-subrip image/webp].freeze
|
|
14
|
+
GIF_MIME_TYPE, JPEG_MIME_TYPE, MP4_MIME_TYPE, PNG_MIME_TYPE, SUBRIP_MIME_TYPE, WEBP_MIME_TYPE = MIME_TYPES
|
|
15
|
+
MIME_TYPE_MAP = {"gif" => GIF_MIME_TYPE, "jpg" => JPEG_MIME_TYPE, "jpeg" => JPEG_MIME_TYPE, "mp4" => MP4_MIME_TYPE,
|
|
16
|
+
"png" => PNG_MIME_TYPE, "srt" => SUBRIP_MIME_TYPE, "webp" => WEBP_MIME_TYPE}.freeze
|
|
17
|
+
|
|
18
|
+
def media_upload(client:, file_path:, media_category:, media_type: infer_media_type(file_path, media_category),
|
|
19
|
+
boundary: SecureRandom.hex)
|
|
20
|
+
validate!(file_path: file_path, media_category: media_category)
|
|
21
|
+
upload_client = client.dup.tap { |c| c.base_uri = "https://upload.twitter.com/1.1/" }
|
|
22
|
+
upload_body = construct_upload_body(file_path, media_type, boundary)
|
|
23
|
+
upload_client.content_type = "multipart/form-data, boundary=#{boundary}"
|
|
24
|
+
upload_client.post("media/upload.json?media_category=#{media_category}", upload_body)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def chunked_media_upload(client:, file_path:, media_category:, media_type: infer_media_type(file_path,
|
|
28
|
+
media_category), boundary: SecureRandom.hex, chunk_size_mb: 8)
|
|
29
|
+
validate!(file_path: file_path, media_category: media_category)
|
|
30
|
+
upload_client = client.dup.tap { |c| c.base_uri = "https://upload.twitter.com/1.1/" }
|
|
31
|
+
media = init(upload_client, file_path, media_type, media_category)
|
|
32
|
+
chunk_size = chunk_size_mb * BYTES_PER_MB
|
|
33
|
+
chunk_paths = split(file_path, chunk_size)
|
|
34
|
+
append(upload_client, chunk_paths, media, media_type, boundary)
|
|
35
|
+
upload_client.post("media/upload.json?command=FINALIZE&media_id=#{media["media_id"]}")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def await_processing(client:, media:)
|
|
39
|
+
upload_client = client.dup.tap { |c| c.base_uri = "https://upload.twitter.com/1.1/" }
|
|
40
|
+
loop do
|
|
41
|
+
status = upload_client.get("media/upload.json?command=STATUS&media_id=#{media["media_id"]}")
|
|
42
|
+
return status if status["processing_info"]["state"] == "succeeded"
|
|
43
|
+
|
|
44
|
+
sleep status["processing_info"]["check_after_secs"].to_i
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def validate!(file_path:, media_category:)
|
|
51
|
+
raise "File not found: #{file_path}" unless File.exist?(file_path)
|
|
52
|
+
|
|
53
|
+
return if MEDIA_CATEGORIES.include?(media_category.downcase)
|
|
54
|
+
|
|
55
|
+
raise ArgumentError, "Invalid media_category: #{media_category}. Valid values: #{MEDIA_CATEGORIES.join(", ")}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def infer_media_type(file_path, media_category)
|
|
59
|
+
case media_category.downcase
|
|
60
|
+
when TWEET_GIF, DM_GIF then GIF_MIME_TYPE
|
|
61
|
+
when TWEET_VIDEO, DM_VIDEO then MP4_MIME_TYPE
|
|
62
|
+
when SUBTITLES then SUBRIP_MIME_TYPE
|
|
63
|
+
else MIME_TYPE_MAP[File.extname(file_path).delete(".").downcase] || DEFAULT_MIME_TYPE
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def init(upload_client, file_path, media_type, media_category)
|
|
68
|
+
total_bytes = File.size(file_path)
|
|
69
|
+
query = "command=INIT&media_type=#{media_type}&media_category=#{media_category}&total_bytes=#{total_bytes}"
|
|
70
|
+
upload_client.post("media/upload.json?#{query}")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def split(file_path, chunk_size)
|
|
74
|
+
file_number = -1
|
|
75
|
+
|
|
76
|
+
[].tap do |chunk_paths|
|
|
77
|
+
File.open(file_path, "rb") do |f|
|
|
78
|
+
while (chunk = f.read(chunk_size))
|
|
79
|
+
chunk_paths << "#{Dir.mktmpdir}/x#{format("%03d", file_number += 1)}".tap do |path|
|
|
80
|
+
File.write(path, chunk)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def append(upload_client, chunk_paths, media, media_type, boundary = SecureRandom.hex)
|
|
88
|
+
threads = chunk_paths.map.with_index do |chunk_path, index|
|
|
89
|
+
Thread.new do
|
|
90
|
+
upload_body = construct_upload_body(chunk_path, media_type, boundary)
|
|
91
|
+
query = "command=APPEND&media_id=#{media["media_id"]}&segment_index=#{index}"
|
|
92
|
+
upload_chunk(upload_client, query, upload_body, chunk_path, boundary)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
threads.each(&:join)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def upload_chunk(upload_client, query, upload_body, chunk_path, boundary)
|
|
99
|
+
# Initialize a new client to avoid shared connection issues
|
|
100
|
+
client = upload_client.dup
|
|
101
|
+
client.connection = Connection.new(**upload_client.connection.configuration.merge(base_url: "https://upload.twitter.com/1.1/"))
|
|
102
|
+
client.content_type = "multipart/form-data, boundary=#{boundary}"
|
|
103
|
+
client.post("media/upload.json?#{query}", upload_body)
|
|
104
|
+
rescue NetworkError, ServerError
|
|
105
|
+
retries ||= 0
|
|
106
|
+
((retries += 1) < MAX_RETRIES) ? retry : raise
|
|
107
|
+
ensure
|
|
108
|
+
cleanup_chunk(chunk_path)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def cleanup_chunk(chunk_path)
|
|
112
|
+
dirname = File.dirname(chunk_path)
|
|
113
|
+
File.delete(chunk_path)
|
|
114
|
+
Dir.delete(dirname) if Dir.empty?(dirname)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def construct_upload_body(file_path, media_type, boundary = SecureRandom.hex)
|
|
118
|
+
"--#{boundary}\r\n" \
|
|
119
|
+
"Content-Disposition: form-data; name=\"media\"; filename=\"#{File.basename(file_path)}\"\r\n" \
|
|
120
|
+
"Content-Type: #{media_type}\r\n\r\n" \
|
|
121
|
+
"#{File.read(file_path)}\r\n" \
|
|
122
|
+
"--#{boundary}--\r\n"
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
data/lib/x/redirect_handler.rb
CHANGED
|
@@ -47,9 +47,7 @@ module X
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def send_new_request(new_uri, new_request)
|
|
50
|
-
@connection = Connection.new(base_url: new_uri
|
|
51
|
-
read_timeout: connection.read_timeout, write_timeout: connection.write_timeout,
|
|
52
|
-
debug_output: connection.debug_output, proxy_url: connection.proxy_uri)
|
|
50
|
+
@connection = Connection.new(**connection.configuration.merge(base_url: new_uri))
|
|
53
51
|
connection.send_request(new_request)
|
|
54
52
|
end
|
|
55
53
|
end
|
data/lib/x/request_builder.rb
CHANGED
data/lib/x/response_handler.rb
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
require "json"
|
|
2
2
|
require "net/http"
|
|
3
|
-
require_relative "errors/bad_request_error"
|
|
4
3
|
require_relative "errors/authentication_error"
|
|
4
|
+
require_relative "errors/bad_request_error"
|
|
5
5
|
require_relative "errors/forbidden_error"
|
|
6
6
|
require_relative "errors/not_found_error"
|
|
7
|
-
require_relative "errors/
|
|
8
|
-
require_relative "errors/
|
|
7
|
+
require_relative "errors/payload_too_large_error"
|
|
8
|
+
require_relative "errors/internal_server_error"
|
|
9
9
|
require_relative "errors/service_unavailable_error"
|
|
10
|
+
require_relative "errors/too_many_requests_error"
|
|
10
11
|
|
|
11
12
|
module X
|
|
12
13
|
# Process HTTP responses
|
|
@@ -18,8 +19,9 @@ module X
|
|
|
18
19
|
401 => AuthenticationError,
|
|
19
20
|
403 => ForbiddenError,
|
|
20
21
|
404 => NotFoundError,
|
|
22
|
+
413 => PayloadTooLargeError,
|
|
21
23
|
429 => TooManyRequestsError,
|
|
22
|
-
500 =>
|
|
24
|
+
500 => InternalServerError,
|
|
23
25
|
503 => ServiceUnavailableError
|
|
24
26
|
}.freeze
|
|
25
27
|
JSON_CONTENT_TYPE_REGEXP = %r{application/(problem\+|)json}
|
|
@@ -41,6 +43,13 @@ module X
|
|
|
41
43
|
end
|
|
42
44
|
end
|
|
43
45
|
|
|
46
|
+
def configuration
|
|
47
|
+
{
|
|
48
|
+
array_class: array_class,
|
|
49
|
+
object_class: object_class
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
|
|
44
53
|
private
|
|
45
54
|
|
|
46
55
|
def success?(response)
|
data/lib/x/version.rb
CHANGED
data/sig/x.rbs
CHANGED
|
@@ -36,8 +36,8 @@ module X
|
|
|
36
36
|
class Error < StandardError
|
|
37
37
|
JSON_CONTENT_TYPE_REGEXP: Regexp
|
|
38
38
|
|
|
39
|
-
attr_reader object:
|
|
40
|
-
def initialize: (String msg, response: Net::HTTPResponse) -> void
|
|
39
|
+
attr_reader object: untyped
|
|
40
|
+
def initialize: (String msg, response: Net::HTTPResponse?) -> void
|
|
41
41
|
|
|
42
42
|
private
|
|
43
43
|
def json_response?: (Net::HTTPResponse response) -> bool
|
|
@@ -55,9 +55,15 @@ module X
|
|
|
55
55
|
class ForbiddenError < ClientError
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
+
class InternalServerError < ServerError
|
|
59
|
+
end
|
|
60
|
+
|
|
58
61
|
class NotFoundError < ClientError
|
|
59
62
|
end
|
|
60
63
|
|
|
64
|
+
class PayloadTooLargeError < ClientError
|
|
65
|
+
end
|
|
66
|
+
|
|
61
67
|
class TooManyRedirectsError < ClientError
|
|
62
68
|
end
|
|
63
69
|
|
|
@@ -87,7 +93,8 @@ module X
|
|
|
87
93
|
DEFAULT_OPEN_TIMEOUT: Integer
|
|
88
94
|
DEFAULT_READ_TIMEOUT: Integer
|
|
89
95
|
DEFAULT_WRITE_TIMEOUT: Integer
|
|
90
|
-
NETWORK_ERRORS: Array[(singleton(
|
|
96
|
+
NETWORK_ERRORS: Array[(singleton(Errno::ECONNREFUSED) | singleton(Errno::ECONNRESET) | singleton(Net::OpenTimeout) | singleton(Net::ReadTimeout) | singleton(OpenSSL::SSL::SSLError))]
|
|
97
|
+
|
|
91
98
|
extend Forwardable
|
|
92
99
|
@http_client: Net::HTTP
|
|
93
100
|
|
|
@@ -100,6 +107,7 @@ module X
|
|
|
100
107
|
def send_request: (Net::HTTPRequest request) -> Net::HTTPResponse
|
|
101
108
|
def base_uri=: (URI::Generic | String base_url) -> void
|
|
102
109
|
def debug_output: -> IO?
|
|
110
|
+
def configuration: -> Hash[Symbol, untyped]
|
|
103
111
|
|
|
104
112
|
private
|
|
105
113
|
def apply_http_client_settings: (open_timeout: Float | Integer, read_timeout: Float | Integer, write_timeout: Float | Integer, debug_output: IO?) -> untyped
|
|
@@ -121,6 +129,7 @@ module X
|
|
|
121
129
|
attr_accessor user_agent: String
|
|
122
130
|
def initialize: (?content_type: String, ?user_agent: String) -> void
|
|
123
131
|
def build: (BearerTokenAuthenticator | OauthAuthenticator authenticator, Symbol http_method, URI::Generic uri, ?body: String?) -> (Net::HTTPRequest)
|
|
132
|
+
def configuration: -> Hash[Symbol, untyped]
|
|
124
133
|
|
|
125
134
|
private
|
|
126
135
|
def create_request: (Symbol http_method, URI::Generic uri, String? body) -> (Net::HTTPRequest)
|
|
@@ -149,13 +158,14 @@ module X
|
|
|
149
158
|
class ResponseHandler
|
|
150
159
|
DEFAULT_ARRAY_CLASS: Class
|
|
151
160
|
DEFAULT_OBJECT_CLASS: Class
|
|
152
|
-
ERROR_CLASSES: Hash[Integer, singleton(AuthenticationError) | singleton(BadRequestError) | singleton(ForbiddenError) | singleton(NotFoundError) | singleton(
|
|
161
|
+
ERROR_CLASSES: Hash[Integer, singleton(AuthenticationError) | singleton(BadRequestError) | singleton(ForbiddenError) | singleton(InternalServerError) | singleton(NotFoundError) | singleton(PayloadTooLargeError) | singleton(ServiceUnavailableError) | singleton(TooManyRequestsError)]
|
|
153
162
|
JSON_CONTENT_TYPE_REGEXP: Regexp
|
|
154
163
|
|
|
155
164
|
attr_accessor array_class: Class
|
|
156
165
|
attr_accessor object_class: Class
|
|
157
166
|
def initialize: (?array_class: Class, ?object_class: Class) -> void
|
|
158
|
-
def handle: (Net::HTTPResponse response) ->
|
|
167
|
+
def handle: (Net::HTTPResponse response) -> untyped
|
|
168
|
+
def configuration: -> Hash[Symbol, Class]
|
|
159
169
|
|
|
160
170
|
private
|
|
161
171
|
def success?: (Net::HTTPResponse response) -> bool
|
|
@@ -170,15 +180,80 @@ module X
|
|
|
170
180
|
@redirect_handler: RedirectHandler
|
|
171
181
|
@response_handler: ResponseHandler
|
|
172
182
|
|
|
173
|
-
|
|
183
|
+
attr_accessor access_token: String
|
|
184
|
+
attr_accessor access_token_secret: String
|
|
185
|
+
attr_accessor api_key: String
|
|
186
|
+
attr_accessor api_key_secret: String
|
|
187
|
+
attr_accessor bearer_token: String
|
|
188
|
+
attr_accessor base_uri: URI::Generic | String
|
|
189
|
+
attr_accessor open_timeout: Float | Integer
|
|
190
|
+
attr_accessor read_timeout: Float | Integer
|
|
191
|
+
attr_accessor write_timeout: Float | Integer
|
|
192
|
+
attr_accessor proxy_url: String
|
|
193
|
+
attr_accessor content_type: String
|
|
194
|
+
attr_accessor user_agent: String
|
|
195
|
+
attr_accessor debug_output: IO?
|
|
196
|
+
attr_accessor array_class: Class
|
|
197
|
+
attr_accessor object_class: Class
|
|
198
|
+
attr_accessor max_redirects: Integer
|
|
199
|
+
attr_accessor authenticator: BearerTokenAuthenticator | OauthAuthenticator
|
|
200
|
+
attr_accessor connection: Connection
|
|
201
|
+
attr_accessor request_builder: RequestBuilder
|
|
202
|
+
attr_accessor redirect_handler: RedirectHandler
|
|
203
|
+
attr_accessor response_handler: ResponseHandler
|
|
204
|
+
alias base_url base_uri
|
|
205
|
+
|
|
206
|
+
|
|
174
207
|
def initialize: (?bearer_token: String?, ?api_key: String?, ?api_key_secret: String?, ?access_token: String?, ?access_token_secret: String?, ?base_url: URI::Generic | String, ?content_type: String, ?user_agent: String, ?open_timeout: Float | Integer, ?read_timeout: Float | Integer, ?write_timeout: Float | Integer, ?proxy_url: URI::Generic? | String?, ?debug_output: IO?, ?array_class: Class, ?object_class: Class, ?max_redirects: Integer) -> void
|
|
175
|
-
def get: (String endpoint) ->
|
|
176
|
-
def post: (String endpoint, ?
|
|
177
|
-
def put: (String endpoint, ?
|
|
178
|
-
def delete: (String endpoint) ->
|
|
208
|
+
def get: (String endpoint) -> untyped
|
|
209
|
+
def post: (String endpoint, ?String? body) -> untyped
|
|
210
|
+
def put: (String endpoint, ?String? body) -> untyped
|
|
211
|
+
def delete: (String endpoint) -> untyped
|
|
179
212
|
|
|
180
213
|
private
|
|
181
214
|
def initialize_authenticator: (String? bearer_token, String? api_key, String? api_key_secret, String? access_token, String? access_token_secret) -> (BearerTokenAuthenticator | OauthAuthenticator)
|
|
182
|
-
def send_request: (Symbol http_method, String endpoint, ?
|
|
215
|
+
def send_request: (Symbol http_method, String endpoint, ?String? body) -> untyped
|
|
183
216
|
end
|
|
217
|
+
|
|
218
|
+
module MediaUpload
|
|
219
|
+
MAX_RETRIES: Integer
|
|
220
|
+
BYTES_PER_MB: Integer
|
|
221
|
+
MEDIA_CATEGORIES: Array[String]
|
|
222
|
+
DM_GIF: String
|
|
223
|
+
DM_IMAGE: String
|
|
224
|
+
DM_VIDEO: String
|
|
225
|
+
SUBTITLES: String
|
|
226
|
+
TWEET_GIF: String
|
|
227
|
+
TWEET_IMAGE: String
|
|
228
|
+
TWEET_VIDEO: String
|
|
229
|
+
DEFAULT_MIME_TYPE: String
|
|
230
|
+
MIME_TYPES: Array[String]
|
|
231
|
+
GIF_MIME_TYPE: String
|
|
232
|
+
JPEG_MIME_TYPE: String
|
|
233
|
+
MP4_MIME_TYPE: String
|
|
234
|
+
PNG_MIME_TYPE: String
|
|
235
|
+
SUBRIP_MIME_TYPE: String
|
|
236
|
+
WEBP_MIME_TYPE: String
|
|
237
|
+
MIME_TYPE_MAP: Hash[String, String]
|
|
238
|
+
extend MediaUpload
|
|
239
|
+
|
|
240
|
+
def media_upload: (client: Client, file_path: String, media_category: String, ?media_type: String, ?boundary: String) -> untyped
|
|
241
|
+
def chunked_media_upload: (client: Client, file_path: String, media_category: String, ?media_type: String, ?boundary: String, ?chunk_size_mb: Integer) -> untyped
|
|
242
|
+
def await_processing: (client: Client, media: untyped) -> untyped
|
|
243
|
+
|
|
244
|
+
private
|
|
245
|
+
def validate!: (file_path: String, media_category: String) -> nil
|
|
246
|
+
def infer_media_type: (String file_path, String media_category) -> String
|
|
247
|
+
def init: (Client upload_client, String file_path, String media_type, String media_category) -> untyped
|
|
248
|
+
def split: (String file_path, Integer chunk_size) -> Array[String]
|
|
249
|
+
def append: (Client upload_client, Array[String] chunk_paths, untyped media, String media_type, ?String boundary) -> Array[String]
|
|
250
|
+
def upload_chunk: (Client upload_client, String query, String chunk_path, String media_type, String boundary) -> Integer?
|
|
251
|
+
def cleanup_chunk: (String chunk_path) -> Integer?
|
|
252
|
+
def finalize: (Client upload_client, untyped media) -> untyped
|
|
253
|
+
def construct_upload_body: (String file_path, String media_type, ?String boundary) -> String
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
class Dir
|
|
258
|
+
def self.mktmpdir: (?String? prefix_suffix) -> String
|
|
184
259
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: x
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Erik Berlin
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-10-
|
|
11
|
+
date: 2023-10-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description:
|
|
14
14
|
email:
|
|
@@ -31,12 +31,15 @@ files:
|
|
|
31
31
|
- lib/x/errors/client_error.rb
|
|
32
32
|
- lib/x/errors/error.rb
|
|
33
33
|
- lib/x/errors/forbidden_error.rb
|
|
34
|
+
- lib/x/errors/internal_server_error.rb
|
|
34
35
|
- lib/x/errors/network_error.rb
|
|
35
36
|
- lib/x/errors/not_found_error.rb
|
|
37
|
+
- lib/x/errors/payload_too_large_error.rb
|
|
36
38
|
- lib/x/errors/server_error.rb
|
|
37
39
|
- lib/x/errors/service_unavailable_error.rb
|
|
38
40
|
- lib/x/errors/too_many_redirects_error.rb
|
|
39
41
|
- lib/x/errors/too_many_requests_error.rb
|
|
42
|
+
- lib/x/media_upload.rb
|
|
40
43
|
- lib/x/oauth_authenticator.rb
|
|
41
44
|
- lib/x/redirect_handler.rb
|
|
42
45
|
- lib/x/request_builder.rb
|