telegem 3.2.2 → 3.2.4
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 +1 -1
- data/docs/.gitkeep +0 -0
- data/docs/ctx.md +437 -0
- data/docs/file_extract.md +378 -0
- data/lib/core/bot.rb +17 -1
- data/lib/markup/keyboard.rb +26 -16
- data/lib/telegem.rb +8 -13
- metadata +15 -27
- data/lib/telegem.code-workspace +0 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 64cc027dc87d2e24f181a24705a85243f95c7f498a44f70fcb175d8a9b2dd597
|
|
4
|
+
data.tar.gz: 37310a9a9c973f31f3dfd5b93e8347843a50c8218475b90a7f055df116c8353e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9cd4708d09629445cf1398677521270c22991eee81b45204d2384570ba14876c937888cf86fc9ba8bfdad78138a48ea9bee6fed6e019a12ae89b29433f91e881
|
|
7
|
+
data.tar.gz: ee85cb0026f096291a1700d825e68cf21f08385e1c7eaf5396763e00fbe4edecfb574b4ed1017293e4e41a06d60b98be5963893b6b0ba2e53f8816396ae33dfc
|
data/Readme.md
CHANGED
data/docs/.gitkeep
ADDED
|
File without changes
|
data/docs/ctx.md
ADDED
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
The Context object (ctx) is the heart of every Telegem bot handler. It provides access to everything about the current update and methods to interact with Telegram.
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
📦 Basic Properties
|
|
6
|
+
|
|
7
|
+
Property Type Description
|
|
8
|
+
ctx.update Update Raw Telegram update object
|
|
9
|
+
ctx.bot Bot The bot instance
|
|
10
|
+
ctx.state Hash Temporary state for current request (cleared after)
|
|
11
|
+
ctx.session Hash Persistent user session (stored between requests)
|
|
12
|
+
ctx.match MatchData Regex match data from hears or pattern matching
|
|
13
|
+
ctx.scene String Current scene name (if in a scene)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
👤 User & Chat Information
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
ctx.from # User who sent the message
|
|
21
|
+
ctx.from.id # User ID
|
|
22
|
+
ctx.from.username # Username (may be nil)
|
|
23
|
+
ctx.from.first_name
|
|
24
|
+
ctx.from.last_name
|
|
25
|
+
|
|
26
|
+
ctx.chat # Current chat
|
|
27
|
+
ctx.chat.id # Chat ID
|
|
28
|
+
ctx.chat.type # "private", "group", "supergroup", "channel"
|
|
29
|
+
|
|
30
|
+
ctx.user_id # Shortcut for ctx.from&.id
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
📨 Message Access
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
ctx.message # Current message
|
|
39
|
+
ctx.message.text # Message text
|
|
40
|
+
ctx.message.caption # Caption for media
|
|
41
|
+
ctx.message.message_id
|
|
42
|
+
ctx.message.date
|
|
43
|
+
|
|
44
|
+
ctx.message.reply_to_message # Message being replied to
|
|
45
|
+
ctx.reply_to_message # Same as above
|
|
46
|
+
|
|
47
|
+
ctx.message.photo # Array of photo sizes
|
|
48
|
+
ctx.message.document # Document object
|
|
49
|
+
ctx.message.audio
|
|
50
|
+
ctx.message.video
|
|
51
|
+
ctx.message.voice
|
|
52
|
+
ctx.message.sticker
|
|
53
|
+
ctx.message.location
|
|
54
|
+
|
|
55
|
+
ctx.message.entities # Formatting entities
|
|
56
|
+
ctx.message.caption_entities # Caption entities
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
🔍 Update Type Detection
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
ctx.callback_query # Callback query object (if present)
|
|
65
|
+
ctx.inline_query # Inline query object
|
|
66
|
+
ctx.chosen_inline_result
|
|
67
|
+
ctx.poll # Poll object
|
|
68
|
+
ctx.poll_answer
|
|
69
|
+
ctx.chat_member
|
|
70
|
+
ctx.my_chat_member
|
|
71
|
+
ctx.chat_join_request
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
📝 Message Content Shortcuts
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
ctx.text # ctx.message&.text
|
|
80
|
+
ctx.data # ctx.callback_query&.data
|
|
81
|
+
ctx.query # ctx.inline_query&.query
|
|
82
|
+
ctx.command? # True if message is a command
|
|
83
|
+
ctx.command # Command name (e.g., "start")
|
|
84
|
+
ctx.command_args # Arguments after command
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
💬 Sending Messages
|
|
90
|
+
|
|
91
|
+
Basic Text
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
ctx.reply("Hello world") # Simple reply
|
|
95
|
+
ctx.reply("Hello", parse_mode: "Markdown") # With formatting
|
|
96
|
+
ctx.reply("Hello", disable_web_page_preview: true)
|
|
97
|
+
ctx.reply("Hello", reply_to_message_id: 123)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
With Keyboard
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
# Reply keyboard
|
|
104
|
+
keyboard = Telegem::Markup.keyboard do
|
|
105
|
+
button "Option 1"
|
|
106
|
+
button "Option 2"
|
|
107
|
+
request_location "Share Location"
|
|
108
|
+
end
|
|
109
|
+
ctx.reply("Choose:", reply_markup: keyboard)
|
|
110
|
+
|
|
111
|
+
# Inline keyboard
|
|
112
|
+
inline = Telegem::Markup.inline do
|
|
113
|
+
callback "Yes", "confirm"
|
|
114
|
+
callback "No", "cancel"
|
|
115
|
+
url "Visit", "https://example.com"
|
|
116
|
+
end
|
|
117
|
+
ctx.reply("Confirm?", reply_markup: inline)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Remove Keyboard
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
ctx.remove_keyboard # Just removes keyboard
|
|
124
|
+
ctx.remove_keyboard("Done!") # Sends message + removes keyboard
|
|
125
|
+
ctx.remove_keyboard(selective: true) # Only for specific users
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Force Reply
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
ctx.reply("What's your name?", reply_markup: Telegem::Markup.force_reply)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
🖼️ Sending Media
|
|
137
|
+
|
|
138
|
+
Photos
|
|
139
|
+
|
|
140
|
+
```ruby
|
|
141
|
+
ctx.photo("https://example.com/image.jpg")
|
|
142
|
+
ctx.photo("https://example.com/image.jpg", caption: "Beautiful gem")
|
|
143
|
+
ctx.photo(File.open("local.jpg"))
|
|
144
|
+
ctx.photo(File.open("local.jpg"), caption: "Local file")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Documents
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
ctx.document("https://example.com/file.pdf")
|
|
151
|
+
ctx.document(File.open("report.pdf"), caption: "Monthly report")
|
|
152
|
+
ctx.document(file_id) # Using Telegram file_id
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Audio
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
ctx.audio("song.mp3", title: "My Song", performer: "Artist", duration: 180)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Video
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
ctx.video("video.mp4", caption: "Check this out", width: 1920, height: 1080)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Voice
|
|
168
|
+
|
|
169
|
+
```ruby
|
|
170
|
+
ctx.voice("voice.ogg") # Must be OGG format
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Sticker
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
ctx.sticker("CAACAgIAAxkBAAIBZmd...") # Sticker file_id
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Location
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
ctx.location(6.454, 3.394) # Latitude, Longitude
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Contact
|
|
186
|
+
|
|
187
|
+
```ruby
|
|
188
|
+
ctx.contact("+1234567890", "John", last_name: "Doe")
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
✏️ Editing Messages
|
|
194
|
+
|
|
195
|
+
```ruby
|
|
196
|
+
ctx.edit_message_text("Updated text")
|
|
197
|
+
ctx.edit_message_text("New text", message_id: 123) # Specific message
|
|
198
|
+
|
|
199
|
+
ctx.edit_message_caption("New caption")
|
|
200
|
+
ctx.edit_message_media(new_photo)
|
|
201
|
+
ctx.edit_message_reply_markup(new_keyboard)
|
|
202
|
+
|
|
203
|
+
ctx.edit_message_live_location(6.455, 3.395) # For live locations
|
|
204
|
+
ctx.stop_message_live_location
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
❌ Deleting Messages
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
ctx.delete_message # Deletes current message
|
|
213
|
+
ctx.delete_message(123) # Deletes specific message ID
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
🔄 Replying to Callbacks
|
|
219
|
+
|
|
220
|
+
```ruby
|
|
221
|
+
ctx.answer_callback_query("Done!") # Simple toast
|
|
222
|
+
ctx.answer_callback_query("Error!", show_alert: true) # Alert popup
|
|
223
|
+
ctx.answer_callback_query(url: "https://example.com") # Open URL
|
|
224
|
+
ctx.answer_callback_query(text: "Loading...", cache_time: 5)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
🔎 Inline Queries
|
|
230
|
+
|
|
231
|
+
```ruby
|
|
232
|
+
ctx.answer_inline_query(results, cache_time: 300)
|
|
233
|
+
ctx.answer_inline_query(results, next_offset: "20") # Pagination
|
|
234
|
+
|
|
235
|
+
# Results array of InlineQueryResult objects
|
|
236
|
+
results = [
|
|
237
|
+
Telegem::Types::InlineQueryResultArticle.new(
|
|
238
|
+
id: "1",
|
|
239
|
+
title: "Result",
|
|
240
|
+
input_message_content: { message_text: "Text" }
|
|
241
|
+
)
|
|
242
|
+
]
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
📥 File Operations
|
|
248
|
+
|
|
249
|
+
```ruby
|
|
250
|
+
ctx.download_file(file_id, "local/path") # Download file
|
|
251
|
+
ctx.file(file_id) # Get file info
|
|
252
|
+
ctx.file_path(file_id) # Get path on Telegram servers
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
🎬 Chat Actions (Typing Indicators)
|
|
258
|
+
|
|
259
|
+
```ruby
|
|
260
|
+
ctx.typing # "typing..."
|
|
261
|
+
ctx.uploading_photo # "sending photo..."
|
|
262
|
+
ctx.uploading_video # "sending video..."
|
|
263
|
+
ctx.uploading_audio # "sending audio..."
|
|
264
|
+
ctx.uploading_document # "sending document..."
|
|
265
|
+
ctx.find_location # "finding location..."
|
|
266
|
+
ctx.record_video # "recording video..."
|
|
267
|
+
ctx.record_audio # "recording audio..."
|
|
268
|
+
ctx.choose_sticker # "choosing sticker..."
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
All accept optional **options:
|
|
272
|
+
|
|
273
|
+
```ruby
|
|
274
|
+
ctx.typing(business_connection_id: "123") # For business connections
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
👥 Chat Management
|
|
280
|
+
|
|
281
|
+
```ruby
|
|
282
|
+
ctx.kick_chat_member(user_id) # Kick user
|
|
283
|
+
ctx.ban_chat_member(user_id, until_date: future_time)
|
|
284
|
+
ctx.unban_chat_member(user_id, only_if_banned: true)
|
|
285
|
+
|
|
286
|
+
ctx.restrict_chat_member(user_id, permissions: { can_send_messages: false })
|
|
287
|
+
ctx.promote_chat_member(user_id, can_invite_users: true)
|
|
288
|
+
|
|
289
|
+
ctx.get_chat_administrators
|
|
290
|
+
ctx.get_chat_member(user_id)
|
|
291
|
+
ctx.get_chat_members_count
|
|
292
|
+
|
|
293
|
+
ctx.leave_chat
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
📌 Pinning Messages
|
|
299
|
+
|
|
300
|
+
```ruby
|
|
301
|
+
ctx.pin_message(123) # Pin message
|
|
302
|
+
ctx.pin_message(123, disable_notification: true)
|
|
303
|
+
ctx.unpin_message # Unpin current
|
|
304
|
+
ctx.unpin_message(123) # Unpin specific
|
|
305
|
+
ctx.unpin_all_messages
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
🔁 Forwarding & Copying
|
|
311
|
+
|
|
312
|
+
```ruby
|
|
313
|
+
ctx.forward_message(from_chat_id, message_id)
|
|
314
|
+
ctx.copy_message(from_chat_id, message_id, caption: "New caption")
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
🌐 Web App & Polls
|
|
320
|
+
|
|
321
|
+
```ruby
|
|
322
|
+
ctx.web_app_data # Data from Web App
|
|
323
|
+
|
|
324
|
+
ctx.send_poll("Question?", ["Option1", "Option2"], is_anonymous: true)
|
|
325
|
+
ctx.stop_poll(message_id)
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
🎭 Scenes (Multi-step Conversations)
|
|
331
|
+
|
|
332
|
+
```ruby
|
|
333
|
+
ctx.enter_scene(:survey) # Enter scene
|
|
334
|
+
ctx.leave_scene # Leave current scene
|
|
335
|
+
ctx.leave_scene(reason: :cancel) # Leave with reason
|
|
336
|
+
ctx.in_scene? # Check if in a scene
|
|
337
|
+
ctx.current_scene # Get current scene name
|
|
338
|
+
|
|
339
|
+
ctx.ask("What's your name?") # Prompt for response (scene helper)
|
|
340
|
+
ctx.next_step # Move to next scene step
|
|
341
|
+
ctx.next_step(:payment) # Move to specific step
|
|
342
|
+
ctx.scene_data # Get scene data hash
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
🎨 Keyboard Building Helpers
|
|
348
|
+
|
|
349
|
+
```ruby
|
|
350
|
+
# Create and use in one line
|
|
351
|
+
ctx.reply_with_keyboard("Choose:", Telegem::Markup.keyboard { button "Yes" })
|
|
352
|
+
|
|
353
|
+
ctx.reply_with_inline_keyboard("Options:", Telegem::Markup.inline { callback "Yes", "data" })
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
📊 Poll Helpers
|
|
359
|
+
|
|
360
|
+
```ruby
|
|
361
|
+
ctx.poll? # True if update is a poll
|
|
362
|
+
ctx.poll_answer? # True if poll answer
|
|
363
|
+
ctx.poll_answer # Get poll answer object
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
⏱️ Timing & Metadata
|
|
369
|
+
|
|
370
|
+
```ruby
|
|
371
|
+
ctx.logger # Bot's logger
|
|
372
|
+
ctx.api # Direct API client
|
|
373
|
+
ctx.raw_update # Original update hash
|
|
374
|
+
ctx.update_id # Update ID
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
🔄 Session Management
|
|
380
|
+
|
|
381
|
+
```ruby
|
|
382
|
+
ctx.session[:user_preference] = "dark" # Store in session
|
|
383
|
+
pref = ctx.session[:user_preference] # Retrieve
|
|
384
|
+
ctx.session.delete(:temp_data) # Delete
|
|
385
|
+
ctx.session.clear # Clear all
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
🎯 Complete Usage Example
|
|
391
|
+
|
|
392
|
+
```ruby
|
|
393
|
+
bot.command('start') do |ctx|
|
|
394
|
+
ctx.session[:visits] ||= 0
|
|
395
|
+
ctx.session[:visits] += 1
|
|
396
|
+
|
|
397
|
+
keyboard = Telegem::Markup.keyboard do
|
|
398
|
+
button "💎 Random Gem"
|
|
399
|
+
request_location "📍 Share Location"
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
ctx.reply(
|
|
403
|
+
"Welcome! You've visited #{ctx.session[:visits]} times",
|
|
404
|
+
reply_markup: keyboard
|
|
405
|
+
)
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
bot.hears(/^gem$/i) do |ctx|
|
|
409
|
+
ctx.typing
|
|
410
|
+
sleep 1 # Simulate processing
|
|
411
|
+
|
|
412
|
+
gem = GemDatabase.random
|
|
413
|
+
ctx.photo(gem.image_url, caption: "#{gem.name}\n#{gem.fact}")
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
bot.callback_query('favorite') do |ctx|
|
|
417
|
+
gem_id = ctx.data.split('_').last
|
|
418
|
+
ctx.session[:favorites] ||= []
|
|
419
|
+
ctx.session[:favorites] << gem_id
|
|
420
|
+
|
|
421
|
+
ctx.answer_callback_query("Added to favorites! ❤️")
|
|
422
|
+
ctx.edit_message_text("⭐ Favorited!")
|
|
423
|
+
end
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
⚠️ Error Handling
|
|
429
|
+
|
|
430
|
+
```ruby
|
|
431
|
+
bot.error do |error, ctx|
|
|
432
|
+
ctx.logger.error("Error for user #{ctx.user_id}: #{error.message}")
|
|
433
|
+
ctx.reply("Something went wrong. Please try again.") if ctx.chat
|
|
434
|
+
end
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
---
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
# Telegem::Plugins::FileExtract
|
|
4
|
+
|
|
5
|
+
**Version:** 3.2.2
|
|
6
|
+
**Status:** Production Ready
|
|
7
|
+
**Dependencies:** `pdf-reader`
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
FileExtractor provides seamless document processing for Telegram bots. It automatically detects file types, extracts structured content, and handles all edge cases—eliminating boilerplate code for developers.
|
|
14
|
+
|
|
15
|
+
Unlike traditional approaches that require manual MIME type checking and format-specific handlers, FileExtractor implements a unified interface that works across all supported formats.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
Add to your Gemfile:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
gem 'telegem'
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
Quick Start
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
# Initialize with any Telegram file_id
|
|
33
|
+
extractor = Telegem::Plugins::FileExtract.new(
|
|
34
|
+
bot,
|
|
35
|
+
ctx.message.document.file_id
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Extract content - auto-detects file type
|
|
39
|
+
result = extractor.extract
|
|
40
|
+
|
|
41
|
+
if result[:success]
|
|
42
|
+
# Unified response structure across all file types
|
|
43
|
+
puts "Type: #{result[:type]}"
|
|
44
|
+
puts "Content: #{result[:content]}"
|
|
45
|
+
puts "Metadata: #{result[:metadata]}"
|
|
46
|
+
else
|
|
47
|
+
# Graceful error handling
|
|
48
|
+
puts "Error: #{result[:error]}"
|
|
49
|
+
end
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
Supported Formats
|
|
55
|
+
|
|
56
|
+
| format | Mime type | extension | features |
|
|
57
|
+
|:--------:| :--------:| :--------: |:--------:|
|
|
58
|
+
| pdf | application/pdf | .pdf | text extraction and page count |
|
|
59
|
+
| json| application/json | .json | ful parsibg nested structure |
|
|
60
|
+
| html | text/html | .html | tag stripping |
|
|
61
|
+
| plain text| text/plain | .txt | full content |
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
Note: All formats except PDF work without additional dependencies.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
API Reference
|
|
69
|
+
|
|
70
|
+
Constructor
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
def initialize(bot, file_id, **options)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
| Parameter | Type Required | Default Description |
|
|
77
|
+
|:--------: | :--------:| :--------:| :--------:|
|
|
78
|
+
| bot | Telegem::Bot ✓ | – Active bot instance
|
|
79
|
+
| file_id | String ✓| – Telegram file identifier
|
|
80
|
+
| auto_delete | Boolean |true Remove temp files after processing|
|
|
81
|
+
| max_size | Integer | 50_000_000 Maximum file size in bytes (50MB) |
|
|
82
|
+
| timeout | Integer | 30 Processing timeout in seconds |
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
Instance Methods
|
|
86
|
+
|
|
87
|
+
extract → Hash
|
|
88
|
+
|
|
89
|
+
Processes the file and returns a standardized response hash.
|
|
90
|
+
|
|
91
|
+
Success Response:
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
{
|
|
95
|
+
success: true,
|
|
96
|
+
type: :pdf, # Symbol identifying file format
|
|
97
|
+
content: "extracted text", # Parsed content (String, Hash, or Array)
|
|
98
|
+
metadata: {
|
|
99
|
+
size: 45210, # File size in bytes
|
|
100
|
+
# Format-specific fields (see below)
|
|
101
|
+
},
|
|
102
|
+
error: nil
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Error Response:
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
{
|
|
110
|
+
success: false,
|
|
111
|
+
type: :unknown,
|
|
112
|
+
content: nil,
|
|
113
|
+
metadata: {},
|
|
114
|
+
error: "Descriptive error message"
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
Format-Specific Behavior
|
|
121
|
+
|
|
122
|
+
PDF Documents
|
|
123
|
+
|
|
124
|
+
Requirements: gem 'pdf-reader'
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
result = extractor.extract
|
|
128
|
+
|
|
129
|
+
if result[:success]
|
|
130
|
+
puts "Pages: #{result[:metadata][:pages]}"
|
|
131
|
+
puts "Content: #{result[:content][0..500]}..."
|
|
132
|
+
end
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Metadata:
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
metadata: {
|
|
139
|
+
pages: 12, # Total page count
|
|
140
|
+
size: 1048576 # File size in bytes
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Error Cases:
|
|
145
|
+
|
|
146
|
+
- Malformed PDF format – Corrupted or invalid PDF structure
|
|
147
|
+
- Encrypted PDF (password protected) – Document requires password
|
|
148
|
+
- PDF contains no extractable text – Scanned/image-only document
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
JSON Documents
|
|
153
|
+
|
|
154
|
+
```ruby
|
|
155
|
+
result = extractor.extract
|
|
156
|
+
|
|
157
|
+
if result[:success]
|
|
158
|
+
# Content is already parsed Ruby objects
|
|
159
|
+
data = result[:content]
|
|
160
|
+
puts data['user']['name'] # Direct hash access
|
|
161
|
+
end
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Metadata:
|
|
165
|
+
|
|
166
|
+
```ruby
|
|
167
|
+
metadata: {
|
|
168
|
+
size: 1250,
|
|
169
|
+
structure: "hash", # "hash", "array", or "scalar"
|
|
170
|
+
keys: 5 # Top-level keys (hash only)
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Error Cases:
|
|
175
|
+
|
|
176
|
+
- Invalid JSON: unexpected token – Syntax error with position
|
|
177
|
+
- JSON file is empty – Zero-byte file
|
|
178
|
+
- File encoding not supported – Non-UTF8 encoding
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
HTML Documents
|
|
183
|
+
|
|
184
|
+
```ruby
|
|
185
|
+
result = extractor.extract
|
|
186
|
+
|
|
187
|
+
if result[:success]
|
|
188
|
+
# HTML tags stripped, whitespace normalized
|
|
189
|
+
text = result[:content]
|
|
190
|
+
|
|
191
|
+
# Original HTML available when keep_original: true
|
|
192
|
+
original = result[:metadata][:original] if @keep_original
|
|
193
|
+
end
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Metadata:
|
|
197
|
+
|
|
198
|
+
```ruby
|
|
199
|
+
metadata: {
|
|
200
|
+
size: 3072,
|
|
201
|
+
original: "<html>...</html>" # Only if keep_original: true
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Error Cases:
|
|
206
|
+
|
|
207
|
+
- HTML file is empty – Zero-byte file
|
|
208
|
+
- HTML contains no extractable text – Only tags, comments, or scripts
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
Text-Based Formats (TXT, CSV, Markdown)
|
|
213
|
+
|
|
214
|
+
```ruby
|
|
215
|
+
result = extractor.extract
|
|
216
|
+
|
|
217
|
+
if result[:success]
|
|
218
|
+
lines = result[:metadata][:lines]
|
|
219
|
+
encoding = result[:metadata][:encoding]
|
|
220
|
+
end
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Metadata:
|
|
224
|
+
|
|
225
|
+
```ruby
|
|
226
|
+
metadata: {
|
|
227
|
+
size: 2048,
|
|
228
|
+
lines: 42, # Line count
|
|
229
|
+
encoding: "UTF-8" # Detected encoding
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Error Cases:
|
|
234
|
+
- Text file is empty – Zero-byte file
|
|
235
|
+
- File encoding not supported – Binary or invalid UTF-8
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
Error Handling
|
|
240
|
+
|
|
241
|
+
FileExtractor never raises exceptions during normal operation. All errors are captured and returned in the standardized hash format.
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
Client-Side Error Handling
|
|
246
|
+
|
|
247
|
+
```ruby
|
|
248
|
+
result = extractor.extract
|
|
249
|
+
|
|
250
|
+
unless result[:success]
|
|
251
|
+
case result[:error]
|
|
252
|
+
when /encrypted/i
|
|
253
|
+
ctx.reply("🔒 Please send an unencrypted PDF")
|
|
254
|
+
when /malformed/i
|
|
255
|
+
ctx.reply("📄 File appears corrupted, please resend")
|
|
256
|
+
when /size exceeds/i
|
|
257
|
+
ctx.reply("📦 File too large (max 50MB)")
|
|
258
|
+
else
|
|
259
|
+
ctx.reply("❌ #{result[:error]}")
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
Benchmarks: (50KB file, Ruby 3.2)
|
|
268
|
+
|
|
269
|
+
- PDF: ~150ms
|
|
270
|
+
- JSON: ~20ms
|
|
271
|
+
- HTML: ~15ms
|
|
272
|
+
- Text: ~5ms
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
Security Considerations
|
|
277
|
+
|
|
278
|
+
File Size Limits
|
|
279
|
+
|
|
280
|
+
Default max_size: 50_000_000 prevents denial-of-service attacks via large file uploads.
|
|
281
|
+
|
|
282
|
+
Temporary File Management
|
|
283
|
+
|
|
284
|
+
All files are written to Dir.tmpdir with:
|
|
285
|
+
|
|
286
|
+
- Random filename generation (SecureRandom.hex)
|
|
287
|
+
- Automatic deletion (auto_delete: true)
|
|
288
|
+
- Explicit cleanup in ensure blocks
|
|
289
|
+
|
|
290
|
+
Memory Protection
|
|
291
|
+
|
|
292
|
+
- Large files are never fully read into memory unless required
|
|
293
|
+
- Streaming parsers used where available
|
|
294
|
+
- Configurable timeouts prevent hanging operations
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
Common Patterns
|
|
299
|
+
|
|
300
|
+
Reply-to-Message Processing
|
|
301
|
+
|
|
302
|
+
```ruby
|
|
303
|
+
bot.command('extract') do |ctx|
|
|
304
|
+
replied = ctx.message.reply_to_message
|
|
305
|
+
|
|
306
|
+
if replied&.document
|
|
307
|
+
result = FileExtractor.new(bot, replied.document.file_id).extract
|
|
308
|
+
|
|
309
|
+
if result[:success]
|
|
310
|
+
ctx.reply("📄 Extracted from #{result[:type]}:\n\n#{result[:content][0..300]}")
|
|
311
|
+
else
|
|
312
|
+
ctx.reply("❌ #{result[:error]}")
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Batch Processing
|
|
319
|
+
|
|
320
|
+
```ruby
|
|
321
|
+
files = updates.map do |update|
|
|
322
|
+
next unless update.message&.document
|
|
323
|
+
|
|
324
|
+
Thread.new do
|
|
325
|
+
FileExtractor.new(bot, update.message.document.file_id).extract
|
|
326
|
+
end
|
|
327
|
+
end.map(&:value)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Caching Extracted Content
|
|
331
|
+
|
|
332
|
+
```ruby
|
|
333
|
+
class CachedExtractor < Telegem::Plugins::FileExtractor
|
|
334
|
+
def extract
|
|
335
|
+
cache_key = "extract:#{@file_id}"
|
|
336
|
+
|
|
337
|
+
Rails.cache.fetch(cache_key, expires_in: 1.hour) do
|
|
338
|
+
super
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
Troubleshooting
|
|
347
|
+
|
|
348
|
+
PDF::Reader Not Found
|
|
349
|
+
|
|
350
|
+
```
|
|
351
|
+
LoadError: cannot load such file -- pdf/reader
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
Solution: gem install pdf-reader or add to Gemfile
|
|
355
|
+
|
|
356
|
+
File ID Expired
|
|
357
|
+
|
|
358
|
+
```
|
|
359
|
+
Error: File not found
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Solution: File IDs expire after 24 hours. Request fresh file from user.
|
|
363
|
+
|
|
364
|
+
Memory Usage Spikes
|
|
365
|
+
|
|
366
|
+
Solution: Reduce max_size or process files sequentially rather than in parallel.
|
|
367
|
+
|
|
368
|
+
Unsupported File Types
|
|
369
|
+
|
|
370
|
+
Solution: Check file extension or MIME type before extraction:
|
|
371
|
+
|
|
372
|
+
```ruby
|
|
373
|
+
if ctx.message.document.mime_type.start_with?('image/')
|
|
374
|
+
# Handle images separately
|
|
375
|
+
else
|
|
376
|
+
result = FileExtractor.new(bot, file_id).extract
|
|
377
|
+
end
|
|
378
|
+
```
|
data/lib/core/bot.rb
CHANGED
|
@@ -134,6 +134,22 @@ module Telegem
|
|
|
134
134
|
end
|
|
135
135
|
end
|
|
136
136
|
|
|
137
|
+
def set_my_profile_photo(photo, **options)
|
|
138
|
+
@api.call('setMyProfilePhoto', { photo: photo }.merge(options))
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def remove_my_profile_photo
|
|
142
|
+
@api.call('removeMyProfilePhoto', {})
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def get_user_profile_audios(user_id, **options)
|
|
146
|
+
result = @api.call('getUserProfileAudios', { user_id: user_id }.merge(options))
|
|
147
|
+
result ? Types::UserProfileAudios.new(result) : nil
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def create_forum_topic(chat_id, name, **options)
|
|
151
|
+
@api.call('createForumTopic', { chat_id: chat_id, name: name }.merge(options))
|
|
152
|
+
end
|
|
137
153
|
def location(&block)
|
|
138
154
|
on(:message, location: true) do |ctx|
|
|
139
155
|
block.call(ctx)
|
|
@@ -369,4 +385,4 @@ module Telegem
|
|
|
369
385
|
end
|
|
370
386
|
end
|
|
371
387
|
end
|
|
372
|
-
end
|
|
388
|
+
end
|
data/lib/markup/keyboard.rb
CHANGED
|
@@ -27,14 +27,19 @@ module Telegem
|
|
|
27
27
|
self
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
def button(text, **options)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
def button(text, style: nil, icon_custom_emoji_id: nil, **options)
|
|
31
|
+
btn = {
|
|
32
|
+
text: text
|
|
33
|
+
}.merge(options)
|
|
34
|
+
btn[:style] = style if style
|
|
35
|
+
btn[:icon_custom_emoji_id] = icon_custom_emoji_id if icon_custom_emoji_id
|
|
36
|
+
if @buttons.empty? || @buttons.last.is_a?(Array)
|
|
37
|
+
@buttons << [btn]
|
|
38
|
+
else
|
|
39
|
+
@buttons.last << btn
|
|
40
|
+
end
|
|
41
|
+
self
|
|
42
|
+
end
|
|
38
43
|
|
|
39
44
|
def request_contact(text)
|
|
40
45
|
button(text, request_contact: true)
|
|
@@ -114,14 +119,19 @@ module Telegem
|
|
|
114
119
|
self
|
|
115
120
|
end
|
|
116
121
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
def button(text, style: nil, icon_custom_emoji_id: nil, **options)
|
|
123
|
+
btn = {
|
|
124
|
+
text: text
|
|
125
|
+
}.merge(options)
|
|
126
|
+
btn[:style] = style if style
|
|
127
|
+
btn[:icon_custom_emoji_id] = icon_custom_emoji_id if icon_custom_emoji_id
|
|
128
|
+
if @buttons.empty? || @buttons.last.is_a?(Array)
|
|
129
|
+
@buttons << [btn]
|
|
130
|
+
else
|
|
131
|
+
@buttons.last << btn
|
|
132
|
+
end
|
|
133
|
+
self
|
|
134
|
+
end
|
|
125
135
|
|
|
126
136
|
def url(text, url)
|
|
127
137
|
button(text, url: url)
|
data/lib/telegem.rb
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
2
|
require 'logger'
|
|
3
3
|
require 'json'
|
|
4
4
|
|
|
5
5
|
module Telegem
|
|
6
|
-
VERSION = "3.2.
|
|
6
|
+
VERSION = "3.2.4".freeze
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
-
#
|
|
9
|
+
#
|
|
10
10
|
require_relative 'api/client'
|
|
11
11
|
require_relative 'api/types'
|
|
12
12
|
require_relative 'core/bot'
|
|
@@ -16,17 +16,17 @@ require_relative 'core/scene'
|
|
|
16
16
|
require_relative 'session/middleware'
|
|
17
17
|
require_relative 'session/memory_store'
|
|
18
18
|
require_relative 'markup/keyboard'
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
require_relative 'plugins/file_extract'
|
|
21
21
|
require_relative 'session/scene_middleware'
|
|
22
22
|
|
|
23
23
|
module Telegem
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
def self.new(token, **options)
|
|
26
26
|
Core::Bot.new(token, **options)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
def self.keyboard(&block)
|
|
31
31
|
Markup.keyboard(&block)
|
|
32
32
|
end
|
|
@@ -35,28 +35,23 @@ module Telegem
|
|
|
35
35
|
Markup.inline(&block)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
# Remove keyboard markup
|
|
39
38
|
def self.remove_keyboard(**options)
|
|
40
39
|
Markup.remove(**options)
|
|
41
40
|
end
|
|
42
41
|
|
|
43
|
-
# Force reply markup
|
|
44
42
|
def self.force_reply(**options)
|
|
45
43
|
Markup.force_reply(**options)
|
|
46
44
|
end
|
|
47
45
|
|
|
48
|
-
# Current version
|
|
49
46
|
def self.version
|
|
50
47
|
VERSION
|
|
51
48
|
end
|
|
52
|
-
|
|
53
|
-
# Quick webhook setup
|
|
49
|
+
|
|
54
50
|
def self.webhook(bot, **options)
|
|
55
51
|
require_relative 'webhook/server'
|
|
56
52
|
Webhook::Server.setup(bot, **options)
|
|
57
53
|
end
|
|
58
54
|
|
|
59
|
-
# Framework information
|
|
60
55
|
def self.info
|
|
61
56
|
<<~INFO
|
|
62
57
|
🤖 Telegem #{VERSION}
|
|
@@ -77,7 +72,7 @@ module Telegem
|
|
|
77
72
|
end
|
|
78
73
|
end
|
|
79
74
|
|
|
80
|
-
|
|
75
|
+
|
|
81
76
|
if ENV['TELEGEM_GLOBAL'] == 'true'
|
|
82
77
|
def Telegem(token, **options)
|
|
83
78
|
::Telegem.new(token, **options)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: telegem
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.2.
|
|
4
|
+
version: 3.2.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- sick_phantom
|
|
@@ -9,34 +9,20 @@ bindir: bin
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
-
- !ruby/object:Gem::Dependency
|
|
13
|
-
name: httpx
|
|
14
|
-
requirement: !ruby/object:Gem::Requirement
|
|
15
|
-
requirements:
|
|
16
|
-
- - "~>"
|
|
17
|
-
- !ruby/object:Gem::Version
|
|
18
|
-
version: '1.0'
|
|
19
|
-
type: :runtime
|
|
20
|
-
prerelease: false
|
|
21
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
-
requirements:
|
|
23
|
-
- - "~>"
|
|
24
|
-
- !ruby/object:Gem::Version
|
|
25
|
-
version: '1.0'
|
|
26
12
|
- !ruby/object:Gem::Dependency
|
|
27
13
|
name: concurrent-ruby
|
|
28
14
|
requirement: !ruby/object:Gem::Requirement
|
|
29
15
|
requirements:
|
|
30
16
|
- - "~>"
|
|
31
17
|
- !ruby/object:Gem::Version
|
|
32
|
-
version:
|
|
18
|
+
version: 1.3.6
|
|
33
19
|
type: :runtime
|
|
34
20
|
prerelease: false
|
|
35
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
22
|
requirements:
|
|
37
23
|
- - "~>"
|
|
38
24
|
- !ruby/object:Gem::Version
|
|
39
|
-
version:
|
|
25
|
+
version: 1.3.6
|
|
40
26
|
- !ruby/object:Gem::Dependency
|
|
41
27
|
name: securerandom
|
|
42
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -57,56 +43,56 @@ dependencies:
|
|
|
57
43
|
requirements:
|
|
58
44
|
- - "~>"
|
|
59
45
|
- !ruby/object:Gem::Version
|
|
60
|
-
version:
|
|
46
|
+
version: 2.35.2
|
|
61
47
|
type: :runtime
|
|
62
48
|
prerelease: false
|
|
63
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
50
|
requirements:
|
|
65
51
|
- - "~>"
|
|
66
52
|
- !ruby/object:Gem::Version
|
|
67
|
-
version:
|
|
53
|
+
version: 2.35.2
|
|
68
54
|
- !ruby/object:Gem::Dependency
|
|
69
55
|
name: async-http
|
|
70
56
|
requirement: !ruby/object:Gem::Requirement
|
|
71
57
|
requirements:
|
|
72
58
|
- - "~>"
|
|
73
59
|
- !ruby/object:Gem::Version
|
|
74
|
-
version:
|
|
60
|
+
version: 0.92.1
|
|
75
61
|
type: :runtime
|
|
76
62
|
prerelease: false
|
|
77
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
64
|
requirements:
|
|
79
65
|
- - "~>"
|
|
80
66
|
- !ruby/object:Gem::Version
|
|
81
|
-
version:
|
|
67
|
+
version: 0.92.1
|
|
82
68
|
- !ruby/object:Gem::Dependency
|
|
83
69
|
name: pdf-reader
|
|
84
70
|
requirement: !ruby/object:Gem::Requirement
|
|
85
71
|
requirements:
|
|
86
72
|
- - "~>"
|
|
87
73
|
- !ruby/object:Gem::Version
|
|
88
|
-
version:
|
|
74
|
+
version: 2.14.1
|
|
89
75
|
type: :runtime
|
|
90
76
|
prerelease: false
|
|
91
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
92
78
|
requirements:
|
|
93
79
|
- - "~>"
|
|
94
80
|
- !ruby/object:Gem::Version
|
|
95
|
-
version:
|
|
81
|
+
version: 2.14.1
|
|
96
82
|
- !ruby/object:Gem::Dependency
|
|
97
83
|
name: docx
|
|
98
84
|
requirement: !ruby/object:Gem::Requirement
|
|
99
85
|
requirements:
|
|
100
86
|
- - "~>"
|
|
101
87
|
- !ruby/object:Gem::Version
|
|
102
|
-
version:
|
|
88
|
+
version: 0.10.0
|
|
103
89
|
type: :runtime
|
|
104
90
|
prerelease: false
|
|
105
91
|
version_requirements: !ruby/object:Gem::Requirement
|
|
106
92
|
requirements:
|
|
107
93
|
- - "~>"
|
|
108
94
|
- !ruby/object:Gem::Version
|
|
109
|
-
version:
|
|
95
|
+
version: 0.10.0
|
|
110
96
|
- !ruby/object:Gem::Dependency
|
|
111
97
|
name: rspec
|
|
112
98
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -158,6 +144,9 @@ files:
|
|
|
158
144
|
- assets/logo.png
|
|
159
145
|
- bin/.gitkeep
|
|
160
146
|
- bin/telegem-ssl
|
|
147
|
+
- docs/.gitkeep
|
|
148
|
+
- docs/ctx.md
|
|
149
|
+
- docs/file_extract.md
|
|
161
150
|
- examples/.gitkeep
|
|
162
151
|
- lib/api/client.rb
|
|
163
152
|
- lib/api/types.rb
|
|
@@ -173,7 +162,6 @@ files:
|
|
|
173
162
|
- lib/session/memory_store.rb
|
|
174
163
|
- lib/session/middleware.rb
|
|
175
164
|
- lib/session/scene_middleware.rb
|
|
176
|
-
- lib/telegem.code-workspace
|
|
177
165
|
- lib/telegem.rb
|
|
178
166
|
- lib/webhook/.gitkeep
|
|
179
167
|
- lib/webhook/server.rb
|
|
@@ -188,7 +176,7 @@ metadata:
|
|
|
188
176
|
bug_tracker_uri: https://gitlab.com/ruby-telegem/telegem/-/issues
|
|
189
177
|
documentation_uri: https://gitlab.com/ruby-telegem/telegem/-/tree/main/docs-src?ref_type=heads
|
|
190
178
|
rubygems_mfa_required: 'false'
|
|
191
|
-
post_install_message: "Thanks for installing Telegem 3.2.
|
|
179
|
+
post_install_message: "Thanks for installing Telegem 3.2.4!\n\n\U0001F4DA Documentation:
|
|
192
180
|
https://gitlab.com/ruby-telegem/telegem\n\n\U0001F510 For SSL Webhooks:\nRun: telegem-ssl
|
|
193
181
|
your-domain.com\nThis sets up Let's Encrypt certificates automatically.\n\n\U0001F916
|
|
194
182
|
Happy bot building!\n"
|