telegem 1.0.6 → 2.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/docs/Api.md +146 -354
- data/docs/Cookbook(copy_paste).md +644 -0
- data/docs/Getting_started.md +348 -0
- data/docs/webhook_setup.md +199 -0
- data/lib/api/client.rb +123 -49
- data/lib/api/types.rb +283 -67
- data/lib/core/bot.rb +91 -56
- data/lib/core/context.rb +96 -110
- data/lib/markup/.gitkeep +0 -0
- data/lib/markup/keyboard.rb +53 -38
- data/lib/telegem.rb +15 -5
- data/lib/webhook/server.rb +246 -112
- metadata +108 -17
- data/docs/Cookbook.md +0 -407
- data/docs/SETTING_WEBHOOK.md +0 -367
- data/docs/UNDERSTANDING-WEBHOOK-n-POLLING.md +0 -241
data/lib/api/types.rb
CHANGED
|
@@ -3,16 +3,19 @@ module Telegem
|
|
|
3
3
|
class BaseType
|
|
4
4
|
def initialize(data)
|
|
5
5
|
@_raw_data = data || {}
|
|
6
|
+
@_accessors_defined = {}
|
|
6
7
|
end
|
|
7
8
|
|
|
8
9
|
def method_missing(name, *args)
|
|
9
|
-
|
|
10
|
-
return @_raw_data[key] if @_raw_data.key?(key)
|
|
10
|
+
return super if args.any? || block_given?
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
return @_raw_data[camel_key] if @_raw_data.key?(camel_key)
|
|
12
|
+
define_accessor(name)
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
if respond_to?(name)
|
|
15
|
+
send(name)
|
|
16
|
+
else
|
|
17
|
+
super
|
|
18
|
+
end
|
|
16
19
|
end
|
|
17
20
|
|
|
18
21
|
def respond_to_missing?(name, include_private = false)
|
|
@@ -21,30 +24,66 @@ module Telegem
|
|
|
21
24
|
@_raw_data.key?(key) || @_raw_data.key?(camel_key) || super
|
|
22
25
|
end
|
|
23
26
|
|
|
27
|
+
def to_h
|
|
28
|
+
@_raw_data.dup
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
alias_method :to_hash, :to_h
|
|
32
|
+
|
|
33
|
+
def inspect
|
|
34
|
+
"#<#{self.class.name} #{@_raw_data.inspect}>"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def to_s
|
|
38
|
+
inspect
|
|
39
|
+
end
|
|
40
|
+
|
|
24
41
|
attr_reader :_raw_data
|
|
25
42
|
|
|
26
43
|
private
|
|
27
44
|
|
|
45
|
+
def define_accessor(name)
|
|
46
|
+
return if @_accessors_defined[name]
|
|
47
|
+
|
|
48
|
+
key = name.to_s
|
|
49
|
+
camel_key = snake_to_camel(key)
|
|
50
|
+
|
|
51
|
+
if @_raw_data.key?(key)
|
|
52
|
+
define_singleton_method(name) { @_raw_data[key] }
|
|
53
|
+
elsif @_raw_data.key?(camel_key)
|
|
54
|
+
define_singleton_method(name) { @_raw_data[camel_key] }
|
|
55
|
+
else
|
|
56
|
+
define_singleton_method(name) do
|
|
57
|
+
raise NoMethodError,
|
|
58
|
+
"undefined method `#{name}' for #{self.class} with keys: #{@_raw_data.keys}"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
@_accessors_defined[name] = true
|
|
63
|
+
end
|
|
64
|
+
|
|
28
65
|
def snake_to_camel(str)
|
|
29
66
|
str.gsub(/_([a-z])/) { $1.upcase }
|
|
30
67
|
end
|
|
68
|
+
|
|
69
|
+
def camel_to_snake(str)
|
|
70
|
+
str.gsub(/([A-Z])/) { "_#{$1.downcase}" }.sub(/^_/, '')
|
|
71
|
+
end
|
|
31
72
|
end
|
|
32
73
|
|
|
33
74
|
class User < BaseType
|
|
34
|
-
|
|
35
|
-
|
|
75
|
+
COMMON_FIELDS = %w[id is_bot first_name last_name username
|
|
76
|
+
can_join_groups can_read_all_group_messages
|
|
77
|
+
supports_inline_queries language_code
|
|
78
|
+
is_premium added_to_attachment_menu
|
|
79
|
+
can_connect_to_business].freeze
|
|
36
80
|
|
|
37
81
|
def initialize(data)
|
|
38
82
|
super(data)
|
|
39
83
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@last_name = data['last_name']
|
|
44
|
-
@username = data['username']
|
|
45
|
-
@can_join_groups = data['can_join_groups']
|
|
46
|
-
@can_read_all_group_messages = data['can_read_all_group_messages']
|
|
47
|
-
@supports_inline_queries = data['supports_inline_queries']
|
|
84
|
+
COMMON_FIELDS.each do |field|
|
|
85
|
+
define_accessor(field.to_sym)
|
|
86
|
+
end
|
|
48
87
|
end
|
|
49
88
|
|
|
50
89
|
def full_name
|
|
@@ -52,20 +91,35 @@ module Telegem
|
|
|
52
91
|
end
|
|
53
92
|
|
|
54
93
|
def mention
|
|
55
|
-
|
|
94
|
+
if username
|
|
95
|
+
"@#{username}"
|
|
96
|
+
elsif first_name
|
|
97
|
+
first_name
|
|
98
|
+
else
|
|
99
|
+
"User ##{id}"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def to_s
|
|
104
|
+
full_name
|
|
56
105
|
end
|
|
57
106
|
end
|
|
58
107
|
|
|
59
108
|
class Chat < BaseType
|
|
60
|
-
|
|
109
|
+
COMMON_FIELDS = %w[id type username title first_name last_name
|
|
110
|
+
photo bio has_private_forwards
|
|
111
|
+
has_restricted_voice_and_video_messages
|
|
112
|
+
description invite_link pinned_message
|
|
113
|
+
permissions slow_mode_delay message_auto_delete_time
|
|
114
|
+
has_protected_content sticker_set_name
|
|
115
|
+
can_set_sticker_set linked_chat_id location].freeze
|
|
61
116
|
|
|
62
117
|
def initialize(data)
|
|
63
118
|
super(data)
|
|
64
119
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
@title = data['title']
|
|
120
|
+
COMMON_FIELDS.each do |field|
|
|
121
|
+
define_accessor(field.to_sym)
|
|
122
|
+
end
|
|
69
123
|
end
|
|
70
124
|
|
|
71
125
|
def private?
|
|
@@ -83,58 +137,62 @@ module Telegem
|
|
|
83
137
|
def channel?
|
|
84
138
|
type == 'channel'
|
|
85
139
|
end
|
|
140
|
+
|
|
141
|
+
def to_s
|
|
142
|
+
title || username || "Chat ##{id}"
|
|
143
|
+
end
|
|
86
144
|
end
|
|
87
145
|
|
|
88
146
|
class MessageEntity < BaseType
|
|
89
|
-
|
|
147
|
+
COMMON_FIELDS = %w[type offset length url user language
|
|
148
|
+
custom_emoji_id].freeze
|
|
90
149
|
|
|
91
150
|
def initialize(data)
|
|
92
151
|
super(data)
|
|
93
152
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
@
|
|
99
|
-
|
|
153
|
+
COMMON_FIELDS.each do |field|
|
|
154
|
+
define_accessor(field.to_sym)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
if @_raw_data['user'] && !@_raw_data['user'].is_a?(User)
|
|
158
|
+
@_raw_data['user'] = User.new(@_raw_data['user'])
|
|
159
|
+
end
|
|
100
160
|
end
|
|
101
161
|
end
|
|
102
162
|
|
|
103
163
|
class Message < BaseType
|
|
104
|
-
|
|
105
|
-
|
|
164
|
+
COMMON_FIELDS = %w[message_id from chat date edit_date
|
|
165
|
+
text caption entities caption_entities
|
|
166
|
+
audio document photo sticker video voice
|
|
167
|
+
video_note contact location venue
|
|
168
|
+
new_chat_members left_chat_member
|
|
169
|
+
new_chat_title new_chat_photo
|
|
170
|
+
delete_chat_photo group_chat_created
|
|
171
|
+
supergroup_chat_created channel_chat_created
|
|
172
|
+
migrate_to_chat_id migrate_from_chat_id
|
|
173
|
+
pinned_message invoice successful_payment
|
|
174
|
+
connected_website reply_markup via_bot
|
|
175
|
+
forward_from forward_from_chat
|
|
176
|
+
forward_from_message_id forward_signature
|
|
177
|
+
forward_sender_name forward_date reply_to_message
|
|
178
|
+
media_group_id author_signature
|
|
179
|
+
has_protected_content].freeze
|
|
106
180
|
|
|
107
181
|
def initialize(data)
|
|
108
182
|
super(data)
|
|
109
183
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
@chat = Chat.new(data['chat']) if data['chat']
|
|
113
|
-
@date = Time.at(data['date']) if data['date']
|
|
114
|
-
@text = data['text']
|
|
115
|
-
|
|
116
|
-
if data['entities']
|
|
117
|
-
@entities = data['entities'].map { |e| MessageEntity.new(e) }
|
|
184
|
+
COMMON_FIELDS.each do |field|
|
|
185
|
+
define_accessor(field.to_sym)
|
|
118
186
|
end
|
|
119
187
|
|
|
120
|
-
|
|
121
|
-
@via_bot = User.new(data['via_bot']) if data['via_bot']
|
|
122
|
-
@forward_from = User.new(data['forward_from']) if data['forward_from']
|
|
123
|
-
@forward_from_chat = Chat.new(data['forward_from_chat']) if data['forward_from_chat']
|
|
188
|
+
convert_complex_fields
|
|
124
189
|
end
|
|
125
190
|
|
|
126
|
-
# FIXED: Proper command detection
|
|
127
191
|
def command?
|
|
128
|
-
return false unless text
|
|
129
|
-
return false unless entities
|
|
130
|
-
|
|
131
|
-
# Find a "bot_command" entity
|
|
132
|
-
command_entity = entities.find { |e| e.type == 'bot_command' }
|
|
133
|
-
return false unless command_entity
|
|
192
|
+
return false unless text && entities
|
|
134
193
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
command_text&.start_with?('/')
|
|
194
|
+
entities.any? { |e| e.type == 'bot_command' &&
|
|
195
|
+
text[e.offset, e.length]&.start_with?('/') }
|
|
138
196
|
end
|
|
139
197
|
|
|
140
198
|
def command_name
|
|
@@ -144,9 +202,10 @@ module Telegem
|
|
|
144
202
|
return nil unless command_entity
|
|
145
203
|
|
|
146
204
|
cmd = text[command_entity.offset, command_entity.length]
|
|
147
|
-
cmd
|
|
148
|
-
|
|
149
|
-
cmd
|
|
205
|
+
return nil if cmd.nil? || cmd.length <= 1
|
|
206
|
+
|
|
207
|
+
cmd = cmd[1..-1]
|
|
208
|
+
cmd.split('@').first.strip
|
|
150
209
|
end
|
|
151
210
|
|
|
152
211
|
def command_args
|
|
@@ -155,35 +214,192 @@ module Telegem
|
|
|
155
214
|
command_entity = entities.find { |e| e.type == 'bot_command' }
|
|
156
215
|
return nil unless command_entity
|
|
157
216
|
|
|
158
|
-
# Text after the command entity
|
|
159
217
|
args_start = command_entity.offset + command_entity.length
|
|
160
|
-
text[args_start..-1]
|
|
218
|
+
remaining = text[args_start..-1]
|
|
219
|
+
|
|
220
|
+
next_entity = entities.select { |e| e.offset >= args_start }
|
|
221
|
+
.min_by(&:offset)
|
|
222
|
+
|
|
223
|
+
if next_entity
|
|
224
|
+
args_end = next_entity.offset - 1
|
|
225
|
+
text[args_start..args_end]&.strip
|
|
226
|
+
else
|
|
227
|
+
remaining&.strip
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def reply?
|
|
232
|
+
!!reply_to_message
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def has_media?
|
|
236
|
+
!!(audio || document || photo || video || voice || video_note || sticker)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def media_type
|
|
240
|
+
return :audio if audio
|
|
241
|
+
return :document if document
|
|
242
|
+
return :photo if photo
|
|
243
|
+
return :video if video
|
|
244
|
+
return :voice if voice
|
|
245
|
+
return :video_note if video_note
|
|
246
|
+
return :sticker if sticker
|
|
247
|
+
nil
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
private
|
|
251
|
+
|
|
252
|
+
def convert_complex_fields
|
|
253
|
+
if @_raw_data['date'] && !@_raw_data['date'].is_a?(Time)
|
|
254
|
+
@_raw_data['date'] = Time.at(@_raw_data['date'])
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
if @_raw_data['edit_date'] && !@_raw_data['edit_date'].is_a?(Time)
|
|
258
|
+
@_raw_data['edit_date'] = Time.at(@_raw_data['edit_date'])
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
if @_raw_data['forward_date'] && !@_raw_data['forward_date'].is_a?(Time)
|
|
262
|
+
@_raw_data['forward_date'] = Time.at(@_raw_data['forward_date'])
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
@_raw_data['from'] = User.new(@_raw_data['from']) if @_raw_data['from'] && !@_raw_data['from'].is_a?(User)
|
|
266
|
+
@_raw_data['chat'] = Chat.new(@_raw_data['chat']) if @_raw_data['chat'] && !@_raw_data['chat'].is_a?(Chat)
|
|
267
|
+
@_raw_data['via_bot'] = User.new(@_raw_data['via_bot']) if @_raw_data['via_bot'] && !@_raw_data['via_bot'].is_a?(User)
|
|
268
|
+
@_raw_data['forward_from'] = User.new(@_raw_data['forward_from']) if @_raw_data['forward_from'] && !@_raw_data['forward_from'].is_a?(User)
|
|
269
|
+
@_raw_data['forward_from_chat'] = Chat.new(@_raw_data['forward_from_chat']) if @_raw_data['forward_from_chat'] && !@_raw_data['forward_from_chat'].is_a?(Chat)
|
|
270
|
+
@_raw_data['left_chat_member'] = User.new(@_raw_data['left_chat_member']) if @_raw_data['left_chat_member'] && !@_raw_data['left_chat_member'].is_a?(User)
|
|
271
|
+
|
|
272
|
+
if @_raw_data['entities'] && @_raw_data['entities'].is_a?(Array)
|
|
273
|
+
@_raw_data['entities'] = @_raw_data['entities'].map do |e|
|
|
274
|
+
e.is_a?(MessageEntity) ? e : MessageEntity.new(e)
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
if @_raw_data['caption_entities'] && @_raw_data['caption_entities'].is_a?(Array)
|
|
279
|
+
@_raw_data['caption_entities'] = @_raw_data['caption_entities'].map do |e|
|
|
280
|
+
e.is_a?(MessageEntity) ? e : MessageEntity.new(e)
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
if @_raw_data['reply_to_message'] && !@_raw_data['reply_to_message'].is_a?(Message)
|
|
285
|
+
@_raw_data['reply_to_message'] = Message.new(@_raw_data['reply_to_message'])
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
if @_raw_data['pinned_message'] && !@_raw_data['pinned_message'].is_a?(Message)
|
|
289
|
+
@_raw_data['pinned_message'] = Message.new(@_raw_data['pinned_message'])
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
if @_raw_data['new_chat_members'] && @_raw_data['new_chat_members'].is_a?(Array)
|
|
293
|
+
@_raw_data['new_chat_members'] = @_raw_data['new_chat_members'].map do |u|
|
|
294
|
+
u.is_a?(User) ? u : User.new(u)
|
|
295
|
+
end
|
|
296
|
+
end
|
|
161
297
|
end
|
|
162
298
|
end
|
|
163
299
|
|
|
164
300
|
class CallbackQuery < BaseType
|
|
165
|
-
|
|
301
|
+
COMMON_FIELDS = %w[id from message inline_message_id chat_instance data game_short_name].freeze
|
|
166
302
|
|
|
167
303
|
def initialize(data)
|
|
168
304
|
super(data)
|
|
169
305
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
@
|
|
306
|
+
COMMON_FIELDS.each do |field|
|
|
307
|
+
define_accessor(field.to_sym)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
if @_raw_data['from'] && !@_raw_data['from'].is_a?(User)
|
|
311
|
+
@_raw_data['from'] = User.new(@_raw_data['from'])
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
if @_raw_data['message'] && !@_raw_data['message'].is_a?(Message)
|
|
315
|
+
@_raw_data['message'] = Message.new(@_raw_data['message'])
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def from_user?
|
|
320
|
+
!!from
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def message?
|
|
324
|
+
!!message
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def inline_message?
|
|
328
|
+
!!inline_message_id
|
|
175
329
|
end
|
|
176
330
|
end
|
|
177
331
|
|
|
178
332
|
class Update < BaseType
|
|
179
|
-
|
|
333
|
+
COMMON_FIELDS = %w[update_id message edited_message channel_post
|
|
334
|
+
edited_channel_post inline_query chosen_inline_result
|
|
335
|
+
callback_query shipping_query pre_checkout_query
|
|
336
|
+
poll poll_answer my_chat_member chat_member
|
|
337
|
+
chat_join_request].freeze
|
|
180
338
|
|
|
181
339
|
def initialize(data)
|
|
182
340
|
super(data)
|
|
183
341
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
342
|
+
COMMON_FIELDS.each do |field|
|
|
343
|
+
define_accessor(field.to_sym)
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
convert_update_objects
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def type
|
|
350
|
+
return :message if message
|
|
351
|
+
return :edited_message if edited_message
|
|
352
|
+
return :channel_post if channel_post
|
|
353
|
+
return :edited_channel_post if edited_channel_post
|
|
354
|
+
return :inline_query if inline_query
|
|
355
|
+
return :chosen_inline_result if chosen_inline_result
|
|
356
|
+
return :callback_query if callback_query
|
|
357
|
+
return :shipping_query if shipping_query
|
|
358
|
+
return :pre_checkout_query if pre_checkout_query
|
|
359
|
+
return :poll if poll
|
|
360
|
+
return :poll_answer if poll_answer
|
|
361
|
+
return :my_chat_member if my_chat_member
|
|
362
|
+
return :chat_member if chat_member
|
|
363
|
+
return :chat_join_request if chat_join_request
|
|
364
|
+
:unknown
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def from
|
|
368
|
+
case type
|
|
369
|
+
when :message, :edited_message
|
|
370
|
+
message.from
|
|
371
|
+
when :channel_post, :edited_channel_post
|
|
372
|
+
channel_post.from
|
|
373
|
+
when :inline_query
|
|
374
|
+
inline_query.from
|
|
375
|
+
when :chosen_inline_result
|
|
376
|
+
chosen_inline_result.from
|
|
377
|
+
when :callback_query
|
|
378
|
+
callback_query.from
|
|
379
|
+
when :shipping_query
|
|
380
|
+
shipping_query.from
|
|
381
|
+
when :pre_checkout_query
|
|
382
|
+
pre_checkout_query.from
|
|
383
|
+
when :my_chat_member, :chat_member
|
|
384
|
+
my_chat_member&.from || chat_member&.from
|
|
385
|
+
when :chat_join_request
|
|
386
|
+
chat_join_request.from
|
|
387
|
+
else
|
|
388
|
+
nil
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
private
|
|
393
|
+
|
|
394
|
+
def convert_update_objects
|
|
395
|
+
@_raw_data['message'] = Message.new(@_raw_data['message']) if @_raw_data['message'] && !@_raw_data['message'].is_a?(Message)
|
|
396
|
+
@_raw_data['edited_message'] = Message.new(@_raw_data['edited_message']) if @_raw_data['edited_message'] && !@_raw_data['edited_message'].is_a?(Message)
|
|
397
|
+
@_raw_data['channel_post'] = Message.new(@_raw_data['channel_post']) if @_raw_data['channel_post'] && !@_raw_data['channel_post'].is_a?(Message)
|
|
398
|
+
@_raw_data['edited_channel_post'] = Message.new(@_raw_data['edited_channel_post']) if @_raw_data['edited_channel_post'] && !@_raw_data['edited_channel_post'].is_a?(Message)
|
|
399
|
+
|
|
400
|
+
if @_raw_data['callback_query'] && !@_raw_data['callback_query'].is_a?(CallbackQuery)
|
|
401
|
+
@_raw_data['callback_query'] = CallbackQuery.new(@_raw_data['callback_query'])
|
|
402
|
+
end
|
|
187
403
|
end
|
|
188
404
|
end
|
|
189
405
|
end
|