telegem 2.0.6 → 2.0.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a800a5e96af8c42cd15dd4448e6b157beb99c6a1e5e29de786b9af0464903093
4
- data.tar.gz: 25829f45ed0434bd0a8bd6f09a98296292d96037b21dea52323596f42cdec7a4
3
+ metadata.gz: 272c8898e9301da49ef787a232fe0eca2b1374ed611a04ae43d6ffff0a51053b
4
+ data.tar.gz: 44c00d019fbcd8da206f7231d9a24cec1c10f00b8f075c8c233a9706ceb6675a
5
5
  SHA512:
6
- metadata.gz: 5cb72e7a6d9dc453808e617033f6e5d52229dc72299779834e4ecacf67d4cc25510f2ca97dbbbca199c0adb7e2ee1c5f18a4c77d576198a891f8b98bbfd75c94
7
- data.tar.gz: a490e086730633ccf1e8ba3cc4d018033fbbe0ddc3b300104f9aff1a91d542766e7053a2c81cefcedc8b4555c0415368a7df42ef47c59ff9d5b284a0e413defc
6
+ metadata.gz: 55eba2b0e9a9792e5bd4280656a2986a3150a5c9ebec65c6c5fe60144cbc66dd6c460578966b4077fb53d70b65fdcdc5ee40a1561b663cc0892237ade86a72b9
7
+ data.tar.gz: 1f151636509dd3ba2e63a61db759a45bee1d54a63e9e708694f6a137b517a88d77691728c7be7a371a0aad9c6b41e02d71396aa70f6ec3e418f78161431a37be
data/lib/api/client.rb CHANGED
@@ -1,4 +1,3 @@
1
- # lib/api/client.rb - MINIMAL WORKING VERSION
2
1
  require 'httpx'
3
2
  require 'json'
4
3
 
@@ -12,35 +11,54 @@ module Telegem
12
11
  def initialize(token, **options)
13
12
  @token = token
14
13
  @logger = options[:logger] || Logger.new($stdout)
15
- @timeout = options[:timeout] || 30
14
+ timeout = options[:timeout] || 30
16
15
 
17
16
  @http = HTTPX.with(
18
- timeout: { request_timeout: @timeout},
17
+ timeout: {
18
+ request_timeout: timeout,
19
+ connect_timeout: 10,
20
+ write_timeout: 10,
21
+ read_timeout: timeout
22
+ },
19
23
  headers: {
20
- 'Content-Type' => 'application/json'
24
+ 'Content-Type' => 'application/json',
25
+ 'User-Agent' => "Telegem/#{Telegem::VERSION}"
21
26
  }
22
27
  )
23
28
  end
24
29
 
25
30
  def call(method, params = {})
26
31
  url = "#{BASE_URL}/bot#{@token}/#{method}"
32
+ @logger.debug("API Call: #{method}") if @logger
27
33
  @http.post(url, json: params.compact)
28
34
  end
29
35
 
30
36
  def call!(method, params = {})
31
37
  request = call(method, params)
32
- request.wait
38
+ response = request.wait
33
39
 
34
- if request.error
35
- handle_error(request.error)
40
+ if response.status != 200
41
+ @logger.error("HTTP Error #{response.status}") if @logger
36
42
  return nil
37
43
  end
38
44
 
39
- handle_response(request.response)
45
+ json = response.json
46
+
47
+ if json && json['ok']
48
+ json['result']
49
+ else
50
+ error_msg = json ? json['description'] : "Invalid response"
51
+ @logger.error("API Error: #{error_msg}") if @logger
52
+ nil
53
+ end
54
+ rescue => e
55
+ @logger.error("Error in call!: #{e.class}: #{e.message}") if @logger
56
+ nil
40
57
  end
41
58
 
42
59
  def upload(method, params)
43
60
  url = "#{BASE_URL}/bot#{@token}/#{method}"
61
+
44
62
  form = params.map do |key, value|
45
63
  if file_object?(value)
46
64
  [key.to_s, HTTPX::FormData::File.new(value)]
@@ -48,6 +66,7 @@ module Telegem
48
66
  [key.to_s, value.to_s]
49
67
  end
50
68
  end
69
+
51
70
  @http.post(url, form: form)
52
71
  end
53
72
 
@@ -64,28 +83,6 @@ module Telegem
64
83
 
65
84
  private
66
85
 
67
- def handle_response(response)
68
- return nil unless response
69
-
70
- if response.status != 200
71
- raise APIError, "HTTP #{response.status}"
72
- end
73
-
74
- json = response.json
75
- return nil unless json
76
-
77
- if json['ok']
78
- json['result']
79
- else
80
- raise APIError.new(json['description'], json['error_code'])
81
- end
82
- end
83
-
84
- def handle_error(error)
85
- @logger.error("API Error: #{error.message}") if @logger
86
- raise APIError, error.message
87
- end
88
-
89
86
  def file_object?(obj)
90
87
  obj.is_a?(File) || obj.is_a?(StringIO) || obj.is_a?(Tempfile) ||
91
88
  (obj.is_a?(String) && File.exist?(obj))
data/lib/core/bot.rb CHANGED
@@ -1,11 +1,9 @@
1
- # lib/core/bot.rb - Telegem v2.0.0 (Stable)
2
1
  require 'concurrent'
3
2
  require 'logger'
4
3
 
5
4
  module Telegem
6
5
  module Core
7
6
  class Bot
8
- # Public accessors for bot state and components
9
7
  attr_reader :token, :api, :handlers, :middleware, :logger, :scenes,
10
8
  :running, :polling_thread, :session_store
11
9
 
@@ -13,7 +11,6 @@ module Telegem
13
11
  @token = token
14
12
  @api = API::Client.new(token, **options.slice(:logger, :timeout))
15
13
 
16
- # Initialize handler registries for different update types
17
14
  @handlers = {
18
15
  message: [],
19
16
  callback_query: [],
@@ -30,24 +27,17 @@ module Telegem
30
27
  @error_handler = nil
31
28
  @session_store = options[:session_store] || Session::MemoryStore.new
32
29
 
33
- # Thread pool and worker management
34
30
  @thread_pool = Concurrent::FixedThreadPool.new(options[:max_threads] || 10)
35
31
  @update_queue = Queue.new
36
32
  @worker_threads = []
37
33
 
38
- # Polling state
39
34
  @polling_thread = nil
40
35
  @running = false
41
- @offset = nil # Last processed update ID
36
+ @offset = nil
42
37
 
43
- # Start worker threads for processing updates
44
38
  start_workers(options[:worker_count] || 5)
45
39
  end
46
40
 
47
- # ========================
48
- # POLLING LIFECYCLE METHODS
49
- # ========================
50
-
51
41
  def start_polling(**options)
52
42
  return if @running
53
43
 
@@ -58,7 +48,7 @@ module Telegem
58
48
  allowed_updates: nil
59
49
  }.merge(options)
60
50
 
61
- @offset = nil # Reset offset when starting
51
+ @offset = nil
62
52
 
63
53
  @logger.info "🤖 Starting Telegem bot (polling mode)..."
64
54
 
@@ -76,15 +66,12 @@ module Telegem
76
66
  @logger.info "🛑 Shutting down bot..."
77
67
  @running = false
78
68
 
79
- # Gracefully stop polling thread
80
69
  if @polling_thread&.alive?
81
70
  @polling_thread.join(3)
82
71
  end
83
72
 
84
- # Stop worker threads
85
73
  stop_workers
86
74
 
87
- # Close API connections
88
75
  @api.close if @api.respond_to?(:close)
89
76
 
90
77
  @logger.info "✅ Bot shutdown complete"
@@ -94,10 +81,6 @@ module Telegem
94
81
  @running
95
82
  end
96
83
 
97
- # ========================
98
- # COMMAND & EVENT HANDLERS
99
- # ========================
100
-
101
84
  def command(name, **options, &block)
102
85
  pattern = /^\/#{Regexp.escape(name)}(?:@\w+)?(?:\s+(.+))?$/i
103
86
 
@@ -132,10 +115,6 @@ module Telegem
132
115
  @scenes[id] = Scene.new(id, &block)
133
116
  end
134
117
 
135
- # ========================
136
- # WEBHOOK METHODS
137
- # ========================
138
-
139
118
  def webhook(app = nil, port: nil, host: '0.0.0.0', logger: nil, &block)
140
119
  require_relative '../webhook/server'
141
120
 
@@ -160,43 +139,31 @@ module Telegem
160
139
  @api.call!('getWebhookInfo', {})
161
140
  end
162
141
 
163
- # ========================
164
- # UPDATE PROCESSING
165
- # ========================
166
-
167
142
  def process(update_data)
168
143
  update = Types::Update.new(update_data)
169
144
  process_update(update)
170
145
  end
171
146
 
172
- # ========================
173
- # PRIVATE METHODS
174
- # ========================
175
-
176
147
  private
177
148
 
178
- # ----- POLLING LOGIC -----
179
-
180
149
  def poll_loop
181
150
  @logger.debug "Entering polling loop"
182
151
 
183
152
  while @running
184
153
  begin
185
- # Use synchronous call to avoid HTTPX version conflicts
186
154
  result = fetch_updates
187
155
 
188
156
  if result && result.is_a?(Hash) && result['ok']
189
157
  handle_updates_response(result)
190
158
  elsif result
191
- @logger.warn "Unexpected API response format"
159
+ @logger.warn "Unexpected API response format: #{result.class}"
192
160
  end
193
161
 
194
- # Small delay between polls unless we just processed updates
195
162
  sleep 0.1 unless @offset.nil?
196
163
 
197
164
  rescue => e
198
165
  handle_error(e)
199
- sleep 5 # Wait before retrying after error
166
+ sleep 5
200
167
  end
201
168
  end
202
169
 
@@ -211,8 +178,26 @@ module Telegem
211
178
  params[:offset] = @offset if @offset
212
179
  params[:allowed_updates] = @polling_options[:allowed_updates] if @polling_options[:allowed_updates]
213
180
 
214
- # Use call! for synchronous operation (more reliable)
215
- @api.call!('getUpdates', params)
181
+ begin
182
+ request = @api.get_updates(params)
183
+
184
+ if request.respond_to?(:wait)
185
+ response = request.wait
186
+
187
+ if response.respond_to?(:status) && response.status == 200
188
+ json = response.json
189
+ return json if json && json.is_a?(Hash)
190
+ else
191
+ @logger.error("HTTP Error: #{response.status}") if response.respond_to?(:status)
192
+ end
193
+ else
194
+ @logger.error("API didn't return a waitable request: #{request.class}")
195
+ end
196
+ rescue => e
197
+ @logger.error("Error fetching updates: #{e.class}: #{e.message}")
198
+ end
199
+
200
+ nil
216
201
  end
217
202
 
218
203
  def handle_updates_response(api_response)
@@ -222,18 +207,14 @@ module Telegem
222
207
  @logger.debug "Processing #{updates.length} update(s)"
223
208
 
224
209
  updates.each do |update_data|
225
- # Queue for processing by worker threads
226
210
  @update_queue << [update_data, nil]
227
211
  end
228
212
 
229
- # Update offset for next request
230
213
  @offset = updates.last['update_id'] + 1
231
214
  @logger.debug "Updated offset to #{@offset}"
232
215
  end
233
216
  end
234
217
 
235
- # ----- WORKER THREAD MANAGEMENT -----
236
-
237
218
  def start_workers(count)
238
219
  count.times do |i|
239
220
  @worker_threads << Thread.new do
@@ -247,10 +228,8 @@ module Telegem
247
228
  def stop_workers
248
229
  @logger.debug "Stopping worker threads"
249
230
 
250
- # Send shutdown signals to all workers
251
231
  @worker_threads.size.times { @update_queue << :shutdown }
252
232
 
253
- # Wait for threads to finish
254
233
  @worker_threads.each do |thread|
255
234
  thread.join(2) if thread.alive?
256
235
  end
@@ -263,16 +242,13 @@ module Telegem
263
242
 
264
243
  while @running
265
244
  begin
266
- # Wait for a task from the queue
267
245
  task = @update_queue.pop
268
246
 
269
- # Check for shutdown signal
270
247
  break if task == :shutdown
271
248
 
272
249
  update_data, callback = task
273
250
  process_update(Types::Update.new(update_data))
274
251
 
275
- # Execute callback if provided
276
252
  callback&.call if callback.respond_to?(:call)
277
253
 
278
254
  rescue => e
@@ -283,13 +259,10 @@ module Telegem
283
259
  @logger.debug "Worker #{id} stopped"
284
260
  end
285
261
 
286
- # ----- UPDATE PROCESSING PIPELINE -----
287
-
288
262
  def process_update(update)
289
263
  ctx = Context.new(update, self)
290
264
 
291
265
  begin
292
- # Run through middleware chain, then dispatch to handlers
293
266
  run_middleware_chain(ctx) do |context|
294
267
  dispatch_to_handlers(context)
295
268
  end
@@ -306,7 +279,6 @@ module Telegem
306
279
  def build_middleware_chain
307
280
  chain = Composer.new
308
281
 
309
- # Add user-defined middleware
310
282
  @middleware.each do |middleware_class, args, block|
311
283
  if middleware_class.respond_to?(:new)
312
284
  middleware = middleware_class.new(*args, &block)
@@ -316,7 +288,6 @@ module Telegem
316
288
  end
317
289
  end
318
290
 
319
- # Add session middleware if not already present
320
291
  unless @middleware.any? { |m, _, _| m.is_a?(Session::Middleware) }
321
292
  chain.use(Session::Middleware.new(@session_store))
322
293
  end
@@ -328,17 +299,14 @@ module Telegem
328
299
  update_type = detect_update_type(ctx.update)
329
300
  handlers = @handlers[update_type] || []
330
301
 
331
- # Find and execute the first matching handler
332
302
  handlers.each do |handler|
333
303
  if matches_filters?(ctx, handler[:filters])
334
304
  handler[:handler].call(ctx)
335
- break # First matching handler wins
305
+ break
336
306
  end
337
307
  end
338
308
  end
339
309
 
340
- # ----- FILTER MATCHING LOGIC -----
341
-
342
310
  def detect_update_type(update)
343
311
  return :message if update.message
344
312
  return :callback_query if update.callback_query
@@ -387,8 +355,6 @@ module Telegem
387
355
  ctx.message.command_name == command_name.to_s
388
356
  end
389
357
 
390
- # ----- ERROR HANDLING -----
391
-
392
358
  def handle_error(error, ctx = nil)
393
359
  if @error_handler
394
360
  @error_handler.call(error, ctx)
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.6".freeze
6
+ VERSION = "2.0.7".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.6
4
+ version: 2.0.7
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.6!\n\nQuick start:\n bot
201
+ post_install_message: "Thanks for installing Telegem 2.0.7!\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"