telegem 2.1.0 → 3.0.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.
data/lib/core/bot.rb CHANGED
@@ -1,16 +1,16 @@
1
1
  require 'concurrent'
2
2
  require 'logger'
3
+ require 'async'
3
4
 
4
5
  module Telegem
5
6
  module Core
6
7
  class Bot
7
8
  attr_reader :token, :api, :handlers, :middleware, :logger, :scenes,
8
- :running, :polling_thread, :session_store
9
+ :running, :session_store
9
10
 
10
11
  def initialize(token, **options)
11
12
  @token = token
12
13
  @api = API::Client.new(token, **options.slice(:logger, :timeout))
13
- @api_mutex = Mutex.new # ← LINE 1 ADDED HERE
14
14
 
15
15
  @handlers = {
16
16
  message: [],
@@ -27,57 +27,23 @@ module Telegem
27
27
  @logger = options[:logger] || Logger.new($stdout)
28
28
  @error_handler = nil
29
29
  @session_store = options[:session_store] || Session::MemoryStore.new
30
-
31
- @thread_pool = Concurrent::FixedThreadPool.new(options[:max_threads] || 10)
32
- @update_queue = Queue.new
33
- @worker_threads = []
34
-
35
- @polling_thread = nil
36
- @running = false
37
- @offset = nil
38
-
39
- start_workers(options[:worker_count] || 5)
30
+
31
+ @running = false
32
+ @offset = 0
40
33
  end
41
34
 
42
- def start_polling(**options)
43
- return if @running
44
-
45
- @running = true
46
- @polling_options = {
47
- timeout: 30,
48
- limit: 100,
49
- allowed_updates: nil
50
- }.merge(options)
51
-
52
- @offset = nil
53
-
54
- @logger.info "🤖 Starting Telegem bot (polling mode)..."
55
-
56
- @polling_thread = Thread.new do
57
- Thread.current.abort_on_exception = false
58
- poll_loop
59
- end
60
-
61
- self
35
+ def start_polling(**options)
36
+ Async do
37
+ poll_loop # Now runs in Async context
62
38
  end
39
+ end
63
40
 
64
41
  def shutdown
65
42
  return unless @running
66
43
 
67
44
  @logger.info "🛑 Shutting down bot..."
68
45
  @running = false
69
-
70
- if @polling_thread&.alive?
71
- @polling_thread.join(3)
72
- end
73
-
74
- stop_workers
75
-
76
- @api.close if @api.respond_to?(:close)
77
-
78
- @logger.info "✅ Bot shutdown complete"
79
- end
80
-
46
+ enc
81
47
  def running?
82
48
  @running
83
49
  end
@@ -128,16 +94,16 @@ module Telegem
128
94
  end
129
95
  end
130
96
 
131
- def set_webhook(url, **options)
132
- @api.call!('setWebhook', { url: url }.merge(options))
97
+ def set_webhook(url, **options, &callback)
98
+ @api.call!('setWebhook', { url: url }.merge(options), &callback)
133
99
  end
134
-
135
- def delete_webhook
136
- @api.call!('deleteWebhook', {})
100
+
101
+ def delete_webhook(&callback)
102
+ @api.call!('deleteWebhook', {}, &callback)
137
103
  end
138
-
139
- def get_webhook_info
140
- @api.call!('getWebhookInfo', {})
104
+
105
+ def get_webhook_info(&callback)
106
+ @api.call!('getWebhookInfo', {}, &callback)
141
107
  end
142
108
 
143
109
  def process(update_data)
@@ -147,112 +113,66 @@ module Telegem
147
113
 
148
114
  private
149
115
 
150
- def poll_loop
151
- @logger.debug "Entering polling loop"
152
-
153
- while @running
154
- begin
155
- result = fetch_updates
156
-
157
- if result && result.is_a?(Hash) && result['ok']
158
- handle_updates_response(result)
159
- elsif result
160
- @logger.warn "Unexpected API response format: #{result.class}"
161
- end
162
-
163
- sleep 0.1 unless @offset.nil?
164
-
165
- rescue => e
166
- handle_error(e)
167
- sleep 5
168
- end
116
+ def poll_loop
117
+ fetch_updates do |result|
118
+ if result && result['ok']
119
+ handle_updates_response(result)
120
+ end
121
+
122
+ if @running
123
+ # Schedule next poll in NEW async context
124
+ Async { |task|
125
+ task.sleep(1)
126
+ poll_loop
127
+ }
169
128
  end
170
-
171
- @logger.debug "Exiting polling loop"
172
129
  end
173
-
174
- def fetch_updates
130
+ end
131
+
132
+ def fetch_updates(&completion_callback)
175
133
  params = {
176
- timeout: @polling_options[:timeout],
177
- limit: @polling_options[:limit]
134
+ timeout: @polling_options[:timeout] || 30,
135
+ limit: @polling_options[:limit] || 100
178
136
  }
179
137
  params[:offset] = @offset if @offset
180
138
  params[:allowed_updates] = @polling_options[:allowed_updates] if @polling_options[:allowed_updates]
181
-
182
- @logger.debug("Fetching updates with offset: #{@offset}")
183
-
184
- # Simple direct call - no .wait
185
- updates = @api.call!('getUpdates', params)
186
-
187
- if updates && updates.is_a?(Array)
188
- @logger.debug("Got #{updates.length} updates")
189
- return { 'ok' => true, 'result' => updates }
190
- end
191
-
192
- nil
193
- end
194
-
195
- def handle_updates_response(api_response)
196
- updates = api_response['result'] || []
197
-
198
- if updates.any?
199
- @logger.debug "Processing #{updates.length} update(s)"
200
-
201
- updates.each do |update_data|
202
- @update_queue << [update_data, nil]
203
- end
204
-
205
- @offset = updates.last['update_id'] + 1
206
- @logger.debug "Updated offset to #{@offset}"
207
- end
208
- end
209
-
210
- def start_workers(count)
211
- count.times do |i|
212
- @worker_threads << Thread.new do
213
- Thread.current.abort_on_exception = false
214
- worker_loop(i)
139
+
140
+ @logger.debug "Fetching updates with offset: #{@offset}"
141
+
142
+ @api.call!('getUpdates', params) do |updates_array|
143
+ # Success
144
+ if updates_array && updates_array.is_a?(Array)
145
+ result = { 'ok' => true, 'result' => updates_array }
146
+ completion_callback.call(result) if completion_callback
147
+ else
148
+ completion_callback.call(nil) if completion_callback
215
149
  end
150
+ end.on_error do |error|
151
+ # Network/HTTP error
152
+ @logger.error "Polling error: #{error.message}"
153
+ completion_callback.call(nil, error) if completion_callback
216
154
  end
217
- @logger.debug "Started #{count} worker threads"
218
155
  end
219
-
220
- def stop_workers
221
- @logger.debug "Stopping worker threads"
222
-
223
- @worker_threads.size.times { @update_queue << :shutdown }
224
-
225
- @worker_threads.each do |thread|
226
- thread.join(2) if thread.alive?
227
- end
228
-
229
- @worker_threads.clear
230
- end
231
-
232
- def worker_loop(id)
233
- @logger.debug "Worker #{id} started"
234
-
235
- while @running
236
- begin
237
- task = @update_queue.pop
238
-
239
- break if task == :shutdown
156
+
240
157
 
241
- update_data, callback = task
242
- process_update(Types::Update.new(update_data))
243
-
244
- callback&.call if callback.respond_to?(:call)
245
-
246
- rescue => e
247
- @logger.error "Worker #{id} error: #{e.message}"
248
- end
249
- end
250
-
251
- @logger.debug "Worker #{id} stopped"
252
- end
253
158
 
159
+ def handle_updates_response(api_response)
160
+ if api_response['ok']
161
+ updates = api_response['result'] || []
162
+ updates.each do |data|
163
+ Async do |task|
164
+ update_object = Types::Update.new(data)
165
+ process_update(update_object)
166
+ end
167
+ end
168
+ if updates.any?
169
+ @offset = updates.last['update_id'] + 1
170
+ @logger.debug "Updated offset to; #{@offset}"
171
+ end
172
+ end
173
+ end
174
+
254
175
  def process_update(update)
255
- @api_mutex.synchronize do # ← LINE 2 ADDED HERE
256
176
  ctx = Context.new(update, self)
257
177
 
258
178
  begin
@@ -262,7 +182,7 @@ module Telegem
262
182
  rescue => e
263
183
  handle_error(e, ctx)
264
184
  end
265
- end # ← This 'end' matches the 'do' above
185
+
266
186
  end
267
187
 
268
188
  def run_middleware_chain(ctx, &final)
@@ -361,4 +281,5 @@ module Telegem
361
281
  end
362
282
  end
363
283
  end
364
- end
284
+ end
285
+ 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.1.0".freeze
6
+ VERSION = "3.0.0".freeze
7
7
  end
8
8
 
9
9
  # Load core components