telegem 2.1.0 → 3.0.1

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/Readme.md +2 -2
  3. data/Starts_HallofFame.md +65 -0
  4. data/Test-Projects/bot.md +145 -0
  5. data/Test-Projects/bot1.rb +91 -0
  6. data/bin/telegem-ssl +44 -0
  7. data/docs-src/Bot-registration_.PNG +0 -0
  8. data/docs-src/bot.md +349 -180
  9. data/docs-src/ctx.md +399 -0
  10. data/docs-src/webhook.md +341 -0
  11. data/lib/api/client.rb +72 -27
  12. data/lib/core/bot.rb +81 -149
  13. data/lib/telegem.rb +1 -1
  14. data/lib/webhook/server.rb +149 -290
  15. data/public/index.html +481 -0
  16. metadata +32 -24
  17. data/Test-Projects/Movie-tracker-bot/Gemfile +0 -2
  18. data/Test-Projects/Movie-tracker-bot/bot.rb +0 -62
  19. data/Test-Projects/Movie-tracker-bot/handlers/.gitkeep +0 -0
  20. data/Test-Projects/Movie-tracker-bot/handlers/add_1_.rb +0 -160
  21. data/Test-Projects/Movie-tracker-bot/handlers/add_2_.rb +0 -139
  22. data/Test-Projects/Movie-tracker-bot/handlers/premium.rb +0 -13
  23. data/Test-Projects/Movie-tracker-bot/handlers/report.rb +0 -31
  24. data/Test-Projects/Movie-tracker-bot/handlers/search.rb +0 -150
  25. data/Test-Projects/Movie-tracker-bot/handlers/sponsor.rb +0 -14
  26. data/Test-Projects/Movie-tracker-bot/handlers/start.rb +0 -48
  27. data/Test-Projects/Movie-tracker-bot/handlers/watch.rb +0 -210
  28. data/Test-Projects/Test-submitted-by-marvel/.gitkeep +0 -0
  29. data/Test-Projects/Test-submitted-by-marvel/Marvel-bot.md +0 -3
  30. data/Test-Projects/bot_test1.rb +0 -75
  31. data/Test-Projects/pizza_test_bot_guide.md +0 -163
  32. data/docs-src/understanding-ctx.md +0 -581
  33. /data/{Test-Projects → bin}/.gitkeep +0 -0
  34. /data/{Test-Projects/Movie-tracker-bot → public}/.gitkeep +0 -0
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,27 @@ 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
33
+ @polling_options = options.slice(:timeout, :limit, :allowed_updates) || {}
40
34
  end
41
35
 
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
36
+ def start_polling(**options)
37
+ @running = true
38
+ @polling_options = options
39
+ Async do
40
+ poll_loop # Now runs in Async context
62
41
  end
42
+ end
63
43
 
64
44
  def shutdown
65
45
  return unless @running
66
46
 
67
47
  @logger.info "🛑 Shutting down bot..."
68
48
  @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
-
49
+ sleep 0.1
50
+ end
81
51
  def running?
82
52
  @running
83
53
  end
@@ -128,16 +98,16 @@ module Telegem
128
98
  end
129
99
  end
130
100
 
131
- def set_webhook(url, **options)
132
- @api.call!('setWebhook', { url: url }.merge(options))
101
+ def set_webhook(url, **options, &callback)
102
+ @api.call!('setWebhook', { url: url }.merge(options), &callback)
133
103
  end
134
-
135
- def delete_webhook
136
- @api.call!('deleteWebhook', {})
104
+
105
+ def delete_webhook(&callback)
106
+ @api.call!('deleteWebhook', {}, &callback)
137
107
  end
138
-
139
- def get_webhook_info
140
- @api.call!('getWebhookInfo', {})
108
+
109
+ def get_webhook_info(&callback)
110
+ @api.call!('getWebhookInfo', {}, &callback)
141
111
  end
142
112
 
143
113
  def process(update_data)
@@ -147,112 +117,74 @@ module Telegem
147
117
 
148
118
  private
149
119
 
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
120
+ def poll_loop
121
+ fetch_updates do |result|
122
+ if result && result['ok']
123
+ handle_updates_response(result)
124
+ end
125
+
126
+ if @running
127
+ # Schedule next poll in NEW async context
128
+ Async { |task|
129
+ task.sleep(1)
130
+ poll_loop
131
+ }
169
132
  end
170
-
171
- @logger.debug "Exiting polling loop"
172
133
  end
173
-
174
- def fetch_updates
134
+ end
135
+
136
+ def fetch_updates(&completion_callback)
175
137
  params = {
176
- timeout: @polling_options[:timeout],
177
- limit: @polling_options[:limit]
138
+ timeout: @polling_options[:timeout] || 30,
139
+ limit: @polling_options[:limit] || 100
178
140
  }
179
141
  params[:offset] = @offset if @offset
180
142
  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)
143
+
144
+ @logger.debug "Fetching updates with offset: #{@offset}"
145
+
146
+ @api.call!('getUpdates', params) do |updates_array, error|
147
+ if error
148
+ @logger.error "Polling error: #{error.message}"
149
+ completion_callback.call(nil, error) if completion_callback
150
+ else
151
+
152
+ # Success
153
+ if updates_array && updates_array.is_a?(Array)
154
+ result = { 'ok' => true, 'result' => updates_array }
155
+ completion_callback.call(result, nil) if completion_callback
156
+ else
157
+ completion_callback.call(nil, nil) if completion_callback
215
158
  end
216
- end
217
- @logger.debug "Started #{count} worker threads"
218
- 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
159
+ end
160
+ end
230
161
  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
240
-
241
- update_data, callback = task
242
- process_update(Types::Update.new(update_data))
162
+
243
163
 
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
164
 
165
+ def handle_updates_response(api_response)
166
+ if api_response['ok']
167
+ updates = api_response['result'] || []
168
+ updates.each do |data|
169
+ Async do |task|
170
+ update_object = Types::Update.new(data)
171
+ process_update(update_object)
172
+ end
173
+ end
174
+ if updates.any?
175
+ @offset = updates.last['update_id'] + 1
176
+ @logger.debug "Updated offset to; #{@offset}"
177
+ end
178
+ end
179
+ end
180
+
254
181
  def process_update(update)
255
- @api_mutex.synchronize do # ← LINE 2 ADDED HERE
182
+ if update.message&.text && @logger
183
+ user = update.message.from
184
+ cmd = update.message.text.split.first
185
+ @logger.info("#{cmd} - #{user.username || user.first_name}")
186
+ end
187
+
256
188
  ctx = Context.new(update, self)
257
189
 
258
190
  begin
@@ -262,7 +194,7 @@ module Telegem
262
194
  rescue => e
263
195
  handle_error(e, ctx)
264
196
  end
265
- end # ← This 'end' matches the 'do' above
197
+
266
198
  end
267
199
 
268
200
  def run_middleware_chain(ctx, &final)
@@ -361,4 +293,4 @@ module Telegem
361
293
  end
362
294
  end
363
295
  end
364
- end
296
+ 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.1".freeze
7
7
  end
8
8
 
9
9
  # Load core components