telegem 2.0.9 → 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.
- checksums.yaml +4 -4
- data/Readme.md +2 -2
- data/Starts_HallofFame.md +65 -0
- data/Test-Projects/bot.md +145 -0
- data/Test-Projects/bot1.rb +91 -0
- data/docs-src/Bot-registration_.PNG +0 -0
- data/docs-src/bot.md +349 -180
- data/docs-src/ctx.md +399 -0
- data/lib/api/client.rb +24 -23
- data/lib/core/bot.rb +69 -148
- data/lib/telegem.rb +1 -1
- data/lib/webhook/server.rb +87 -284
- data/setup.sh +30 -0
- metadata +21 -20
- data/Test-Projects/.gitkeep +0 -0
- data/Test-Projects/Movie-tracker-bot/.gitkeep +0 -0
- data/Test-Projects/Movie-tracker-bot/Gemfile +0 -2
- data/Test-Projects/Movie-tracker-bot/bot.rb +0 -62
- data/Test-Projects/Movie-tracker-bot/handlers/.gitkeep +0 -0
- data/Test-Projects/Movie-tracker-bot/handlers/add_1_.rb +0 -160
- data/Test-Projects/Movie-tracker-bot/handlers/add_2_.rb +0 -139
- data/Test-Projects/Movie-tracker-bot/handlers/premium.rb +0 -13
- data/Test-Projects/Movie-tracker-bot/handlers/report.rb +0 -31
- data/Test-Projects/Movie-tracker-bot/handlers/search.rb +0 -150
- data/Test-Projects/Movie-tracker-bot/handlers/sponsor.rb +0 -14
- data/Test-Projects/Movie-tracker-bot/handlers/start.rb +0 -48
- data/Test-Projects/Movie-tracker-bot/handlers/watch.rb +0 -210
- data/Test-Projects/Test-submitted-by-marvel/.gitkeep +0 -0
- data/Test-Projects/Test-submitted-by-marvel/Marvel-bot.md +0 -3
- data/Test-Projects/bot_test1.rb +0 -75
- data/Test-Projects/pizza_test_bot_guide.md +0 -163
- data/docs-src/understanding-ctx.md +0 -581
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, :
|
|
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
|
-
@
|
|
32
|
-
@
|
|
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
|
-
|
|
43
|
-
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
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
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
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
|