telegem 2.0.6 → 2.0.8

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: b5e8e2d8f412d455281a8ed45a4384161431715964447abf7d323c8e5a651eaf
4
+ data.tar.gz: e92cf5b7324c3e745a66dfcf935b9ed18bae231fa0051316070cf726dc259d4b
5
5
  SHA512:
6
- metadata.gz: 5cb72e7a6d9dc453808e617033f6e5d52229dc72299779834e4ecacf67d4cc25510f2ca97dbbbca199c0adb7e2ee1c5f18a4c77d576198a891f8b98bbfd75c94
7
- data.tar.gz: a490e086730633ccf1e8ba3cc4d018033fbbe0ddc3b300104f9aff1a91d542766e7053a2c81cefcedc8b4555c0415368a7df42ef47c59ff9d5b284a0e413defc
6
+ metadata.gz: 95f13660c4b21333d0a4d68a6fb1ba25767a1f3d5aa7f06f07d0f76b82796730f8803c3036c7297d9b8aaea99cd73e22cfe01999d5a52c6456b6b0b9a42921b3
7
+ data.tar.gz: f5cc5f64bb886ea1f38b8c51993db49c8850a0017a0722a70f670f783fbb997fcbdebc816fb28fb92e74bf543473addda1da8300430565df0fce260c84ea8cb4
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,52 @@ 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
- request = call(method, params)
32
- request.wait
37
+ url = "#{BASE_URL}/bot#{@token}/#{method}"
38
+
39
+ @logger.debug("API Call (sync): #{method}") if @logger
33
40
 
34
- if request.error
35
- handle_error(request.error)
36
- return nil
41
+ begin
42
+ response = @http.post(url, json: params.compact)
43
+
44
+ if response.respond_to?(:status) && response.status == 200
45
+ json = response.json
46
+ return json['result'] if json && json['ok']
47
+ end
48
+
49
+ @logger.error("API Error: HTTP #{response.status}") if response.respond_to?(:status) && @logger
50
+ rescue => e
51
+ @logger.error("Exception: #{e.message}") if @logger
37
52
  end
38
53
 
39
- handle_response(request.response)
54
+ nil
40
55
  end
41
56
 
42
57
  def upload(method, params)
43
58
  url = "#{BASE_URL}/bot#{@token}/#{method}"
59
+
44
60
  form = params.map do |key, value|
45
61
  if file_object?(value)
46
62
  [key.to_s, HTTPX::FormData::File.new(value)]
@@ -48,6 +64,7 @@ module Telegem
48
64
  [key.to_s, value.to_s]
49
65
  end
50
66
  end
67
+
51
68
  @http.post(url, form: form)
52
69
  end
53
70
 
@@ -64,28 +81,6 @@ module Telegem
64
81
 
65
82
  private
66
83
 
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
84
  def file_object?(obj)
90
85
  obj.is_a?(File) || obj.is_a?(StringIO) || obj.is_a?(Tempfile) ||
91
86
  (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
 
@@ -204,17 +171,25 @@ module Telegem
204
171
  end
205
172
 
206
173
  def fetch_updates
207
- params = {
208
- timeout: @polling_options[:timeout],
209
- limit: @polling_options[:limit]
210
- }
211
- params[:offset] = @offset if @offset
212
- params[:allowed_updates] = @polling_options[:allowed_updates] if @polling_options[:allowed_updates]
213
-
214
- # Use call! for synchronous operation (more reliable)
215
- @api.call!('getUpdates', params)
216
- end
217
-
174
+ params = {
175
+ timeout: @polling_options[:timeout],
176
+ limit: @polling_options[:limit]
177
+ }
178
+ params[:offset] = @offset if @offset
179
+ params[:allowed_updates] = @polling_options[:allowed_updates] if @polling_options[:allowed_updates]
180
+
181
+ @logger.debug("Fetching updates with offset: #{@offset}")
182
+
183
+ # Simple direct call - no .wait
184
+ updates = @api.call!('getUpdates', params)
185
+
186
+ if updates && updates.is_a?(Array)
187
+ @logger.debug("Got #{updates.length} updates")
188
+ return { 'ok' => true, 'result' => updates }
189
+ end
190
+
191
+ nil
192
+ end
218
193
  def handle_updates_response(api_response)
219
194
  updates = api_response['result'] || []
220
195
 
@@ -222,18 +197,14 @@ module Telegem
222
197
  @logger.debug "Processing #{updates.length} update(s)"
223
198
 
224
199
  updates.each do |update_data|
225
- # Queue for processing by worker threads
226
200
  @update_queue << [update_data, nil]
227
201
  end
228
202
 
229
- # Update offset for next request
230
203
  @offset = updates.last['update_id'] + 1
231
204
  @logger.debug "Updated offset to #{@offset}"
232
205
  end
233
206
  end
234
207
 
235
- # ----- WORKER THREAD MANAGEMENT -----
236
-
237
208
  def start_workers(count)
238
209
  count.times do |i|
239
210
  @worker_threads << Thread.new do
@@ -247,10 +218,8 @@ module Telegem
247
218
  def stop_workers
248
219
  @logger.debug "Stopping worker threads"
249
220
 
250
- # Send shutdown signals to all workers
251
221
  @worker_threads.size.times { @update_queue << :shutdown }
252
222
 
253
- # Wait for threads to finish
254
223
  @worker_threads.each do |thread|
255
224
  thread.join(2) if thread.alive?
256
225
  end
@@ -263,16 +232,13 @@ module Telegem
263
232
 
264
233
  while @running
265
234
  begin
266
- # Wait for a task from the queue
267
235
  task = @update_queue.pop
268
236
 
269
- # Check for shutdown signal
270
237
  break if task == :shutdown
271
238
 
272
239
  update_data, callback = task
273
240
  process_update(Types::Update.new(update_data))
274
241
 
275
- # Execute callback if provided
276
242
  callback&.call if callback.respond_to?(:call)
277
243
 
278
244
  rescue => e
@@ -283,13 +249,10 @@ module Telegem
283
249
  @logger.debug "Worker #{id} stopped"
284
250
  end
285
251
 
286
- # ----- UPDATE PROCESSING PIPELINE -----
287
-
288
252
  def process_update(update)
289
253
  ctx = Context.new(update, self)
290
254
 
291
255
  begin
292
- # Run through middleware chain, then dispatch to handlers
293
256
  run_middleware_chain(ctx) do |context|
294
257
  dispatch_to_handlers(context)
295
258
  end
@@ -306,7 +269,6 @@ module Telegem
306
269
  def build_middleware_chain
307
270
  chain = Composer.new
308
271
 
309
- # Add user-defined middleware
310
272
  @middleware.each do |middleware_class, args, block|
311
273
  if middleware_class.respond_to?(:new)
312
274
  middleware = middleware_class.new(*args, &block)
@@ -316,7 +278,6 @@ module Telegem
316
278
  end
317
279
  end
318
280
 
319
- # Add session middleware if not already present
320
281
  unless @middleware.any? { |m, _, _| m.is_a?(Session::Middleware) }
321
282
  chain.use(Session::Middleware.new(@session_store))
322
283
  end
@@ -328,17 +289,14 @@ module Telegem
328
289
  update_type = detect_update_type(ctx.update)
329
290
  handlers = @handlers[update_type] || []
330
291
 
331
- # Find and execute the first matching handler
332
292
  handlers.each do |handler|
333
293
  if matches_filters?(ctx, handler[:filters])
334
294
  handler[:handler].call(ctx)
335
- break # First matching handler wins
295
+ break
336
296
  end
337
297
  end
338
298
  end
339
299
 
340
- # ----- FILTER MATCHING LOGIC -----
341
-
342
300
  def detect_update_type(update)
343
301
  return :message if update.message
344
302
  return :callback_query if update.callback_query
@@ -387,8 +345,6 @@ module Telegem
387
345
  ctx.message.command_name == command_name.to_s
388
346
  end
389
347
 
390
- # ----- ERROR HANDLING -----
391
-
392
348
  def handle_error(error, ctx = nil)
393
349
  if @error_handler
394
350
  @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.8".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.8
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.8!\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"