telegem 3.3.0 → 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.
- checksums.yaml +4 -4
- data/.rubocop.yml +57 -0
- data/CHANGELOG.md +121 -0
- data/Gemfile +1 -1
- data/README.md +147 -0
- data/bin/telegem-ssl +71 -25
- data/contributing.md +375 -0
- data/docs/api.md +663 -0
- data/docs/bot.md +332 -0
- data/docs/changelog.md +182 -0
- data/docs/context.md +554 -0
- data/docs/core_concepts.md +218 -0
- data/docs/deployment.md +702 -0
- data/docs/error_handling.md +435 -0
- data/docs/examples.md +752 -0
- data/docs/getting_started.md +151 -0
- data/docs/handlers.md +580 -0
- data/docs/keyboards.md +446 -0
- data/docs/middleware.md +536 -0
- data/docs/plugins.md +384 -0
- data/docs/scenes.md +517 -0
- data/docs/sessions.md +544 -0
- data/docs/testing.md +612 -0
- data/docs/troubleshooting.md +574 -0
- data/docs/types.md +538 -0
- data/docs/webhooks.md +456 -0
- data/lib/api/client.rb +38 -10
- data/lib/api/types.rb +433 -172
- data/lib/core/composer.rb +3 -3
- data/lib/core/context.rb +17 -11
- data/lib/plugins/cc +3 -0
- data/lib/plugins/file_extract.rb +2 -2
- data/lib/plugins/translate.rb +43 -0
- data/lib/session/memory_store.rb +90 -103
- data/lib/session/redis.rb +91 -0
- data/lib/telegem.rb +4 -4
- data/lib/webhook/server.rb +5 -3
- metadata +51 -35
- data/CHANGELOG +0 -95
- data/Contributing.md +0 -161
- data/Readme.md +0 -302
- data/examples/.gitkeep +0 -0
- data/public/.gitkeep +0 -0
data/docs/context.md
ADDED
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
# Context Object (ctx)
|
|
2
|
+
|
|
3
|
+
The Context object is the heart of every Telegem bot handler. It encapsulates the current update and provides methods to interact with Telegram's API.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Every handler receives a `ctx` (context) parameter containing:
|
|
8
|
+
|
|
9
|
+
- Current update information
|
|
10
|
+
- Bot instance reference
|
|
11
|
+
- User and chat data
|
|
12
|
+
- Session data
|
|
13
|
+
- Response methods
|
|
14
|
+
- Utility helpers
|
|
15
|
+
|
|
16
|
+
## Core Properties
|
|
17
|
+
|
|
18
|
+
### Update Information
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
ctx.update # Raw Update object (Telegem::Types::Update)
|
|
22
|
+
ctx.update_id # Unique update identifier
|
|
23
|
+
ctx.update_type # :message, :callback_query, :inline_query, etc.
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Bot Reference
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
ctx.bot # Bot instance
|
|
30
|
+
ctx.api # Direct API client access
|
|
31
|
+
ctx.logger # Bot's logger
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### State Management
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
ctx.state # Temporary state (cleared after handler)
|
|
38
|
+
ctx.session # Persistent user session
|
|
39
|
+
ctx.match # Regex match data from hears/command
|
|
40
|
+
ctx.scene # Current scene data
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## User and Chat Information
|
|
44
|
+
|
|
45
|
+
### User Data
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
ctx.from # User object (Telegem::Types::User)
|
|
49
|
+
ctx.from.id # User ID (integer)
|
|
50
|
+
ctx.from.username # Username (string, may be nil)
|
|
51
|
+
ctx.from.first_name # First name
|
|
52
|
+
ctx.from.last_name # Last name (may be nil)
|
|
53
|
+
ctx.from.language_code # User's language ('en', 'es', etc.)
|
|
54
|
+
ctx.from.is_bot # Boolean: is this user a bot?
|
|
55
|
+
ctx.from.is_premium # Boolean: has Telegram Premium?
|
|
56
|
+
|
|
57
|
+
# Convenience methods
|
|
58
|
+
ctx.user_id # Shortcut for ctx.from&.id
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Chat Data
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
ctx.chat # Chat object (Telegem::Types::Chat)
|
|
65
|
+
ctx.chat.id # Chat ID
|
|
66
|
+
ctx.chat.type # 'private', 'group', 'supergroup', 'channel'
|
|
67
|
+
ctx.chat.title # Chat title (for groups/channels)
|
|
68
|
+
ctx.chat.username # Chat username (for public chats)
|
|
69
|
+
|
|
70
|
+
# Type checking
|
|
71
|
+
ctx.chat.private? # Is private chat?
|
|
72
|
+
ctx.chat.group? # Is group?
|
|
73
|
+
ctx.chat.supergroup? # Is supergroup?
|
|
74
|
+
ctx.chat.channel? # Is channel?
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Message Access
|
|
78
|
+
|
|
79
|
+
### Basic Message Properties
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
ctx.message # Message object (Telegem::Types::Message)
|
|
83
|
+
ctx.message.message_id # Message ID
|
|
84
|
+
ctx.message.date # Send date (Time object)
|
|
85
|
+
ctx.message.edit_date # Edit date (Time object, if edited)
|
|
86
|
+
ctx.message.text # Message text
|
|
87
|
+
ctx.message.caption # Media caption
|
|
88
|
+
|
|
89
|
+
# Convenience shortcuts
|
|
90
|
+
ctx.text # ctx.message&.text
|
|
91
|
+
ctx.caption # ctx.message&.caption
|
|
92
|
+
ctx.message_id # ctx.message&.message_id
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Message Metadata
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
ctx.message.from # Sender (same as ctx.from)
|
|
99
|
+
ctx.message.chat # Chat (same as ctx.chat)
|
|
100
|
+
ctx.message.reply_to_message # Replied-to message
|
|
101
|
+
ctx.message.forward_from # Original sender (if forwarded)
|
|
102
|
+
ctx.message.entities # Text formatting entities
|
|
103
|
+
ctx.message.caption_entities # Caption formatting entities
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Reply Information
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
ctx.reply? # Is this a reply?
|
|
110
|
+
ctx.replied_message # Message being replied to
|
|
111
|
+
ctx.replied_text # Text of replied message
|
|
112
|
+
ctx.replied_from # User who sent replied message
|
|
113
|
+
ctx.replied_chat # Chat of replied message
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Command Processing
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
ctx.command? # Is message a command?
|
|
120
|
+
ctx.command_name # Command name ('start', 'help')
|
|
121
|
+
ctx.command_args # Arguments string after command
|
|
122
|
+
|
|
123
|
+
# Example: /ban @user reason
|
|
124
|
+
ctx.command_name # => 'ban'
|
|
125
|
+
ctx.command_args # => '@user reason'
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Media Detection
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
ctx.has_media? # Has any media attachment?
|
|
132
|
+
ctx.media_type # :photo, :document, :audio, :video, :voice, :sticker
|
|
133
|
+
|
|
134
|
+
# Specific media
|
|
135
|
+
ctx.message.photo # Array of PhotoSize objects
|
|
136
|
+
ctx.message.document # Document object
|
|
137
|
+
ctx.message.audio # Audio object
|
|
138
|
+
ctx.message.video # Video object
|
|
139
|
+
ctx.message.voice # Voice object
|
|
140
|
+
ctx.message.sticker # Sticker object
|
|
141
|
+
ctx.message.animation # Animation object
|
|
142
|
+
ctx.message.video_note # VideoNote object
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Update Type Detection
|
|
146
|
+
|
|
147
|
+
### Callback Queries
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
ctx.callback_query? # Is callback query update?
|
|
151
|
+
ctx.callback_query # CallbackQuery object
|
|
152
|
+
ctx.data # Callback data string
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Inline Queries
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
ctx.inline_query? # Is inline query update?
|
|
159
|
+
ctx.inline_query # InlineQuery object
|
|
160
|
+
ctx.query # Search query string
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Other Update Types
|
|
164
|
+
|
|
165
|
+
```ruby
|
|
166
|
+
ctx.poll? # Poll update
|
|
167
|
+
ctx.poll_answer? # Poll answer update
|
|
168
|
+
ctx.chat_member? # Chat member update
|
|
169
|
+
ctx.my_chat_member? # Bot's membership update
|
|
170
|
+
ctx.chat_join_request? # Join request update
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Sending Messages
|
|
174
|
+
|
|
175
|
+
### Basic Text Replies
|
|
176
|
+
|
|
177
|
+
```ruby
|
|
178
|
+
ctx.reply("Hello world!")
|
|
179
|
+
ctx.reply("Hello", parse_mode: "Markdown")
|
|
180
|
+
ctx.reply("Hello", disable_web_page_preview: true)
|
|
181
|
+
ctx.reply("Hello", reply_to_message_id: 123)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Media Messages
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
# Photos
|
|
188
|
+
ctx.photo("https://example.com/image.jpg")
|
|
189
|
+
ctx.photo(File.open("local.jpg"))
|
|
190
|
+
ctx.photo(file_id, caption: "Caption")
|
|
191
|
+
|
|
192
|
+
# Documents
|
|
193
|
+
ctx.document("file.pdf", caption: "Report")
|
|
194
|
+
ctx.document(File.open("report.pdf"))
|
|
195
|
+
|
|
196
|
+
# Audio/Video
|
|
197
|
+
ctx.audio("song.mp3", title: "Song", performer: "Artist")
|
|
198
|
+
ctx.video("video.mp4", caption: "Video")
|
|
199
|
+
ctx.voice("voice.ogg")
|
|
200
|
+
ctx.sticker(sticker_file_id)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Location and Contact
|
|
204
|
+
|
|
205
|
+
```ruby
|
|
206
|
+
ctx.location(37.7749, -122.4194) # Latitude, longitude
|
|
207
|
+
ctx.contact("+1234567890", "John", last_name: "Doe")
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Chat Actions
|
|
211
|
+
|
|
212
|
+
```ruby
|
|
213
|
+
ctx.typing
|
|
214
|
+
ctx.uploading_photo
|
|
215
|
+
ctx.uploading_video
|
|
216
|
+
ctx.uploading_document
|
|
217
|
+
ctx.find_location
|
|
218
|
+
ctx.record_video
|
|
219
|
+
ctx.record_audio
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Keyboard Markup
|
|
223
|
+
|
|
224
|
+
### Reply Keyboards
|
|
225
|
+
|
|
226
|
+
```ruby
|
|
227
|
+
keyboard = Telegem.keyboard do
|
|
228
|
+
row "Button 1", "Button 2"
|
|
229
|
+
row "Button 3"
|
|
230
|
+
end.resize.one_time
|
|
231
|
+
|
|
232
|
+
ctx.reply("Choose:", reply_markup: keyboard)
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Inline Keyboards
|
|
236
|
+
|
|
237
|
+
```ruby
|
|
238
|
+
inline = Telegem.inline do
|
|
239
|
+
row callback("Yes", "yes"), callback("No", "no")
|
|
240
|
+
row url("Visit", "https://example.com")
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
ctx.reply("Confirm?", reply_markup: inline)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Removing Keyboards
|
|
247
|
+
|
|
248
|
+
```ruby
|
|
249
|
+
ctx.remove_keyboard
|
|
250
|
+
ctx.remove_keyboard("Done!")
|
|
251
|
+
ctx.remove_keyboard(selective: true)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Message Editing
|
|
255
|
+
|
|
256
|
+
```ruby
|
|
257
|
+
ctx.edit_message_text("New text")
|
|
258
|
+
ctx.edit_message_text("New text", message_id: 123)
|
|
259
|
+
ctx.edit_message_caption("New caption")
|
|
260
|
+
ctx.edit_message_reply_markup(new_keyboard)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Message Deletion
|
|
264
|
+
|
|
265
|
+
```ruby
|
|
266
|
+
ctx.delete_message # Delete current message
|
|
267
|
+
ctx.delete_message(123) # Delete specific message
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Callback Query Responses
|
|
271
|
+
|
|
272
|
+
```ruby
|
|
273
|
+
ctx.answer_callback_query("Done!")
|
|
274
|
+
ctx.answer_callback_query("Error!", show_alert: true)
|
|
275
|
+
ctx.answer_callback_query(url: "https://example.com")
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Inline Query Responses
|
|
279
|
+
|
|
280
|
+
```ruby
|
|
281
|
+
results = [
|
|
282
|
+
Telegem::Types::InlineQueryResultArticle.new(
|
|
283
|
+
id: "1",
|
|
284
|
+
title: "Article",
|
|
285
|
+
input_message_content: { message_text: "Content" }
|
|
286
|
+
)
|
|
287
|
+
]
|
|
288
|
+
|
|
289
|
+
ctx.answer_inline_query(results)
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## File Operations
|
|
293
|
+
|
|
294
|
+
```ruby
|
|
295
|
+
# Download files
|
|
296
|
+
ctx.download_file(file_id) # Returns content
|
|
297
|
+
ctx.download_file(file_id, "path/to/file") # Saves to file
|
|
298
|
+
|
|
299
|
+
# Get file info
|
|
300
|
+
file_info = ctx.api.call('getFile', file_id: file_id)
|
|
301
|
+
file_path = file_info['file_path']
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Chat Management
|
|
305
|
+
|
|
306
|
+
### Member Management
|
|
307
|
+
|
|
308
|
+
```ruby
|
|
309
|
+
ctx.kick_chat_member(user_id)
|
|
310
|
+
ctx.ban_chat_member(user_id, until_date: future_time)
|
|
311
|
+
ctx.unban_chat_member(user_id)
|
|
312
|
+
ctx.restrict_chat_member(user_id, permissions: { can_send_messages: false })
|
|
313
|
+
ctx.promote_chat_member(user_id, can_invite_users: true)
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Chat Information
|
|
317
|
+
|
|
318
|
+
```ruby
|
|
319
|
+
admins = ctx.get_chat_administrators
|
|
320
|
+
member = ctx.get_chat_member(user_id)
|
|
321
|
+
count = ctx.get_chat_members_count
|
|
322
|
+
chat_info = ctx.get_chat
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Message Pinning
|
|
326
|
+
|
|
327
|
+
```ruby
|
|
328
|
+
ctx.pin_message(message_id)
|
|
329
|
+
ctx.unpin_message(message_id)
|
|
330
|
+
ctx.unpin_all_messages
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Forwarding and Copying
|
|
334
|
+
|
|
335
|
+
```ruby
|
|
336
|
+
ctx.forward_message(from_chat_id, message_id)
|
|
337
|
+
ctx.copy_message(from_chat_id, message_id, caption: "New caption")
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Scene Management
|
|
341
|
+
|
|
342
|
+
```ruby
|
|
343
|
+
ctx.enter_scene(:scene_name)
|
|
344
|
+
ctx.leave_scene
|
|
345
|
+
ctx.leave_scene(reason: :completed)
|
|
346
|
+
ctx.in_scene?
|
|
347
|
+
ctx.current_scene
|
|
348
|
+
ctx.scene_data
|
|
349
|
+
ctx.ask("Question?")
|
|
350
|
+
ctx.next_step
|
|
351
|
+
ctx.next_step(:specific_step)
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## Session Management
|
|
355
|
+
|
|
356
|
+
```ruby
|
|
357
|
+
# Store data
|
|
358
|
+
ctx.session[:user_data] = "value"
|
|
359
|
+
ctx.session[:preferences] = { theme: "dark" }
|
|
360
|
+
|
|
361
|
+
# Retrieve data
|
|
362
|
+
data = ctx.session[:user_data]
|
|
363
|
+
prefs = ctx.session[:preferences]
|
|
364
|
+
|
|
365
|
+
# Modify data
|
|
366
|
+
ctx.session[:counter] ||= 0
|
|
367
|
+
ctx.session[:counter] += 1
|
|
368
|
+
|
|
369
|
+
# Clean up
|
|
370
|
+
ctx.session.delete(:temp_key)
|
|
371
|
+
ctx.session.clear
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## Polls
|
|
375
|
+
|
|
376
|
+
```ruby
|
|
377
|
+
ctx.send_poll("Question?", ["Option 1", "Option 2"])
|
|
378
|
+
ctx.stop_poll(message_id)
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## Web Apps
|
|
382
|
+
|
|
383
|
+
```ruby
|
|
384
|
+
ctx.web_app_data # Data from web app interactions
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
## Advanced Usage
|
|
388
|
+
|
|
389
|
+
### Conditional Responses
|
|
390
|
+
|
|
391
|
+
```ruby
|
|
392
|
+
if ctx.chat.private?
|
|
393
|
+
ctx.reply("This is private")
|
|
394
|
+
elsif ctx.chat.group?
|
|
395
|
+
ctx.reply("This is a group", reply_to_message_id: ctx.message_id)
|
|
396
|
+
end
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Error Handling in Handlers
|
|
400
|
+
|
|
401
|
+
```ruby
|
|
402
|
+
bot.command('process') do |ctx|
|
|
403
|
+
begin
|
|
404
|
+
# Process something risky
|
|
405
|
+
result = process_data(ctx.text)
|
|
406
|
+
ctx.reply("Result: #{result}")
|
|
407
|
+
rescue => e
|
|
408
|
+
ctx.logger.error("Processing error: #{e.message}")
|
|
409
|
+
ctx.reply("Sorry, processing failed. Try again.")
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Complex Keyboard Building
|
|
415
|
+
|
|
416
|
+
```ruby
|
|
417
|
+
keyboard = Telegem.keyboard do
|
|
418
|
+
row "📅 Today", "📅 Tomorrow"
|
|
419
|
+
row "⚙️ Settings", "❓ Help"
|
|
420
|
+
request_location "📍 Location"
|
|
421
|
+
request_contact "📞 Contact"
|
|
422
|
+
end.resize.selective
|
|
423
|
+
|
|
424
|
+
ctx.reply("What would you like to do?", reply_markup: keyboard)
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Session-Based Personalization
|
|
428
|
+
|
|
429
|
+
```ruby
|
|
430
|
+
bot.command('start') do |ctx|
|
|
431
|
+
ctx.session[:name] ||= ctx.from.first_name
|
|
432
|
+
ctx.session[:visit_count] ||= 0
|
|
433
|
+
ctx.session[:visit_count] += 1
|
|
434
|
+
|
|
435
|
+
greeting = if ctx.session[:visit_count] == 1
|
|
436
|
+
"Welcome, #{ctx.session[:name]}!"
|
|
437
|
+
else
|
|
438
|
+
"Welcome back, #{ctx.session[:name]}! (Visit ##{ctx.session[:visit_count]})"
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
ctx.reply(greeting)
|
|
442
|
+
end
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
## Edge Cases and Error Handling
|
|
446
|
+
|
|
447
|
+
### Missing Data
|
|
448
|
+
|
|
449
|
+
```ruby
|
|
450
|
+
# Handle missing user
|
|
451
|
+
if ctx.from
|
|
452
|
+
ctx.reply("Hello #{ctx.from.first_name}")
|
|
453
|
+
else
|
|
454
|
+
ctx.reply("Hello anonymous user")
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
# Handle missing message text
|
|
458
|
+
text = ctx.text || "no text"
|
|
459
|
+
ctx.reply("You said: #{text}")
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Media Validation
|
|
463
|
+
|
|
464
|
+
```ruby
|
|
465
|
+
bot.on(:message) do |ctx|
|
|
466
|
+
if ctx.has_media?
|
|
467
|
+
case ctx.media_type
|
|
468
|
+
when :photo
|
|
469
|
+
ctx.reply("Nice photo!")
|
|
470
|
+
when :document
|
|
471
|
+
if ctx.message.document.mime_type&.start_with?('application/pdf')
|
|
472
|
+
ctx.reply("PDF received")
|
|
473
|
+
else
|
|
474
|
+
ctx.reply("Unsupported document type")
|
|
475
|
+
end
|
|
476
|
+
else
|
|
477
|
+
ctx.reply("Unsupported media type")
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Rate Limiting
|
|
484
|
+
|
|
485
|
+
```ruby
|
|
486
|
+
bot.use do |ctx, next_middleware|
|
|
487
|
+
user_id = ctx.from&.id
|
|
488
|
+
if user_id
|
|
489
|
+
# Implement rate limiting logic
|
|
490
|
+
# ...
|
|
491
|
+
end
|
|
492
|
+
next_middleware.call(ctx)
|
|
493
|
+
end
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
### Large Messages
|
|
497
|
+
|
|
498
|
+
Telegram has limits:
|
|
499
|
+
- Text messages: 4096 characters
|
|
500
|
+
- Captions: 1024 characters
|
|
501
|
+
- Handle truncation:
|
|
502
|
+
|
|
503
|
+
```ruby
|
|
504
|
+
def truncate_text(text, max_length = 4000)
|
|
505
|
+
if text.length > max_length
|
|
506
|
+
text[0...max_length] + "..."
|
|
507
|
+
else
|
|
508
|
+
text
|
|
509
|
+
end
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
ctx.reply(truncate_text(long_text))
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### File Size Limits
|
|
516
|
+
|
|
517
|
+
```ruby
|
|
518
|
+
bot.document do |ctx|
|
|
519
|
+
doc = ctx.message.document
|
|
520
|
+
if doc.file_size > 50 * 1024 * 1024 # 50MB
|
|
521
|
+
ctx.reply("File too large (max 50MB)")
|
|
522
|
+
else
|
|
523
|
+
# Process file
|
|
524
|
+
end
|
|
525
|
+
end
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
### Timeouts and Async Operations
|
|
529
|
+
|
|
530
|
+
```ruby
|
|
531
|
+
bot.command('long_process') do |ctx|
|
|
532
|
+
ctx.reply("Processing...")
|
|
533
|
+
|
|
534
|
+
# For long operations, use async
|
|
535
|
+
Async do
|
|
536
|
+
result = long_running_task(ctx.text)
|
|
537
|
+
ctx.reply("Done: #{result}")
|
|
538
|
+
end
|
|
539
|
+
end
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
## Best Practices
|
|
543
|
+
|
|
544
|
+
1. **Always check for nil values** before accessing properties
|
|
545
|
+
2. **Use convenience methods** when available (ctx.text vs ctx.message&.text)
|
|
546
|
+
3. **Handle errors gracefully** in handlers
|
|
547
|
+
4. **Validate user input** before processing
|
|
548
|
+
5. **Use sessions sparingly** and with TTL
|
|
549
|
+
6. **Implement rate limiting** for intensive operations
|
|
550
|
+
7. **Log important actions** for debugging
|
|
551
|
+
8. **Test with different update types** and edge cases
|
|
552
|
+
|
|
553
|
+
The Context object is your primary interface to Telegram's API. Understanding its properties and methods is essential for building robust bots.</content>
|
|
554
|
+
<parameter name="filePath">/home/slick/telegem/docs/context.md
|