telegem 3.3.1 → 3.4.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/api/types.rb CHANGED
@@ -5,70 +5,70 @@ module Telegem
5
5
  @_raw_data = data || {}
6
6
  @_accessors_defined = {}
7
7
  end
8
-
8
+
9
9
  def method_missing(name, *args)
10
10
  return super if args.any? || block_given?
11
-
11
+
12
12
  define_accessor(name)
13
-
13
+
14
14
  if respond_to?(name)
15
15
  send(name)
16
16
  else
17
17
  super
18
18
  end
19
19
  end
20
-
20
+
21
21
  def respond_to_missing?(name, include_private = false)
22
22
  key = name.to_s
23
23
  camel_key = snake_to_camel(key)
24
24
  @_raw_data.key?(key) || @_raw_data.key?(camel_key) || super
25
25
  end
26
-
26
+
27
27
  def to_h
28
28
  @_raw_data.dup
29
29
  end
30
-
30
+
31
31
  alias_method :to_hash, :to_h
32
-
32
+
33
33
  def inspect
34
34
  "#<#{self.class.name} #{@_raw_data.inspect}>"
35
35
  end
36
-
36
+
37
37
  def to_s
38
38
  inspect
39
39
  end
40
-
40
+
41
41
  attr_reader :_raw_data
42
-
42
+
43
43
  private
44
-
44
+
45
45
  def define_accessor(name)
46
46
  return if @_accessors_defined[name]
47
-
47
+
48
48
  key = name.to_s
49
49
  camel_key = snake_to_camel(key)
50
-
50
+
51
51
  if @_raw_data.key?(key)
52
52
  define_singleton_method(name) { @_raw_data[key] }
53
53
  elsif @_raw_data.key?(camel_key)
54
54
  define_singleton_method(name) { @_raw_data[camel_key] }
55
55
  else
56
56
  define_singleton_method(name) do
57
- raise NoMethodError,
57
+ raise NoMethodError,
58
58
  "undefined method `#{name}' for #{self.class} with keys: #{@_raw_data.keys}"
59
59
  end
60
60
  end
61
-
61
+
62
62
  @_accessors_defined[name] = true
63
63
  end
64
-
64
+
65
65
  # helpers for converting nested objects
66
66
  def wrap(key, klass)
67
67
  if @_raw_data[key] && !@_raw_data[key].is_a?(klass)
68
68
  @_raw_data[key] = klass.new(@_raw_data[key])
69
69
  end
70
70
  end
71
-
71
+
72
72
  def wrap_array(key, klass)
73
73
  if @_raw_data[key] && @_raw_data[key].is_a?(Array)
74
74
  @_raw_data[key] = @_raw_data[key].map do |v|
@@ -76,35 +76,35 @@ module Telegem
76
76
  end
77
77
  end
78
78
  end
79
-
79
+
80
80
  def snake_to_camel(str)
81
81
  str.gsub(/_([a-z])/) { $1.upcase }
82
82
  end
83
-
83
+
84
84
  def camel_to_snake(str)
85
85
  str.gsub(/([A-Z])/) { "_#{$1.downcase}" }.sub(/^_/, '')
86
86
  end
87
87
  end
88
-
88
+
89
89
  class User < BaseType
90
90
  COMMON_FIELDS = %w[id is_bot first_name last_name username
91
- can_join_groups can_read_all_group_messages
92
- supports_inline_queries language_code
93
- is_premium added_to_attachment_menu
94
- can_connect_to_business].freeze
95
-
91
+ can_join_groups can_read_all_group_messages
92
+ supports_inline_queries language_code
93
+ is_premium added_to_attachment_menu
94
+ can_connect_to_business can_manage_bots].freeze
95
+
96
96
  def initialize(data)
97
97
  super(data)
98
-
98
+
99
99
  COMMON_FIELDS.each do |field|
100
100
  define_accessor(field.to_sym)
101
101
  end
102
102
  end
103
-
103
+
104
104
  def full_name
105
105
  [first_name, last_name].compact.join(' ')
106
106
  end
107
-
107
+
108
108
  def mention
109
109
  if username
110
110
  "@#{username}"
@@ -114,127 +114,127 @@ module Telegem
114
114
  "User ##{id}"
115
115
  end
116
116
  end
117
-
117
+
118
118
  def to_s
119
119
  full_name
120
120
  end
121
121
  end
122
-
122
+
123
123
  class Chat < BaseType
124
124
  COMMON_FIELDS = %w[id type username title first_name last_name
125
- photo bio has_private_forwards
125
+ photo bio has_private_forwards
126
126
  has_restricted_voice_and_video_messages
127
- description invite_link pinned_message
127
+ description invite_link pinned_message
128
128
  permissions slow_mode_delay message_auto_delete_time
129
- has_protected_content sticker_set_name
129
+ has_protected_content sticker_set_name
130
130
  can_set_sticker_set linked_chat_id location].freeze
131
-
131
+
132
132
  def initialize(data)
133
133
  super(data)
134
-
134
+
135
135
  COMMON_FIELDS.each do |field|
136
136
  define_accessor(field.to_sym)
137
137
  end
138
138
  end
139
-
139
+
140
140
  def private?
141
141
  type == 'private'
142
142
  end
143
-
143
+
144
144
  def group?
145
145
  type == 'group'
146
146
  end
147
-
147
+
148
148
  def supergroup?
149
149
  type == 'supergroup'
150
150
  end
151
-
151
+
152
152
  def channel?
153
153
  type == 'channel'
154
154
  end
155
-
155
+
156
156
  def to_s
157
157
  title || username || "Chat ##{id}"
158
158
  end
159
159
  end
160
-
160
+
161
161
  class MessageEntity < BaseType
162
162
  COMMON_FIELDS = %w[type offset length url user language
163
163
  custom_emoji_id].freeze
164
-
164
+
165
165
  def initialize(data)
166
166
  super(data)
167
-
167
+
168
168
  COMMON_FIELDS.each do |field|
169
169
  define_accessor(field.to_sym)
170
170
  end
171
-
171
+
172
172
  if @_raw_data['user'] && !@_raw_data['user'].is_a?(User)
173
173
  @_raw_data['user'] = User.new(@_raw_data['user'])
174
174
  end
175
175
  end
176
176
  end
177
-
177
+
178
178
  class Message < BaseType
179
- COMMON_FIELDS = %w[message_id from chat date edit_date
180
- text caption entities caption_entities
181
- audio document photo sticker video voice
182
- video_note contact location venue
183
- new_chat_members left_chat_member
184
- new_chat_title new_chat_photo
185
- delete_chat_photo group_chat_created
186
- supergroup_chat_created channel_chat_created
187
- migrate_to_chat_id migrate_from_chat_id
188
- pinned_message invoice successful_payment
189
- connected_website reply_markup via_bot
190
- forward_from forward_from_chat
191
- forward_from_message_id forward_signature
192
- forward_sender_name forward_date reply_to_message
193
- media_group_id author_signature
194
- has_protected_content].freeze
195
-
179
+ COMMON_FIELDS = %w[message_id from chat date edit_date
180
+ text caption entities caption_entities
181
+ audio document photo sticker video voice
182
+ video_note contact location venue
183
+ new_chat_members left_chat_member
184
+ new_chat_title new_chat_photo
185
+ delete_chat_photo group_chat_created
186
+ supergroup_chat_created channel_chat_created
187
+ migrate_to_chat_id migrate_from_chat_id
188
+ pinned_message invoice successful_payment
189
+ connected_website reply_markup via_bot
190
+ forward_from forward_from_chat
191
+ forward_from_message_id forward_signature
192
+ forward_sender_name forward_date reply_to_message
193
+ media_group_id author_signature
194
+ has_protected_content managed_bot_created managed_bot].freeze
195
+
196
196
  def initialize(data)
197
197
  super(data)
198
-
198
+
199
199
  COMMON_FIELDS.each do |field|
200
200
  define_accessor(field.to_sym)
201
201
  end
202
-
202
+
203
203
  convert_complex_fields
204
204
  end
205
-
205
+
206
206
  def command?
207
207
  return false unless text && entities
208
-
209
- entities.any? { |e| e.type == 'bot_command' &&
208
+
209
+ entities.any? { |e| e.type == 'bot_command' &&
210
210
  text[e.offset, e.length]&.start_with?('/') }
211
211
  end
212
-
212
+
213
213
  def command_name
214
214
  return nil unless command?
215
-
215
+
216
216
  command_entity = entities.find { |e| e.type == 'bot_command' }
217
217
  return nil unless command_entity
218
-
218
+
219
219
  cmd = text[command_entity.offset, command_entity.length]
220
220
  return nil if cmd.nil? || cmd.length <= 1
221
-
221
+
222
222
  cmd = cmd[1..-1]
223
223
  cmd.split('@').first.strip
224
224
  end
225
-
225
+
226
226
  def command_args
227
227
  return nil unless command?
228
-
228
+
229
229
  command_entity = entities.find { |e| e.type == 'bot_command' }
230
230
  return nil unless command_entity
231
-
231
+
232
232
  args_start = command_entity.offset + command_entity.length
233
233
  remaining = text[args_start..-1]
234
-
234
+
235
235
  next_entity = entities.select { |e| e.offset >= args_start }
236
236
  .min_by(&:offset)
237
-
237
+
238
238
  if next_entity
239
239
  args_end = next_entity.offset - 1
240
240
  text[args_start..args_end]&.strip
@@ -242,15 +242,15 @@ module Telegem
242
242
  remaining&.strip
243
243
  end
244
244
  end
245
-
245
+
246
246
  def reply?
247
247
  !!reply_to_message
248
248
  end
249
-
249
+
250
250
  def has_media?
251
251
  !!(audio || document || photo || video || voice || video_note || sticker)
252
252
  end
253
-
253
+
254
254
  def media_type
255
255
  return :audio if audio
256
256
  return :document if document
@@ -261,9 +261,9 @@ module Telegem
261
261
  return :sticker if sticker
262
262
  nil
263
263
  end
264
-
264
+
265
265
  private
266
-
266
+
267
267
  def convert_complex_fields
268
268
  # time conversions
269
269
  @_raw_data['date'] = Time.at(@_raw_data['date']) if @_raw_data['date'] && !@_raw_data['date'].is_a?(Time)
@@ -324,6 +324,10 @@ module Telegem
324
324
  wrap('general_forum_topic_unhidden', GeneralForumTopicUnhidden)
325
325
  wrap('write_access_allowed', WriteAccessAllowed)
326
326
 
327
+ # Bot API 9.6 - Managed Bot Support
328
+ wrap('managed_bot_created', ManagedBotCreated)
329
+ wrap('managed_bot', ManagedBotUpdated)
330
+
327
331
  # arrays of sizes and photos
328
332
  wrap_array('photo', PhotoSize)
329
333
  wrap_array('new_chat_photo', PhotoSize)
@@ -331,7 +335,7 @@ module Telegem
331
335
  # fall back to original media wrapper for backward compatibility
332
336
  wrap_media_objects
333
337
  end
334
-
338
+
335
339
  def wrap_media_objects
336
340
  # Media files (fall‑back to generic types if no specific class defined)
337
341
  @_raw_data['document'] = Document.new(@_raw_data['document']) if @_raw_data['document'] && !@_raw_data['document'].is_a?(Document)
@@ -367,56 +371,56 @@ module Telegem
367
371
  end
368
372
  end
369
373
  end
370
-
374
+
371
375
  class CallbackQuery < BaseType
372
376
  COMMON_FIELDS = %w[id from message inline_message_id chat_instance data game_short_name].freeze
373
-
377
+
374
378
  def initialize(data)
375
379
  super(data)
376
-
380
+
377
381
  COMMON_FIELDS.each do |field|
378
382
  define_accessor(field.to_sym)
379
383
  end
380
-
384
+
381
385
  if @_raw_data['from'] && !@_raw_data['from'].is_a?(User)
382
386
  @_raw_data['from'] = User.new(@_raw_data['from'])
383
387
  end
384
-
388
+
385
389
  if @_raw_data['message'] && !@_raw_data['message'].is_a?(Message)
386
390
  @_raw_data['message'] = Message.new(@_raw_data['message'])
387
391
  end
388
392
  end
389
-
393
+
390
394
  def from_user?
391
395
  !!from
392
396
  end
393
-
397
+
394
398
  def message?
395
399
  !!message
396
400
  end
397
-
401
+
398
402
  def inline_message?
399
403
  !!inline_message_id
400
404
  end
401
405
  end
402
-
406
+
403
407
  class Update < BaseType
404
- COMMON_FIELDS = %w[update_id message edited_message channel_post
405
- edited_channel_post inline_query chosen_inline_result
406
- callback_query shipping_query pre_checkout_query
407
- poll poll_answer my_chat_member chat_member
408
- chat_join_request].freeze
409
-
408
+ COMMON_FIELDS = %w[update_id message edited_message channel_post
409
+ edited_channel_post inline_query chosen_inline_result
410
+ callback_query shipping_query pre_checkout_query
411
+ poll poll_answer my_chat_member chat_member
412
+ chat_join_request managed_bot_created managed_bot].freeze
413
+
410
414
  def initialize(data)
411
415
  super(data)
412
-
416
+
413
417
  COMMON_FIELDS.each do |field|
414
418
  define_accessor(field.to_sym)
415
419
  end
416
-
420
+
417
421
  convert_update_objects
418
422
  end
419
-
423
+
420
424
  def type
421
425
  return :message if message
422
426
  return :edited_message if edited_message
@@ -432,9 +436,11 @@ module Telegem
432
436
  return :my_chat_member if my_chat_member
433
437
  return :chat_member if chat_member
434
438
  return :chat_join_request if chat_join_request
439
+ return :managed_bot_created if managed_bot_created
440
+ return :managed_bot if managed_bot
435
441
  :unknown
436
442
  end
437
-
443
+
438
444
  def from
439
445
  case type
440
446
  when :message, :edited_message
@@ -455,13 +461,15 @@ module Telegem
455
461
  my_chat_member&.from || chat_member&.from
456
462
  when :chat_join_request
457
463
  chat_join_request.from
464
+ when :managed_bot_created, :managed_bot
465
+ managed_bot_created&.from || managed_bot&.from
458
466
  else
459
467
  nil
460
468
  end
461
469
  end
462
-
470
+
463
471
  private
464
-
472
+
465
473
  def convert_update_objects
466
474
  wrap('message', Message)
467
475
  wrap('edited_message', Message)
@@ -485,6 +493,10 @@ module Telegem
485
493
  wrap('general_forum_topic_hidden', GeneralForumTopicHidden)
486
494
  wrap('general_forum_topic_unhidden', GeneralForumTopicUnhidden)
487
495
  wrap('write_access_allowed', WriteAccessAllowed)
496
+
497
+ # Bot API 9.6 - Managed Bot Support
498
+ wrap('managed_bot_created', ManagedBotCreated)
499
+ wrap('managed_bot', ManagedBotUpdated)
488
500
  end
489
501
  end
490
502
 
@@ -528,7 +540,15 @@ module Telegem
528
540
  end
529
541
  end
530
542
 
531
- class PollOption < BaseType; end
543
+ class PollOption < BaseType
544
+ def initialize(data)
545
+ super(data)
546
+ # Bot API 9.6 enhancements
547
+ wrap('added_by_user', User)
548
+ wrap('added_by_chat', Chat)
549
+ end
550
+ end
551
+
532
552
  class PollAnswer < BaseType; end
533
553
 
534
554
  class Poll < BaseType
@@ -675,6 +695,9 @@ module Telegem
675
695
  class VideoChatEnded < BaseType; end
676
696
  class VideoChatParticipantsInvited < BaseType; end
677
697
  class VideoChatLocation < BaseType; end
678
-
698
+ # Bot API 9.6 - Managed Bot Support
699
+ class ManagedBotCreated < BaseType; end
700
+ class ManagedBotDeleted < BaseType; end
701
+ class ManagedBotUpdated < BaseType; end
702
+ end
679
703
  end
680
- end
data/lib/core/composer.rb CHANGED
@@ -1,4 +1,4 @@
1
- # lib/core/composer.rb - HTTPX VERSION (NO ASYNC GEM)
1
+ # lib/core/composer.rb
2
2
  module Telegem
3
3
  module Core
4
4
  class Composer
@@ -40,7 +40,7 @@ module Telegem
40
40
  # Call the middleware with next in chain
41
41
  middleware_instance.call(context, next_middleware)
42
42
  else
43
- raise "Invalid middleware: #{middleware.class}"
43
+ raise "Invalid middleware: #{middleware.inspect} does not respond to :call"
44
44
  end
45
45
  end
46
46
  end
@@ -58,4 +58,4 @@ module Telegem
58
58
  end
59
59
  end
60
60
  end
61
- end
61
+ end
data/lib/core/context.rb CHANGED
@@ -436,16 +436,8 @@ module Telegem
436
436
  scene&.next_step(self, step_name)
437
437
  end
438
438
  def with_typing(&block)
439
- thread = Thread.new do
440
- while @typing_active
441
- typing
442
- sleep 5
443
- end
444
- end
445
- result = block.call
446
- @typing_active = false
447
- thread.join
448
- result
439
+ typing
440
+ block.call
449
441
  end
450
442
 
451
443
  def command?
@@ -463,6 +455,20 @@ module Telegem
463
455
  scene.enter(self, options[:step], options.except(:step))
464
456
  scene_name
465
457
  end
458
+
459
+ # Essential 9.6 methods for context.rb
460
+ def business_connection_id
461
+ message&.business_connection_id || @update.business_connection_id
462
+ end
463
+
464
+ def reply_draft(text, **options)
465
+ return unless chat
466
+ @bot.api.call('sendMessageDraft', { chat_id: chat.id, text: text }.merge(options))
467
+ end
468
+
469
+ def managed_bot
470
+ @update.managed_bot
471
+ end
466
472
 
467
473
  def logger
468
474
  @bot.logger
@@ -488,4 +494,4 @@ module Telegem
488
494
  end
489
495
  end
490
496
  end
491
- end
497
+ end
data/lib/plugins/cc ADDED
@@ -0,0 +1,3 @@
1
+ curl -H "Content-Type: application/json" \
2
+ -d '{"world": "hello world", "from": "es", "to": "fr"}' \
3
+ https://zen-drx-api.onrender.com
@@ -0,0 +1,43 @@
1
+ require "httparty"
2
+
3
+ module Telegem
4
+ module Plugins
5
+ class Translate
6
+ def initialize(word, from, to)
7
+ @word = word
8
+ @from = from
9
+ @to = to
10
+ start_translating
11
+ end
12
+
13
+ def start_translating
14
+ url = "https://zen-drx-api.onrender.com/api/translate"
15
+ options = {
16
+ body: {
17
+ word: @word,
18
+ from: @from,
19
+ to: @to
20
+ }.to_json,
21
+ headers: {
22
+ 'Content-Type' => 'application/json'
23
+ }
24
+ }
25
+ response = HTTParty.post(url, options)
26
+ if response.success?
27
+ translation = response.parsed_response["translation"]
28
+ {
29
+ error: "false",
30
+ translation: translation
31
+ }
32
+ else
33
+ {
34
+ error: "an error occurred",
35
+ code: "#{response.code}"
36
+ }
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+