telegem 2.0.0 → 2.0.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/lib/api/client.rb +58 -133
- data/lib/telegem.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5b3073c1e51978c6c7a09bc9ef4e28a0d262b15b23be16d6a81c9c69d11ef9dd
|
|
4
|
+
data.tar.gz: a4850c9a75a3009bf55c9aa587ab61e2a9fbf75f1d210762e103c64d5c044a7e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e259c050dd7d42564dc53e82266e7becc2089d7bf72ab14b1c85c7189215f36a543bc1c0ee6b677277cafc9661ac35057e4c2110260ad198397d49623a8c4cf7
|
|
7
|
+
data.tar.gz: c30b8f65dacb91c98d78a01ff359896f85d79295ca466667c8c60c5ea995fa82d8d28127c44b5490f64573524f3a9820dfe8b96ccc7a54a571d9a7e77377ce9d
|
data/lib/api/client.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# lib/api/client.rb -
|
|
1
|
+
# lib/api/client.rb - WORKING VERSION
|
|
2
2
|
require 'httpx'
|
|
3
3
|
require 'json'
|
|
4
4
|
|
|
@@ -7,89 +7,62 @@ module Telegem
|
|
|
7
7
|
class Client
|
|
8
8
|
BASE_URL = 'https://api.telegram.org'
|
|
9
9
|
|
|
10
|
-
attr_reader :token, :logger, :http
|
|
10
|
+
attr_reader :token, :logger, :http
|
|
11
11
|
|
|
12
|
-
def initialize(token,
|
|
12
|
+
def initialize(token, **options)
|
|
13
13
|
@token = token
|
|
14
|
-
@logger = logger || Logger.new($stdout)
|
|
14
|
+
@logger = options[:logger] || Logger.new($stdout)
|
|
15
|
+
timeout = options[:timeout] || 30
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
'User-Agent' => "Telegem/#{Telegem::VERSION} (Ruby #{RUBY_VERSION}; #{RUBY_PLATFORM})"
|
|
28
|
-
},
|
|
29
|
-
max_requests: pool_size # Connection pool
|
|
30
|
-
)
|
|
17
|
+
@http = HTTPX.with(
|
|
18
|
+
timeout: {
|
|
19
|
+
connect_timeout: 10,
|
|
20
|
+
write_timeout: 10,
|
|
21
|
+
read_timeout: timeout
|
|
22
|
+
},
|
|
23
|
+
headers: {
|
|
24
|
+
'Content-Type' => 'application/json',
|
|
25
|
+
'User-Agent' => "Telegem/#{Telegem::VERSION}"
|
|
26
|
+
}
|
|
27
|
+
)
|
|
31
28
|
|
|
32
|
-
# Add retry plugin if available
|
|
33
|
-
if HTTPX.plugins.key?(:retries)
|
|
34
|
-
@http = @http.plugin(:retries, max_retries: 3, retry_on: [500, 502, 503, 504])
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Ensure proper cleanup
|
|
38
29
|
ObjectSpace.define_finalizer(self, proc { close })
|
|
39
30
|
end
|
|
40
31
|
|
|
41
|
-
# PRIMARY API: Async call - returns HTTPX request (promise-like)
|
|
42
32
|
def call(method, params = {})
|
|
43
33
|
url = "#{BASE_URL}/bot#{@token}/#{method}"
|
|
44
34
|
|
|
45
|
-
@logger.debug("
|
|
35
|
+
@logger.debug("API: #{method}") if @logger
|
|
46
36
|
|
|
47
|
-
# Pure async - returns immediately
|
|
48
37
|
@http.post(url, json: params.compact)
|
|
49
|
-
.then(&method(:handle_response_async))
|
|
50
|
-
.on_error(&method(:handle_error_async))
|
|
51
38
|
end
|
|
52
39
|
|
|
53
|
-
|
|
54
|
-
def call!(method, params = {}, timeout: nil)
|
|
55
|
-
timeout ||= @http.options.timeout[:read_timeout]
|
|
56
|
-
|
|
57
|
-
# Start async request
|
|
40
|
+
def call!(method, params = {})
|
|
58
41
|
request = call(method, params)
|
|
42
|
+
request.wait
|
|
59
43
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if request.error
|
|
65
|
-
# Error already logged by handle_error_async
|
|
66
|
-
raise APIError, request.error.message
|
|
67
|
-
elsif !wait_result
|
|
68
|
-
raise NetworkError, "Request timeout after #{timeout}s"
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# Return the actual result (not the request object)
|
|
72
|
-
request.instance_variable_get(:@result) || request.response
|
|
73
|
-
rescue Timeout::Error
|
|
74
|
-
raise NetworkError, "Request timeout after #{timeout}s"
|
|
44
|
+
if request.error
|
|
45
|
+
handle_error(request.error)
|
|
46
|
+
return nil
|
|
75
47
|
end
|
|
48
|
+
|
|
49
|
+
handle_response(request.response)
|
|
76
50
|
end
|
|
77
51
|
|
|
78
|
-
# File upload - also async
|
|
79
52
|
def upload(method, params)
|
|
80
53
|
url = "#{BASE_URL}/bot#{@token}/#{method}"
|
|
81
54
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
55
|
+
form = params.map do |key, value|
|
|
56
|
+
if file_object?(value)
|
|
57
|
+
[key.to_s, HTTPX::FormData::File.new(value)]
|
|
58
|
+
else
|
|
59
|
+
[key.to_s, value.to_s]
|
|
60
|
+
end
|
|
61
|
+
end
|
|
86
62
|
|
|
87
63
|
@http.post(url, form: form)
|
|
88
|
-
.then(&method(:handle_response_async))
|
|
89
|
-
.on_error(&method(:handle_error_async))
|
|
90
64
|
end
|
|
91
65
|
|
|
92
|
-
# Convenience method for getUpdates with proper async handling
|
|
93
66
|
def get_updates(offset: nil, timeout: 30, limit: 100, allowed_updates: nil)
|
|
94
67
|
params = { timeout: timeout, limit: limit }
|
|
95
68
|
params[:offset] = offset if offset
|
|
@@ -98,95 +71,56 @@ module Telegem
|
|
|
98
71
|
call('getUpdates', params)
|
|
99
72
|
end
|
|
100
73
|
|
|
101
|
-
# Close connections gracefully
|
|
102
74
|
def close
|
|
103
75
|
@http.close
|
|
104
76
|
end
|
|
105
77
|
|
|
106
78
|
private
|
|
107
79
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
response.raise_for_status unless response.status == 200
|
|
80
|
+
def handle_response(response)
|
|
81
|
+
return nil unless response
|
|
111
82
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
# Store result on request for sync access
|
|
129
|
-
response.request.instance_variable_set(:@result, json['result'])
|
|
130
|
-
json['result']
|
|
131
|
-
else
|
|
132
|
-
raise APIError.new(json['description'], json['error_code'])
|
|
133
|
-
end
|
|
83
|
+
if response.status != 200
|
|
84
|
+
raise APIError, "HTTP #{response.status}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
begin
|
|
88
|
+
json = response.json
|
|
89
|
+
rescue JSON::ParserError
|
|
90
|
+
raise APIError, "Invalid JSON response"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
unless json
|
|
94
|
+
raise APIError, "Empty response"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
if json['ok']
|
|
98
|
+
json['result']
|
|
134
99
|
else
|
|
135
|
-
|
|
100
|
+
raise APIError.new(json['description'], json['error_code'])
|
|
136
101
|
end
|
|
137
102
|
end
|
|
138
103
|
|
|
139
|
-
|
|
140
|
-
def handle_error_async(error)
|
|
104
|
+
def handle_error(error)
|
|
141
105
|
case error
|
|
142
106
|
when HTTPX::TimeoutError
|
|
143
|
-
@logger.error("
|
|
144
|
-
raise NetworkError, "
|
|
107
|
+
@logger.error("Timeout: #{error.message}") if @logger
|
|
108
|
+
raise NetworkError, "Request timeout"
|
|
145
109
|
when HTTPX::ConnectionError
|
|
146
|
-
@logger.error("
|
|
147
|
-
raise NetworkError, "Connection failed
|
|
148
|
-
when HTTPX::HTTPError
|
|
149
|
-
@logger.error("🌐 HTTP #{error.response.status}: #{error.message}") if @logger
|
|
150
|
-
raise APIError, "HTTP #{error.response.status}: #{error.message}"
|
|
151
|
-
when RateLimitError
|
|
152
|
-
@logger.error("🚦 Rate limit: retry after #{error.retry_after}s") if @logger
|
|
153
|
-
raise error # Re-raise for user handling
|
|
110
|
+
@logger.error("Connection error: #{error.message}") if @logger
|
|
111
|
+
raise NetworkError, "Connection failed"
|
|
154
112
|
else
|
|
155
|
-
@logger.error("
|
|
113
|
+
@logger.error("Error: #{error.class}: #{error.message}") if @logger
|
|
156
114
|
raise APIError, error.message
|
|
157
115
|
end
|
|
158
116
|
end
|
|
159
117
|
|
|
160
|
-
def build_multipart_form(params)
|
|
161
|
-
params.map do |key, value|
|
|
162
|
-
if file_object?(value)
|
|
163
|
-
[key.to_s, HTTPX::FormData::File.new(value)]
|
|
164
|
-
else
|
|
165
|
-
[key.to_s, value.to_s]
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
|
|
170
118
|
def file_object?(obj)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
true
|
|
174
|
-
when Pathname
|
|
175
|
-
obj.exist? && obj.readable?
|
|
176
|
-
when String
|
|
177
|
-
# Check if it's a local file (not URL)
|
|
178
|
-
if obj.start_with?('http://', 'https://', 'ftp://')
|
|
179
|
-
false # Telegram handles URLs directly
|
|
180
|
-
else
|
|
181
|
-
File.exist?(obj) && File.readable?(obj)
|
|
182
|
-
end
|
|
183
|
-
else
|
|
184
|
-
false
|
|
185
|
-
end
|
|
119
|
+
obj.is_a?(File) || obj.is_a?(StringIO) || obj.is_a?(Tempfile) ||
|
|
120
|
+
(obj.is_a?(String) && File.exist?(obj))
|
|
186
121
|
end
|
|
187
122
|
end
|
|
188
123
|
|
|
189
|
-
# Custom Errors
|
|
190
124
|
class APIError < StandardError
|
|
191
125
|
attr_reader :code
|
|
192
126
|
|
|
@@ -197,14 +131,5 @@ module Telegem
|
|
|
197
131
|
end
|
|
198
132
|
|
|
199
133
|
class NetworkError < APIError; end
|
|
200
|
-
|
|
201
|
-
class RateLimitError < APIError
|
|
202
|
-
attr_reader :retry_after
|
|
203
|
-
|
|
204
|
-
def initialize(message, retry_after = 1)
|
|
205
|
-
super(message)
|
|
206
|
-
@retry_after = retry_after
|
|
207
|
-
end
|
|
208
|
-
end
|
|
209
134
|
end
|
|
210
135
|
end
|
data/lib/telegem.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: telegem
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- sick_phantom
|
|
@@ -198,7 +198,7 @@ metadata:
|
|
|
198
198
|
bug_tracker_uri: https://gitlab.com/ruby-telegem/telegem/-/issues
|
|
199
199
|
documentation_uri: https://gitlab.com/ruby-telegem/telegem/-/blob/main/README.md
|
|
200
200
|
rubygems_mfa_required: 'false'
|
|
201
|
-
post_install_message: "Thanks for installing Telegem 2.0.
|
|
201
|
+
post_install_message: "Thanks for installing Telegem 2.0.2!\n\nQuick start:\n bot
|
|
202
202
|
= Telegem.new(\"YOUR_TOKEN\")\n bot.on(:message) { |ctx| ctx.reply(\"Hello!\")
|
|
203
203
|
}\n bot.start_polling\n\nDocumentation: https://gitlab.com/ruby-telegem/telegem\nHappy
|
|
204
204
|
bot building! \U0001F916\n"
|