telegem 0.2.5 → 1.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/context.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # lib/core/context.rb - HTTPX VERSION (NO ASYNC GEM)
1
2
  module Telegem
2
3
  module Core
3
4
  class Context
@@ -42,222 +43,227 @@ module Telegem
42
43
  inline_query&.query
43
44
  end
44
45
 
45
- # Action methods
46
+ # Action methods - ALL return HTTPX request objects
46
47
  def reply(text, **options)
47
- Async do
48
- params = { chat_id: chat.id, text: text }.merge(options)
49
- await @bot.api.call('sendMessage', params)
50
- end
48
+ return nil unless chat
49
+
50
+ params = { chat_id: chat.id, text: text }.merge(options)
51
+ @bot.api.call('sendMessage', params)
51
52
  end
52
53
 
53
54
  def edit_message_text(text, **options)
54
- return Async::Task.new(nil) unless message
55
+ return nil unless message && chat
55
56
 
56
- Async do
57
- params = {
58
- chat_id: chat.id,
59
- message_id: message.message_id,
60
- text: text
61
- }.merge(options)
57
+ params = {
58
+ chat_id: chat.id,
59
+ message_id: message.message_id,
60
+ text: text
61
+ }.merge(options)
62
62
 
63
- await @bot.api.call('editMessageText', params)
64
- end
63
+ @bot.api.call('editMessageText', params)
65
64
  end
66
65
 
67
66
  def delete_message(message_id = nil)
68
67
  mid = message_id || message&.message_id
69
- return Async::Task.new(nil) unless mid && chat
68
+ return nil unless mid && chat
70
69
 
71
- Async do
72
- await @bot.api.call('deleteMessage', chat_id: chat.id, message_id: mid)
73
- end
70
+ @bot.api.call('deleteMessage', chat_id: chat.id, message_id: mid)
74
71
  end
75
72
 
76
73
  def answer_callback_query(text: nil, show_alert: false, **options)
77
- return Async::Task.new(nil) unless callback_query
74
+ return nil unless callback_query
78
75
 
79
- Async do
80
- params = {
81
- callback_query_id: callback_query.id,
82
- show_alert: show_alert
83
- }.merge(options)
76
+ params = {
77
+ callback_query_id: callback_query.id,
78
+ show_alert: show_alert
79
+ }.merge(options)
84
80
 
85
- params[:text] = text if text
86
- await @bot.api.call('answerCallbackQuery', params)
87
- end
81
+ params[:text] = text if text
82
+ @bot.api.call('answerCallbackQuery', params)
88
83
  end
89
84
 
90
85
  def answer_inline_query(results, **options)
91
- return Async::Task.new(nil) unless inline_query
86
+ return nil unless inline_query
92
87
 
93
- Async do
94
- params = {
95
- inline_query_id: inline_query.id,
96
- results: results.to_json
97
- }.merge(options)
88
+ params = {
89
+ inline_query_id: inline_query.id,
90
+ results: results.to_json
91
+ }.merge(options)
98
92
 
99
- await @bot.api.call('answerInlineQuery', params)
100
- end
93
+ @bot.api.call('answerInlineQuery', params)
101
94
  end
102
95
 
103
96
  # Media methods
104
97
  def photo(photo, caption: nil, **options)
105
- Async do
106
- params = { chat_id: chat.id, caption: caption }.merge(options)
107
-
108
- if file_object?(photo)
109
- await @bot.api.upload('sendPhoto', params.merge(photo: photo))
110
- else
111
- await @bot.api.call('sendPhoto', params.merge(photo: photo))
112
- end
98
+ return nil unless chat
99
+
100
+ params = { chat_id: chat.id, caption: caption }.merge(options)
101
+
102
+ if file_object?(photo)
103
+ @bot.api.upload('sendPhoto', params.merge(photo: photo))
104
+ else
105
+ @bot.api.call('sendPhoto', params.merge(photo: photo))
113
106
  end
114
107
  end
115
108
 
116
109
  def document(document, caption: nil, **options)
117
- Async do
118
- params = { chat_id: chat.id, caption: caption }.merge(options)
119
-
120
- if file_object?(document)
121
- await @bot.api.upload('sendDocument', params.merge(document: document))
122
- else
123
- await @bot.api.call('sendDocument', params.merge(document: document))
124
- end
110
+ return nil unless chat
111
+
112
+ params = { chat_id: chat.id, caption: caption }.merge(options)
113
+
114
+ if file_object?(document)
115
+ @bot.api.upload('sendDocument', params.merge(document: document))
116
+ else
117
+ @bot.api.call('sendDocument', params.merge(document: document))
125
118
  end
126
119
  end
127
120
 
128
121
  def audio(audio, caption: nil, **options)
129
- Async do
130
- params = { chat_id: chat.id, caption: caption }.merge(options)
131
-
132
- if file_object?(audio)
133
- await @bot.api.upload('sendAudio', params.merge(audio: audio))
134
- else
135
- await @bot.api.call('sendAudio', params.merge(audio: audio))
136
- end
122
+ return nil unless chat
123
+
124
+ params = { chat_id: chat.id, caption: caption }.merge(options)
125
+
126
+ if file_object?(audio)
127
+ @bot.api.upload('sendAudio', params.merge(audio: audio))
128
+ else
129
+ @bot.api.call('sendAudio', params.merge(audio: audio))
137
130
  end
138
131
  end
139
132
 
140
133
  def video(video, caption: nil, **options)
141
- Async do
142
- params = { chat_id: chat.id, caption: caption }.merge(options)
143
-
144
- if file_object?(video)
145
- await @bot.api.upload('sendVideo', params.merge(video: video))
146
- else
147
- await @bot.api.call('sendVideo', params.merge(video: video))
148
- end
134
+ return nil unless chat
135
+
136
+ params = { chat_id: chat.id, caption: caption }.merge(options)
137
+
138
+ if file_object?(video)
139
+ @bot.api.upload('sendVideo', params.merge(video: video))
140
+ else
141
+ @bot.api.call('sendVideo', params.merge(video: video))
149
142
  end
150
143
  end
151
144
 
152
145
  def voice(voice, caption: nil, **options)
153
- Async do
154
- params = { chat_id: chat.id, caption: caption }.merge(options)
155
-
156
- if file_object?(voice)
157
- await @bot.api.upload('sendVoice', params.merge(voice: voice))
158
- else
159
- await @bot.api.call('sendVoice', params.merge(voice: voice))
160
- end
146
+ return nil unless chat
147
+
148
+ params = { chat_id: chat.id, caption: caption }.merge(options)
149
+
150
+ if file_object?(voice)
151
+ @bot.api.upload('sendVoice', params.merge(voice: voice))
152
+ else
153
+ @bot.api.call('sendVoice', params.merge(voice: voice))
161
154
  end
162
155
  end
163
156
 
164
157
  def sticker(sticker, **options)
165
- Async do
166
- params = { chat_id: chat.id, sticker: sticker }.merge(options)
167
- await @bot.api.call('sendSticker', params)
168
- end
158
+ return nil unless chat
159
+
160
+ params = { chat_id: chat.id, sticker: sticker }.merge(options)
161
+ @bot.api.call('sendSticker', params)
169
162
  end
170
163
 
171
164
  def location(latitude, longitude, **options)
172
- Async do
173
- params = {
174
- chat_id: chat.id,
175
- latitude: latitude,
176
- longitude: longitude
177
- }.merge(options)
178
-
179
- await @bot.api.call('sendLocation', params)
180
- end
165
+ return nil unless chat
166
+
167
+ params = {
168
+ chat_id: chat.id,
169
+ latitude: latitude,
170
+ longitude: longitude
171
+ }.merge(options)
172
+
173
+ @bot.api.call('sendLocation', params)
181
174
  end
182
175
 
183
176
  def send_chat_action(action, **options)
184
- Async do
185
- params = { chat_id: chat.id, action: action }.merge(options)
186
- await @bot.api.call('sendChatAction', params)
187
- end
177
+ return nil unless chat
178
+
179
+ params = { chat_id: chat.id, action: action }.merge(options)
180
+ @bot.api.call('sendChatAction', params)
188
181
  end
189
182
 
190
183
  def forward_message(from_chat_id, message_id, **options)
191
- Async do
192
- params = {
193
- chat_id: chat.id,
194
- from_chat_id: from_chat_id,
195
- message_id: message_id
196
- }.merge(options)
197
-
198
- await @bot.api.call('forwardMessage', params)
199
- end
184
+ return nil unless chat
185
+
186
+ params = {
187
+ chat_id: chat.id,
188
+ from_chat_id: from_chat_id,
189
+ message_id: message_id
190
+ }.merge(options)
191
+
192
+ @bot.api.call('forwardMessage', params)
193
+ end
194
+
195
+ def copy_message(from_chat_id, message_id, **options)
196
+ return nil unless chat
197
+
198
+ params = {
199
+ chat_id: chat.id,
200
+ from_chat_id: from_chat_id,
201
+ message_id: message_id
202
+ }.merge(options)
203
+
204
+ @bot.api.call('copyMessage', params)
200
205
  end
201
206
 
207
+ # Chat management
202
208
  def pin_message(message_id, **options)
203
- Async do
204
- params = { chat_id: chat.id, message_id: message_id }.merge(options)
205
- await @bot.api.call('pinChatMessage', params)
206
- end
209
+ return nil unless chat
210
+
211
+ params = { chat_id: chat.id, message_id: message_id }.merge(options)
212
+ @bot.api.call('pinChatMessage', params)
207
213
  end
208
214
 
209
215
  def unpin_message(**options)
210
- Async do
211
- params = { chat_id: chat.id }.merge(options)
212
- await @bot.api.call('unpinChatMessage', params)
213
- end
216
+ return nil unless chat
217
+
218
+ params = { chat_id: chat.id }.merge(options)
219
+ @bot.api.call('unpinChatMessage', params)
214
220
  end
215
221
 
216
222
  def kick_chat_member(user_id, **options)
217
- Async do
218
- params = { chat_id: chat.id, user_id: user_id }.merge(options)
219
- await @bot.api.call('kickChatMember', params)
220
- end
223
+ return nil unless chat
224
+
225
+ params = { chat_id: chat.id, user_id: user_id }.merge(options)
226
+ @bot.api.call('kickChatMember', params)
221
227
  end
222
228
 
223
229
  def ban_chat_member(user_id, **options)
224
- Async do
225
- params = { chat_id: chat.id, user_id: user_id }.merge(options)
226
- await @bot.api.call('banChatMember', params)
227
- end
230
+ return nil unless chat
231
+
232
+ params = { chat_id: chat.id, user_id: user_id }.merge(options)
233
+ @bot.api.call('banChatMember', params)
228
234
  end
229
235
 
230
236
  def unban_chat_member(user_id, **options)
231
- Async do
232
- params = { chat_id: chat.id, user_id: user_id }.merge(options)
233
- await @bot.api.call('unbanChatMember', params)
234
- end
237
+ return nil unless chat
238
+
239
+ params = { chat_id: chat.id, user_id: user_id }.merge(options)
240
+ @bot.api.call('unbanChatMember', params)
235
241
  end
236
242
 
237
243
  def get_chat_administrators(**options)
238
- Async do
239
- params = { chat_id: chat.id }.merge(options)
240
- await @bot.api.call('getChatAdministrators', params)
241
- end
244
+ return nil unless chat
245
+
246
+ params = { chat_id: chat.id }.merge(options)
247
+ @bot.api.call('getChatAdministrators', params)
242
248
  end
243
249
 
244
250
  def get_chat_members_count(**options)
245
- Async do
246
- params = { chat_id: chat.id }.merge(options)
247
- await @bot.api.call('getChatMembersCount', params)
248
- end
251
+ return nil unless chat
252
+
253
+ params = { chat_id: chat.id }.merge(options)
254
+ @bot.api.call('getChatMembersCount', params)
249
255
  end
250
256
 
251
257
  def get_chat(**options)
252
- Async do
253
- params = { chat_id: chat.id }.merge(options)
254
- await @bot.api.call('getChat', params)
255
- end
258
+ return nil unless chat
259
+
260
+ params = { chat_id: chat.id }.merge(options)
261
+ @bot.api.call('getChat', params)
256
262
  end
257
263
 
258
264
  # Keyboard helpers
259
265
  def keyboard(&block)
260
- require 'lib/markup/keyboard'
266
+ require 'telegem/markup/keyboard'
261
267
  Telegem::Markup.keyboard(&block)
262
268
  end
263
269
 
@@ -267,42 +273,40 @@ module Telegem
267
273
  end
268
274
 
269
275
  def reply_with_keyboard(text, keyboard_markup, **options)
270
- Async do
271
- reply_markup = keyboard_markup.is_a?(Hash) ? keyboard_markup : keyboard_markup.to_h
272
- await reply(text, reply_markup: reply_markup, **options)
273
- end
276
+ return nil unless chat
277
+
278
+ reply_markup = keyboard_markup.is_a?(Hash) ? keyboard_markup : keyboard_markup.to_h
279
+ reply(text, reply_markup: reply_markup, **options)
274
280
  end
275
281
 
276
282
  def reply_with_inline_keyboard(text, inline_markup, **options)
277
- Async do
278
- reply_markup = inline_markup.is_a?(Hash) ? inline_markup : inline_markup.to_h
279
- await reply(text, reply_markup: reply_markup, **options)
280
- end
283
+ return nil unless chat
284
+
285
+ reply_markup = inline_markup.is_a?(Hash) ? inline_markup : inline_markup.to_h
286
+ reply(text, reply_markup: reply_markup, **options)
281
287
  end
282
288
 
283
289
  def remove_keyboard(text = nil, **options)
284
- Async do
285
- reply_markup = Telegem::Markup.remove(**options.slice(:selective))
286
- if text
287
- await reply(text, reply_markup: reply_markup, **options.except(:selective))
288
- else
289
- reply_markup
290
- end
290
+ return nil unless chat
291
+
292
+ reply_markup = Telegem::Markup.remove(**options.slice(:selective))
293
+ if text
294
+ reply(text, reply_markup: reply_markup, **options.except(:selective))
295
+ else
296
+ reply_markup
291
297
  end
292
298
  end
293
299
 
294
300
  def edit_message_reply_markup(reply_markup, **options)
295
- return Async::Task.new(nil) unless message
301
+ return nil unless message && chat
296
302
 
297
- Async do
298
- params = {
299
- chat_id: chat.id,
300
- message_id: message.message_id,
301
- reply_markup: reply_markup
302
- }.merge(options)
303
+ params = {
304
+ chat_id: chat.id,
305
+ message_id: message.message_id,
306
+ reply_markup: reply_markup
307
+ }.merge(options)
303
308
 
304
- await @bot.api.call('editMessageReplyMarkup', params)
305
- end
309
+ @bot.api.call('editMessageReplyMarkup', params)
306
310
  end
307
311
 
308
312
  # Chat action shortcuts
@@ -327,11 +331,14 @@ module Telegem
327
331
  end
328
332
 
329
333
  def with_typing(&block)
330
- Async do
331
- await typing
332
- result = block.call
333
- result.is_a?(Async::Task) ? await(result) : result
334
- end
334
+ # Start typing action
335
+ typing_request = typing
336
+
337
+ # Execute block
338
+ result = block.call
339
+
340
+ # Return block's result (could be HTTPX request or nil)
341
+ result
335
342
  end
336
343
 
337
344
  # Command detection
@@ -345,22 +352,18 @@ module Telegem
345
352
 
346
353
  # Scene management
347
354
  def enter_scene(scene_name, **options)
348
- Async do
349
- @scene = scene_name
350
- if @bot.scenes[scene_name]
351
- await @bot.scenes[scene_name].enter(self, **options)
352
- end
353
- end
355
+ return nil unless @bot.scenes[scene_name]
356
+
357
+ @scene = scene_name
358
+ @bot.scenes[scene_name].enter(self, **options)
354
359
  end
355
360
 
356
361
  def leave_scene(**options)
357
- Async do
358
- return unless @scene
359
- if @bot.scenes[@scene]
360
- await @bot.scenes[@scene].leave(self, **options)
361
- end
362
- @scene = nil
363
- end
362
+ return nil unless @scene && @bot.scenes[@scene]
363
+
364
+ scene_name = @scene
365
+ @scene = nil
366
+ @bot.scenes[scene_name].leave(self, **options)
364
367
  end
365
368
 
366
369
  def current_scene
@@ -388,7 +391,7 @@ module Telegem
388
391
 
389
392
  def file_object?(obj)
390
393
  obj.is_a?(File) || obj.is_a?(StringIO) || obj.is_a?(Tempfile) ||
391
- (obj.is_a?(String) && File.exist?(obj))
394
+ (obj.is_a?(String) && File.exist?(obj))
392
395
  end
393
396
  end
394
397
  end
data/lib/core/scene.rb CHANGED
@@ -1,81 +1,91 @@
1
- module Telegem
2
- module Core
3
- class Scene
4
- attr_reader :id, :steps, :enter_callbacks, :leave_callbacks
1
+ # lib/core/scene.rb - HTTPX VERSION (NO ASYNC GEM)
2
+ module Telegem
3
+ module Core
4
+ class Scene
5
+ attr_reader :id, :steps, :enter_callbacks, :leave_callbacks
5
6
 
6
- def initialize(id, default_step: :start, &block)
7
- @id = id
8
- @steps = {}
9
- @enter_callbacks = []
10
- @leave_callbacks = []
11
- @default_step = default_step
12
- instance_eval(&block) if block_given?
13
- end
7
+ def initialize(id, default_step: :start, &block)
8
+ @id = id
9
+ @steps = {}
10
+ @enter_callbacks = []
11
+ @leave_callbacks = []
12
+ @default_step = default_step
13
+ instance_eval(&block) if block_given?
14
+ end
14
15
 
15
- def step(name, &action)
16
- @steps[name.to_sym] = action
17
- self
18
- end
16
+ def step(name, &action)
17
+ @steps[name.to_sym] = action
18
+ self
19
+ end
19
20
 
20
- def on_enter(&callback)
21
- @enter_callbacks << callback
22
- self
23
- end
21
+ def on_enter(&callback)
22
+ @enter_callbacks << callback
23
+ self
24
+ end
24
25
 
25
- def on_leave(&callback)
26
- @leave_callbacks << callback
27
- self
28
- end
26
+ def on_leave(&callback)
27
+ @leave_callbacks << callback
28
+ self
29
+ end
29
30
 
30
- def enter(ctx, step_name = nil)
31
- Async do
32
- step_name ||= @default_step
33
- ctx.scene = { id: @id, step: step_name.to_sym }
34
- @enter_callbacks.each { |cb| await(cb.call(ctx)) }
35
- await process_step(ctx, step_name)
36
- rescue => e
37
- ctx.logger.error("Error entering scene #{@id}: #{e.message}")
38
- raise
39
- end
40
- end
31
+ def enter(ctx, step_name = nil)
32
+ step_name ||= @default_step
33
+
34
+ # Store scene state in context
35
+ ctx.scene = { id: @id, step: step_name.to_sym }
36
+
37
+ # Run enter callbacks
38
+ @enter_callbacks.each { |cb| cb.call(ctx) }
39
+
40
+ # Enter the first step
41
+ process_step(ctx, step_name)
42
+ end
41
43
 
42
- def leave(ctx)
43
- Async do
44
- @leave_callbacks.each { |cb| await(cb.call(ctx)) }
45
- ctx.scene = nil
46
- rescue => e
47
- ctx.logger.error("Error leaving scene #{@id}: #{e.message}")
48
- raise
49
- end
50
- end
44
+ def leave(ctx)
45
+ # Run leave callbacks
46
+ @leave_callbacks.each { |cb| cb.call(ctx) }
47
+
48
+ # Clear scene from context
49
+ ctx.scene = nil
50
+ nil
51
+ end
51
52
 
52
- def process_step(ctx, step_name)
53
- Async do
54
- step = @steps[step_name.to_sym]
55
- raise "Unknown step #{step_name} in scene #{@id}" unless step
53
+ def process_step(ctx, step_name)
54
+ step = @steps[step_name.to_sym]
55
+
56
+ unless step
57
+ ctx.logger.error("Unknown step #{step_name} in scene #{@id}")
58
+ return nil
59
+ end
60
+
61
+ # Execute the step
62
+ result = step.call(ctx)
63
+
64
+ # Update step in context (unless step changed it)
65
+ if ctx.scene && ctx.scene[:id] == @id
66
+ ctx.scene[:step] = step_name.to_sym
67
+ end
68
+
69
+ result
70
+ rescue => e
71
+ ctx.logger.error("Error processing step #{step_name} in scene #{@id}: #{e.message}")
72
+ ctx.logger.error(e.backtrace.join("\n")) if e.backtrace
73
+ nil
74
+ end
56
75
 
57
- result = step.call(ctx)
58
- result = await(result) if result.is_a?(Async::Task)
59
- ctx.scene[:step] = step_name.to_sym
60
- result
61
- rescue => e
62
- ctx.logger.error("Error processing step #{step_name} in scene #{@id}: #{e.message}")
63
- raise
64
- end
65
- end
76
+ def current_step(ctx)
77
+ return nil unless ctx.scene && ctx.scene[:id] == @id
78
+ ctx.scene[:step]
79
+ end
66
80
 
67
- def current_step(ctx)
68
- ctx.scene[:step] if ctx.scene && ctx.scene[:id] == @id
69
- end
81
+ def reset(ctx)
82
+ ctx.scene = { id: @id, step: @default_step }
83
+ self
84
+ end
70
85
 
71
- def reset(ctx)
72
- ctx.scene = { id: @id, step: @default_step }
73
- self
74
- end
75
-
76
- def to_s
77
- "#<Scene #{@id}>"
78
- end
79
- end
80
- end
81
- end
86
+ def to_s
87
+ "#<Scene #{@id}>"
88
+ end
89
+ end
90
+ end
91
+ end
@@ -1,7 +1,7 @@
1
1
  # lib/core/memory_store.rb - Generic in-memory store for caching/rate limiting
2
2
  module Telegem
3
3
  module Core
4
- class MemoryStore
4
+ class CacheStore
5
5
  def initialize(default_ttl: 300) # 5 minutes default
6
6
  @store = {}
7
7
  @ttls = {}