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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/api/client.rb +58 -133
  3. data/lib/telegem.rb +1 -1
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c55866ecfc21045fd47141b991f00a209cecd5c866fe52b73c2ffb302f0245ea
4
- data.tar.gz: 0a0e93a2c61487e759ecbc1e3ed2f86b1e77fbd993076acbebc6287f36584a52
3
+ metadata.gz: 5b3073c1e51978c6c7a09bc9ef4e28a0d262b15b23be16d6a81c9c69d11ef9dd
4
+ data.tar.gz: a4850c9a75a3009bf55c9aa587ab61e2a9fbf75f1d210762e103c64d5c044a7e
5
5
  SHA512:
6
- metadata.gz: 791184d5d43c592bbd66b844386112bd336ee4a7ab7bcfe30ce08eaca3a30f53eb819642c9ba5640084c4d17f91b81cee11cf006c4537175e2b86db2420c6db8
7
- data.tar.gz: 668eb8f1b6b3e5d3d2280fb2000e545836fa53af096ab6af89b87560b7484bee51a3be8360dbfe989966423c69ea0a916cb5f40f99862c19a47205d8880d8570
6
+ metadata.gz: e259c050dd7d42564dc53e82266e7becc2089d7bf72ab14b1c85c7189215f36a543bc1c0ee6b677277cafc9661ac35057e4c2110260ad198397d49623a8c4cf7
7
+ data.tar.gz: c30b8f65dacb91c98d78a01ff359896f85d79295ca466667c8c60c5ea995fa82d8d28127c44b5490f64573524f3a9820dfe8b96ccc7a54a571d9a7e77377ce9d
data/lib/api/client.rb CHANGED
@@ -1,4 +1,4 @@
1
- # lib/api/client.rb - V2.0.0 (ASYNC-FIRST, NON-BLOCKING)
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, :connection_pool
10
+ attr_reader :token, :logger, :http
11
11
 
12
- def initialize(token, logger: nil, timeout: 30, pool_size: 10)
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
- # HTTPX with proper async configuration
17
- @http = HTTPX.plugin(:persistent)
18
- .with(
19
- timeout: {
20
- connect_timeout: 10,
21
- write_timeout: 10,
22
- read_timeout: timeout,
23
- keep_alive_timeout: 15
24
- },
25
- headers: {
26
- 'Content-Type' => 'application/json',
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("🚀 Async API: #{method}") if @logger
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
- # SYNC WRAPPER: For when you absolutely need blocking
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
- # Wait with timeout
61
- begin
62
- wait_result = request.wait(timeout)
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
- # Build multipart form
83
- form = build_multipart_form(params)
84
-
85
- @logger.debug("📤 Async Upload: #{method}") if @logger
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
- # Async response handler - stores result on request object
109
- def handle_response_async(response)
110
- response.raise_for_status unless response.status == 200
80
+ def handle_response(response)
81
+ return nil unless response
111
82
 
112
- case response.status
113
- when 429 # Rate limit
114
- retry_after = response.headers['retry-after']&.to_i || 1
115
- raise RateLimitError.new("Rate limited", retry_after)
116
- when 200
117
- begin
118
- json = response.json
119
- rescue JSON::ParserError => e
120
- raise APIError, "Invalid JSON: #{e.message}"
121
- end
122
-
123
- unless json
124
- raise APIError, "Empty response"
125
- end
126
-
127
- if json['ok']
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
- response.raise_for_status
100
+ raise APIError.new(json['description'], json['error_code'])
136
101
  end
137
102
  end
138
103
 
139
- # Async error handler
140
- def handle_error_async(error)
104
+ def handle_error(error)
141
105
  case error
142
106
  when HTTPX::TimeoutError
143
- @logger.error("Timeout: #{error.message}") if @logger
144
- raise NetworkError, "Timeout: #{error.message}"
107
+ @logger.error("Timeout: #{error.message}") if @logger
108
+ raise NetworkError, "Request timeout"
145
109
  when HTTPX::ConnectionError
146
- @logger.error("🔌 Connection: #{error.message}") if @logger
147
- raise NetworkError, "Connection failed: #{error.message}"
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("💥 Unexpected: #{error.class}: #{error.message}") if @logger
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
- case obj
172
- when File, StringIO, Tempfile
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
@@ -3,7 +3,7 @@ require 'logger'
3
3
  require 'json'
4
4
 
5
5
  module Telegem
6
- VERSION = "2.0.0".freeze
6
+ VERSION = "2.0.2".freeze
7
7
  end
8
8
 
9
9
  # Load core components
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.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.0!\n\nQuick start:\n bot
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"