telegem 2.0.7 → 2.0.9
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/Test-Projects/Movie-tracker-bot/Gemfile +2 -0
- data/Test-Projects/Movie-tracker-bot/bot.rb +62 -0
- data/Test-Projects/Movie-tracker-bot/handlers/.gitkeep +0 -0
- data/Test-Projects/Movie-tracker-bot/handlers/add_1_.rb +160 -0
- data/Test-Projects/Movie-tracker-bot/handlers/add_2_.rb +139 -0
- data/Test-Projects/Movie-tracker-bot/handlers/premium.rb +13 -0
- data/Test-Projects/Movie-tracker-bot/handlers/report.rb +31 -0
- data/Test-Projects/Movie-tracker-bot/handlers/search.rb +150 -0
- data/Test-Projects/Movie-tracker-bot/handlers/sponsor.rb +14 -0
- data/Test-Projects/Movie-tracker-bot/handlers/start.rb +48 -0
- data/Test-Projects/Movie-tracker-bot/handlers/watch.rb +210 -0
- data/Test-Projects/Test-submitted-by-marvel/.gitkeep +0 -0
- data/Test-Projects/Test-submitted-by-marvel/Marvel-bot.md +3 -0
- data/docs-src/.gitkeep +0 -0
- data/docs-src/Bot-registration_.PNG +0 -0
- data/docs-src/bot.md +295 -0
- data/docs-src/context|ctx|.md +531 -0
- data/docs-src/getting-started.md +328 -0
- data/docs-src/keyboard_inline.md +413 -0
- data/docs-src/scene.md +509 -0
- data/docs-src/understanding-ctx.md +581 -0
- data/lib/api/client.rb +17 -16
- data/lib/core/bot.rb +19 -25
- data/lib/telegem.rb +1 -1
- data/lib/webhook/server.rb +1 -1
- metadata +26 -15
- data/docs/Api.md +0 -211
- data/docs/Cookbook(copy_paste).md +0 -644
- data/docs/Getting_started.md +0 -348
- data/docs/How_to_use.md +0 -571
- data/docs/QuickStart.md +0 -258
- data/docs/Understanding_Scene.md +0 -434
- data/docs/Usage.md +0 -717
- data/docs/webhook_setup.md +0 -199
- /data/{docs → Test-Projects/Movie-tracker-bot}/.gitkeep +0 -0
|
@@ -1,644 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
# 🍳 Telegem Cookbook
|
|
3
|
-
|
|
4
|
-
Quick copy-paste recipes for common bot tasks. When you think "How do I...", find your answer here and just paste!
|
|
5
|
-
|
|
6
|
-
**Join for help & updates:** [](https://t.me/telegem_me2)
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## 📋 Table of Contents
|
|
11
|
-
- [Basic Setup](#-basic-setup)
|
|
12
|
-
- [Message Handling](#-message-handling)
|
|
13
|
-
- [Keyboards & Buttons](#-keyboards--buttons)
|
|
14
|
-
- [Files & Media](#-files--media)
|
|
15
|
-
- [User Management](#-user-management)
|
|
16
|
-
- [Scenes & Multi-step](#-scenes--multi-step)
|
|
17
|
-
- [Utility Patterns](#-utility-patterns)
|
|
18
|
-
- [Error Handling](#-error-handling)
|
|
19
|
-
- [Deployment](#-deployment)
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## 🤖 Basic Setup
|
|
24
|
-
|
|
25
|
-
### Minimal Echo Bot
|
|
26
|
-
```ruby
|
|
27
|
-
require 'telegem'
|
|
28
|
-
bot = Telegem.new("YOUR_TOKEN")
|
|
29
|
-
bot.on(:message) { |ctx| ctx.reply("You said: #{ctx.message.text}") }
|
|
30
|
-
bot.start_polling
|
|
31
|
-
# Save as bot.rb and run: ruby bot.rb
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
Webhook Setup (Production)
|
|
35
|
-
|
|
36
|
-
```ruby
|
|
37
|
-
require 'telegem'
|
|
38
|
-
bot = Telegem.new(ENV['TELEGRAM_BOT_TOKEN'])
|
|
39
|
-
bot.on(:message) { |ctx| ctx.reply("Hello from webhook!") }
|
|
40
|
-
Telegem.webhook(bot) # One-liner for production
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
📨 Message Handling
|
|
46
|
-
|
|
47
|
-
Command with Arguments
|
|
48
|
-
|
|
49
|
-
```ruby
|
|
50
|
-
bot.command("search") do |ctx|
|
|
51
|
-
query = ctx.message.command_args # "ruby programming" from "/search ruby programming"
|
|
52
|
-
ctx.reply("Searching for: #{query}") if query
|
|
53
|
-
end
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Handle Specific Text
|
|
57
|
-
|
|
58
|
-
```ruby
|
|
59
|
-
# Exact match
|
|
60
|
-
bot.on(:message, text: "ping") { |ctx| ctx.reply("pong!") }
|
|
61
|
-
|
|
62
|
-
# Regex match (case-insensitive)
|
|
63
|
-
bot.hears(/hello|hi|hey/i) { |ctx| ctx.reply("Greetings! 👋") }
|
|
64
|
-
|
|
65
|
-
# Contains word
|
|
66
|
-
bot.on(:message) do |ctx|
|
|
67
|
-
if ctx.message.text&.include?("bot")
|
|
68
|
-
ctx.reply("You mentioned me! 🤖")
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Reply to Specific Message
|
|
74
|
-
|
|
75
|
-
```ruby
|
|
76
|
-
bot.on(:message) do |ctx|
|
|
77
|
-
# Reply to the user's message
|
|
78
|
-
ctx.reply("Got it!", reply_to_message_id: ctx.message.message_id)
|
|
79
|
-
end
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Different Chat Types
|
|
83
|
-
|
|
84
|
-
```ruby
|
|
85
|
-
bot.on(:message) do |ctx|
|
|
86
|
-
case ctx.chat.type
|
|
87
|
-
when "private"
|
|
88
|
-
ctx.reply("Private chat 👤")
|
|
89
|
-
when "group", "supergroup"
|
|
90
|
-
ctx.reply("Group chat 👥")
|
|
91
|
-
when "channel"
|
|
92
|
-
ctx.reply("Channel 📢")
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
---
|
|
98
|
-
|
|
99
|
-
⌨️ Keyboards & Buttons
|
|
100
|
-
|
|
101
|
-
Simple Menu Keyboard
|
|
102
|
-
|
|
103
|
-
```ruby
|
|
104
|
-
bot.command("menu") do |ctx|
|
|
105
|
-
keyboard = Telegem.keyboard do
|
|
106
|
-
row "🍕 Order Food", "🛍️ Shop"
|
|
107
|
-
row "ℹ️ Help", "⚙️ Settings"
|
|
108
|
-
resize true # Fits screen
|
|
109
|
-
one_time true # Hides after use
|
|
110
|
-
end
|
|
111
|
-
ctx.reply("Main Menu:", reply_markup: keyboard)
|
|
112
|
-
end
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
Inline Buttons with Callback
|
|
116
|
-
|
|
117
|
-
```ruby
|
|
118
|
-
bot.command("vote") do |ctx|
|
|
119
|
-
keyboard = Telegem.inline do
|
|
120
|
-
row do
|
|
121
|
-
callback "👍 Yes", "vote_yes"
|
|
122
|
-
callback "👎 No", "vote_no"
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
ctx.reply("Do you like Ruby?", reply_markup: keyboard)
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
# Handle button clicks
|
|
129
|
-
bot.on(:callback_query) do |ctx|
|
|
130
|
-
case ctx.data
|
|
131
|
-
when "vote_yes"
|
|
132
|
-
ctx.answer_callback_query(text: "You voted Yes! 🎉")
|
|
133
|
-
ctx.edit_message_text("✅ Thanks for voting Yes!")
|
|
134
|
-
when "vote_no"
|
|
135
|
-
ctx.answer_callback_query(text: "You voted No 😢", show_alert: true)
|
|
136
|
-
ctx.edit_message_text("❌ You voted No")
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
URL & Web App Buttons
|
|
142
|
-
|
|
143
|
-
```ruby
|
|
144
|
-
bot.command("links") do |ctx|
|
|
145
|
-
keyboard = Telegem.inline do
|
|
146
|
-
row do
|
|
147
|
-
url "🌐 Website", "https://gitlab.com/ruby-telegem/telegem"
|
|
148
|
-
url "📚 Docs", "https://core.telegram.org/bots/api"
|
|
149
|
-
end
|
|
150
|
-
row do
|
|
151
|
-
web_app "📱 Open App", "https://yourapp.com"
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
|
-
ctx.reply("Useful links:", reply_markup: keyboard)
|
|
155
|
-
end
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
Remove Keyboard
|
|
159
|
-
|
|
160
|
-
```ruby
|
|
161
|
-
bot.command("hide") do |ctx|
|
|
162
|
-
ctx.reply("Keyboard hidden!", reply_markup: Telegem.remove_keyboard)
|
|
163
|
-
end
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
Request Contact/Location
|
|
167
|
-
|
|
168
|
-
```ruby
|
|
169
|
-
bot.command("share") do |ctx|
|
|
170
|
-
keyboard = Telegem.keyboard do
|
|
171
|
-
row do
|
|
172
|
-
button "📱 Share Contact", request_contact: true
|
|
173
|
-
button "📍 Share Location", request_location: true
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
ctx.reply("Please share:", reply_markup: keyboard)
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
# Handle received contact
|
|
180
|
-
bot.on(:message) do |ctx|
|
|
181
|
-
if ctx.message.contact
|
|
182
|
-
phone = ctx.message.contact.phone_number
|
|
183
|
-
ctx.reply("Thanks! I got your number: #{phone}")
|
|
184
|
-
elsif ctx.message.location
|
|
185
|
-
lat = ctx.message.location.latitude
|
|
186
|
-
lon = ctx.message.location.longitude
|
|
187
|
-
ctx.reply("Location received: #{lat}, #{lon}")
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
---
|
|
193
|
-
|
|
194
|
-
📁 Files & Media
|
|
195
|
-
|
|
196
|
-
Send Photo from File
|
|
197
|
-
|
|
198
|
-
```ruby
|
|
199
|
-
bot.command("cat") do |ctx|
|
|
200
|
-
ctx.photo("path/to/cat.jpg", caption: "Here's a cute cat! 🐱")
|
|
201
|
-
end
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
Send Photo from URL
|
|
205
|
-
|
|
206
|
-
```ruby
|
|
207
|
-
bot.command("meme") do |ctx|
|
|
208
|
-
ctx.photo("https://api.memegen.com/images/buzz/memes/meme.jpg",
|
|
209
|
-
caption: "Random meme!")
|
|
210
|
-
end
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
Send Document
|
|
214
|
-
|
|
215
|
-
```ruby
|
|
216
|
-
bot.command("report") do |ctx|
|
|
217
|
-
ctx.document("monthly_report.pdf",
|
|
218
|
-
caption: "Monthly Report 📊",
|
|
219
|
-
filename: "report_2024.pdf") # Custom filename
|
|
220
|
-
end
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
Send Multiple Photos as Album
|
|
224
|
-
|
|
225
|
-
```ruby
|
|
226
|
-
bot.command("album") do |ctx|
|
|
227
|
-
# Note: Telegram groups media with same media_group_id
|
|
228
|
-
ctx.photo("photo1.jpg", caption: "First photo", media_group_id: "album_123")
|
|
229
|
-
ctx.photo("photo2.jpg", caption: "Second photo", media_group_id: "album_123")
|
|
230
|
-
end
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
Show "Typing" Indicator
|
|
234
|
-
|
|
235
|
-
```ruby
|
|
236
|
-
bot.command("process") do |ctx|
|
|
237
|
-
ctx.typing # Shows "typing..." for 5 seconds
|
|
238
|
-
sleep 3 # Simulate work
|
|
239
|
-
ctx.reply("Processing complete!")
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
# Or use the helper for longer operations
|
|
243
|
-
bot.command("analyze") do |ctx|
|
|
244
|
-
ctx.with_typing do
|
|
245
|
-
# Your processing code here
|
|
246
|
-
result = complex_analysis(ctx.message.text)
|
|
247
|
-
ctx.reply("Analysis: #{result}")
|
|
248
|
-
end
|
|
249
|
-
end
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
---
|
|
253
|
-
|
|
254
|
-
👥 User Management
|
|
255
|
-
|
|
256
|
-
Welcome New Members
|
|
257
|
-
|
|
258
|
-
```ruby
|
|
259
|
-
bot.on(:message) do |ctx|
|
|
260
|
-
if ctx.message.new_chat_members
|
|
261
|
-
ctx.message.new_chat_members.each do |user|
|
|
262
|
-
ctx.reply("Welcome #{user.full_name} to the group! 🎉")
|
|
263
|
-
end
|
|
264
|
-
end
|
|
265
|
-
end
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
Detect User Left
|
|
269
|
-
|
|
270
|
-
```ruby
|
|
271
|
-
bot.on(:message) do |ctx|
|
|
272
|
-
if ctx.message.left_chat_member
|
|
273
|
-
ctx.reply("#{ctx.message.left_chat_member.full_name} left the group 👋")
|
|
274
|
-
end
|
|
275
|
-
end
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
Basic Admin Commands
|
|
279
|
-
|
|
280
|
-
```ruby
|
|
281
|
-
# Only works if your bot is admin
|
|
282
|
-
bot.command("ban") do |ctx|
|
|
283
|
-
if ctx.message.reply_to_message
|
|
284
|
-
user_id = ctx.message.reply_to_message.from.id
|
|
285
|
-
ctx.ban_chat_member(user_id)
|
|
286
|
-
ctx.reply("User banned 🚫")
|
|
287
|
-
else
|
|
288
|
-
ctx.reply("Reply to a message to ban the user")
|
|
289
|
-
end
|
|
290
|
-
end
|
|
291
|
-
|
|
292
|
-
bot.command("unban") do |ctx|
|
|
293
|
-
args = ctx.message.command_args
|
|
294
|
-
if args && args.match?(/\d+/)
|
|
295
|
-
ctx.unban_chat_member(args.to_i)
|
|
296
|
-
ctx.reply("User unbanned ✅")
|
|
297
|
-
end
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
bot.command("pin") do |ctx|
|
|
301
|
-
if ctx.message.reply_to_message
|
|
302
|
-
ctx.pin_message(ctx.message.reply_to_message.message_id)
|
|
303
|
-
ctx.reply("Message pinned 📌")
|
|
304
|
-
end
|
|
305
|
-
end
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
User Cooldown / Rate Limit
|
|
309
|
-
|
|
310
|
-
```ruby
|
|
311
|
-
# Using session for simple rate limiting
|
|
312
|
-
bot.on(:message) do |ctx|
|
|
313
|
-
user_id = ctx.from.id
|
|
314
|
-
last_time = ctx.session[:last_message_time] || 0
|
|
315
|
-
|
|
316
|
-
if Time.now.to_i - last_time < 2 # 2 seconds cooldown
|
|
317
|
-
ctx.reply("Please wait a moment... ⏳")
|
|
318
|
-
else
|
|
319
|
-
ctx.session[:last_message_time] = Time.now.to_i
|
|
320
|
-
# Process message normally
|
|
321
|
-
ctx.reply("Message received!")
|
|
322
|
-
end
|
|
323
|
-
end
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
---
|
|
327
|
-
|
|
328
|
-
🎭 Scenes & Multi-step
|
|
329
|
-
|
|
330
|
-
Simple Registration Flow
|
|
331
|
-
|
|
332
|
-
```ruby
|
|
333
|
-
bot.scene("signup") do
|
|
334
|
-
step :ask_name do |ctx|
|
|
335
|
-
ctx.reply("What's your name?")
|
|
336
|
-
next_step :ask_email
|
|
337
|
-
end
|
|
338
|
-
|
|
339
|
-
step :ask_email do |ctx|
|
|
340
|
-
ctx.state[:name] = ctx.message.text
|
|
341
|
-
ctx.reply("Great #{ctx.state[:name]}! Now enter your email:")
|
|
342
|
-
next_step :confirm
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
step :confirm do |ctx|
|
|
346
|
-
ctx.state[:email] = ctx.message.text
|
|
347
|
-
|
|
348
|
-
keyboard = Telegem.inline do
|
|
349
|
-
row do
|
|
350
|
-
callback "✅ Confirm", "confirm_signup"
|
|
351
|
-
callback "🔄 Restart", "restart_signup"
|
|
352
|
-
end
|
|
353
|
-
end
|
|
354
|
-
|
|
355
|
-
ctx.reply("Confirm details:\nName: #{ctx.state[:name]}\nEmail: #{ctx.state[:email]}",
|
|
356
|
-
reply_markup: keyboard)
|
|
357
|
-
end
|
|
358
|
-
|
|
359
|
-
on_enter do |ctx|
|
|
360
|
-
ctx.reply("Starting registration... ✨")
|
|
361
|
-
ctx.state.clear # Reset temp storage
|
|
362
|
-
end
|
|
363
|
-
|
|
364
|
-
on_leave do |ctx|
|
|
365
|
-
ctx.reply("Registration complete! 🎉")
|
|
366
|
-
end
|
|
367
|
-
end
|
|
368
|
-
|
|
369
|
-
# Start the scene
|
|
370
|
-
bot.command("register") { |ctx| ctx.enter_scene("signup") }
|
|
371
|
-
|
|
372
|
-
# Handle scene callbacks
|
|
373
|
-
bot.on(:callback_query) do |ctx|
|
|
374
|
-
case ctx.data
|
|
375
|
-
when "confirm_signup"
|
|
376
|
-
ctx.answer_callback_query(text: "Account created!")
|
|
377
|
-
# Save to database here
|
|
378
|
-
ctx.leave_scene
|
|
379
|
-
when "restart_signup"
|
|
380
|
-
ctx.answer_callback_query(text: "Starting over...")
|
|
381
|
-
ctx.enter_scene("signup") # Re-enter scene
|
|
382
|
-
end
|
|
383
|
-
end
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
Shopping Cart Scene
|
|
387
|
-
|
|
388
|
-
```ruby
|
|
389
|
-
bot.scene("cart") do
|
|
390
|
-
step :show_products do |ctx|
|
|
391
|
-
products = ["🍎 Apple $1", "🍌 Banana $2", "🍊 Orange $3"]
|
|
392
|
-
keyboard = Telegem.inline do
|
|
393
|
-
products.each do |product|
|
|
394
|
-
row { callback product, "add_#{product.split.first.downcase}" }
|
|
395
|
-
end
|
|
396
|
-
row { callback "🛒 Checkout", "checkout" }
|
|
397
|
-
end
|
|
398
|
-
ctx.reply("Select products:", reply_markup: keyboard)
|
|
399
|
-
next_step :handle_selection
|
|
400
|
-
end
|
|
401
|
-
|
|
402
|
-
step :handle_selection do |ctx|
|
|
403
|
-
if ctx.callback_query
|
|
404
|
-
if ctx.data.start_with?("add_")
|
|
405
|
-
item = ctx.data.sub("add_", "")
|
|
406
|
-
ctx.session[:cart] ||= []
|
|
407
|
-
ctx.session[:cart] << item
|
|
408
|
-
ctx.answer_callback_query(text: "Added #{item} to cart!")
|
|
409
|
-
elsif ctx.data == "checkout"
|
|
410
|
-
ctx.reply("Your cart: #{ctx.session[:cart].join(', ')}")
|
|
411
|
-
ctx.leave_scene
|
|
412
|
-
end
|
|
413
|
-
end
|
|
414
|
-
# Stay in same step
|
|
415
|
-
step :show_products
|
|
416
|
-
end
|
|
417
|
-
end
|
|
418
|
-
|
|
419
|
-
bot.command("shop") { |ctx| ctx.enter_scene("cart") }
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
---
|
|
423
|
-
|
|
424
|
-
🛠️ Utility Patterns
|
|
425
|
-
|
|
426
|
-
Logging Middleware
|
|
427
|
-
|
|
428
|
-
```ruby
|
|
429
|
-
bot.use do |ctx, next_handler|
|
|
430
|
-
puts "[#{Time.now}] #{ctx.from.username}: #{ctx.message&.text || ctx.callback_query&.data}"
|
|
431
|
-
next_handler.call(ctx)
|
|
432
|
-
end
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
Authentication Middleware
|
|
436
|
-
|
|
437
|
-
```ruby
|
|
438
|
-
ADMIN_IDS = [123456789, 987654321] # Your user IDs
|
|
439
|
-
|
|
440
|
-
class AdminOnly
|
|
441
|
-
def call(ctx, next_handler)
|
|
442
|
-
if ADMIN_IDS.include?(ctx.from.id)
|
|
443
|
-
next_handler.call(ctx)
|
|
444
|
-
else
|
|
445
|
-
ctx.reply("🚫 Admin only!")
|
|
446
|
-
end
|
|
447
|
-
end
|
|
448
|
-
end
|
|
449
|
-
|
|
450
|
-
bot.use(AdminOnly.new)
|
|
451
|
-
|
|
452
|
-
# Or use inline
|
|
453
|
-
bot.use do |ctx, next_handler|
|
|
454
|
-
if ctx.from.username == "slick_phantom"
|
|
455
|
-
next_handler.call(ctx)
|
|
456
|
-
else
|
|
457
|
-
ctx.reply("You're not slick_phantom! 🤨")
|
|
458
|
-
end
|
|
459
|
-
end
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
User Session Data
|
|
463
|
-
|
|
464
|
-
```ruby
|
|
465
|
-
# Store user preferences
|
|
466
|
-
bot.command("setlang") do |ctx|
|
|
467
|
-
lang = ctx.message.command_args
|
|
468
|
-
ctx.session[:language] = lang if ["en", "es", "fr"].include?(lang)
|
|
469
|
-
ctx.reply("Language set to #{lang}")
|
|
470
|
-
end
|
|
471
|
-
|
|
472
|
-
bot.command("profile") do |ctx|
|
|
473
|
-
lang = ctx.session[:language] || "en"
|
|
474
|
-
theme = ctx.session[:theme] || "light"
|
|
475
|
-
ctx.reply("Your settings:\nLanguage: #{lang}\nTheme: #{theme}")
|
|
476
|
-
end
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
Broadcast to All Users
|
|
480
|
-
|
|
481
|
-
```ruby
|
|
482
|
-
# Store user IDs when they start the bot
|
|
483
|
-
bot.command("start") do |ctx|
|
|
484
|
-
user_id = ctx.from.id
|
|
485
|
-
# In production, save to database instead of global variable
|
|
486
|
-
$users ||= []
|
|
487
|
-
$users << user_id unless $users.include?(user_id)
|
|
488
|
-
ctx.reply("Welcome!")
|
|
489
|
-
end
|
|
490
|
-
|
|
491
|
-
# Admin command to broadcast
|
|
492
|
-
bot.command("broadcast") do |ctx|
|
|
493
|
-
if ADMIN_IDS.include?(ctx.from.id)
|
|
494
|
-
message = ctx.message.command_args
|
|
495
|
-
$users.each do |user_id|
|
|
496
|
-
begin
|
|
497
|
-
ctx.api.call("sendMessage", chat_id: user_id, text: message)
|
|
498
|
-
sleep 0.1 # Be nice to Telegram's rate limits
|
|
499
|
-
rescue => e
|
|
500
|
-
puts "Failed to send to #{user_id}: #{e.message}"
|
|
501
|
-
end
|
|
502
|
-
end
|
|
503
|
-
ctx.reply("Broadcast sent to #{$users.size} users")
|
|
504
|
-
end
|
|
505
|
-
end
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
Command Aliases
|
|
509
|
-
|
|
510
|
-
```ruby
|
|
511
|
-
# Multiple commands for same action
|
|
512
|
-
["start", "hello", "hi"].each do |cmd|
|
|
513
|
-
bot.command(cmd) do |ctx|
|
|
514
|
-
ctx.reply("Welcome to the bot! Use /help for commands.")
|
|
515
|
-
end
|
|
516
|
-
end
|
|
517
|
-
```
|
|
518
|
-
|
|
519
|
-
---
|
|
520
|
-
|
|
521
|
-
🚨 Error Handling
|
|
522
|
-
|
|
523
|
-
Global Error Handler
|
|
524
|
-
|
|
525
|
-
```ruby
|
|
526
|
-
bot.error do |error, ctx|
|
|
527
|
-
ctx&.reply("❌ Something went wrong! Our developers have been notified.")
|
|
528
|
-
puts "ERROR: #{error.class}: #{error.message}"
|
|
529
|
-
puts error.backtrace if error.backtrace
|
|
530
|
-
# Send to error tracking service (e.g., Sentry)
|
|
531
|
-
end
|
|
532
|
-
```
|
|
533
|
-
|
|
534
|
-
Safe File Operations
|
|
535
|
-
|
|
536
|
-
```ruby
|
|
537
|
-
bot.command("getfile") do |ctx|
|
|
538
|
-
begin
|
|
539
|
-
if File.exist?("data.txt")
|
|
540
|
-
ctx.document(File.open("data.txt"))
|
|
541
|
-
else
|
|
542
|
-
ctx.reply("File not found")
|
|
543
|
-
end
|
|
544
|
-
rescue => e
|
|
545
|
-
ctx.reply("Error reading file: #{e.message}")
|
|
546
|
-
end
|
|
547
|
-
end
|
|
548
|
-
```
|
|
549
|
-
|
|
550
|
-
API Error Handling
|
|
551
|
-
|
|
552
|
-
```ruby
|
|
553
|
-
bot.on(:message) do |ctx|
|
|
554
|
-
begin
|
|
555
|
-
response = ctx.reply("Processing...")
|
|
556
|
-
# response is an HTTPX request object
|
|
557
|
-
response.wait
|
|
558
|
-
if response.error
|
|
559
|
-
ctx.reply("Telegram API error: #{response.error.message}")
|
|
560
|
-
end
|
|
561
|
-
rescue Telegem::API::NetworkError => e
|
|
562
|
-
ctx.reply("Network issue: #{e.message}")
|
|
563
|
-
rescue => e
|
|
564
|
-
ctx.reply("Unexpected error")
|
|
565
|
-
end
|
|
566
|
-
end
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
---
|
|
570
|
-
|
|
571
|
-
☁️ Deployment
|
|
572
|
-
|
|
573
|
-
Render.com Deployment Files
|
|
574
|
-
|
|
575
|
-
bot.rb
|
|
576
|
-
|
|
577
|
-
```ruby
|
|
578
|
-
require 'telegem'
|
|
579
|
-
|
|
580
|
-
bot = Telegem.new(ENV['TELEGRAM_BOT_TOKEN'])
|
|
581
|
-
|
|
582
|
-
# Your bot logic here
|
|
583
|
-
bot.command("start") { |ctx| ctx.reply("Bot is live on Render! ☁️") }
|
|
584
|
-
bot.on(:message) { |ctx| ctx.reply("Echo: #{ctx.message.text}") }
|
|
585
|
-
|
|
586
|
-
# Auto-start webhook in production
|
|
587
|
-
if ENV['RACK_ENV'] == 'production'
|
|
588
|
-
Telegem.webhook(bot)
|
|
589
|
-
else
|
|
590
|
-
bot.start_polling # For local development
|
|
591
|
-
end
|
|
592
|
-
```
|
|
593
|
-
|
|
594
|
-
Gemfile
|
|
595
|
-
|
|
596
|
-
```ruby
|
|
597
|
-
source 'https://rubygems.org'
|
|
598
|
-
gem 'telegem'
|
|
599
|
-
```
|
|
600
|
-
|
|
601
|
-
config.ru (Required for Render)
|
|
602
|
-
|
|
603
|
-
```ruby
|
|
604
|
-
require './bot'
|
|
605
|
-
run ->(env) { [200, {}, ['Telegem Bot Server']] }
|
|
606
|
-
```
|
|
607
|
-
|
|
608
|
-
Environment Variables Template (.env.local)
|
|
609
|
-
|
|
610
|
-
```bash
|
|
611
|
-
TELEGRAM_BOT_TOKEN=123456:ABCdefGHIjklMNOpqrsTUVwxyz
|
|
612
|
-
TELEGRAM_SECRET_TOKEN= # Auto-generated if not set
|
|
613
|
-
PORT=3000
|
|
614
|
-
RACK_ENV=production
|
|
615
|
-
```
|
|
616
|
-
|
|
617
|
-
Health Check Endpoint (For Cloud Platforms)
|
|
618
|
-
|
|
619
|
-
```ruby
|
|
620
|
-
# Add to your bot.rb for platforms that require /health
|
|
621
|
-
require 'rack'
|
|
622
|
-
app = Rack::Builder.new do
|
|
623
|
-
map "/health" do
|
|
624
|
-
run ->(env) { [200, {}, [{ status: "ok", bot: "running" }.to_json]] }
|
|
625
|
-
end
|
|
626
|
-
# ... rest of your app
|
|
627
|
-
end
|
|
628
|
-
```
|
|
629
|
-
|
|
630
|
-
---
|
|
631
|
-
|
|
632
|
-
🎯 Pro Tips
|
|
633
|
-
|
|
634
|
-
1. Use .env files for local development with the dotenv gem
|
|
635
|
-
2. Store user data in a database (SQLite, PostgreSQL) instead of memory for persistence
|
|
636
|
-
3. Respect rate limits: Add small delays (sleep 0.1) when sending many messages
|
|
637
|
-
4. Use ctx.with_typing for operations longer than 1-2 seconds
|
|
638
|
-
5. Test with multiple users using Telegram's @BotFather test feature
|
|
639
|
-
|
|
640
|
-
---
|
|
641
|
-
|
|
642
|
-
Need more recipes? Join our community and ask! https://img.shields.io/badge/🍳-t.me/telegem__me2-blue?style=flat&logo=telegram
|
|
643
|
-
|
|
644
|
-
Copy. Paste. Ship. 🚀
|