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
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
|
|
2
|
+
# Keyboards & Inline Keyboards Guide
|
|
3
|
+
|
|
4
|
+
Learn how to create interactive buttons for your Telegram bot.
|
|
5
|
+
|
|
6
|
+
## 📱 Two Types of Keyboards
|
|
7
|
+
|
|
8
|
+
### Reply Keyboard (Bottom of Chat)
|
|
9
|
+
**Position:** Always at the bottom of the chat screen
|
|
10
|
+
**Behavior:** User taps → Sends a text message to chat
|
|
11
|
+
**Best for:** Menus, forms, options that everyone should see
|
|
12
|
+
**Visibility:** All chat members see the keyboard
|
|
13
|
+
|
|
14
|
+
### Inline Keyboard (Under Messages)
|
|
15
|
+
**Position:** Attached to a specific message
|
|
16
|
+
**Behavior:** User taps → Triggers private callback (no chat message)
|
|
17
|
+
**Best for:** Polls, votes, games, interactive content
|
|
18
|
+
**Visibility:** Only people who see the message can tap
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## ⌨️ Reply Keyboards
|
|
23
|
+
|
|
24
|
+
### Creating a Basic Keyboard
|
|
25
|
+
```ruby
|
|
26
|
+
# Simple 2x2 keyboard
|
|
27
|
+
keyboard = Telegem.keyboard do
|
|
28
|
+
row "Yes", "No" # First row with 2 buttons
|
|
29
|
+
row "Maybe", "Cancel" # Second row with 2 buttons
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Send with message
|
|
33
|
+
ctx.reply("Do you agree?", reply_markup: keyboard)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
What happens:
|
|
37
|
+
|
|
38
|
+
1. User sees keyboard at bottom with 4 buttons
|
|
39
|
+
2. User taps "Yes"
|
|
40
|
+
3. Bot receives message: "Yes"
|
|
41
|
+
4. Everyone in chat sees "User: Yes"
|
|
42
|
+
|
|
43
|
+
Handling Keyboard Responses
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
# When user taps "Yes" from keyboard
|
|
47
|
+
bot.hears("Yes") do |ctx|
|
|
48
|
+
ctx.reply("Great! You agreed! ✅")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# When user taps "No"
|
|
52
|
+
bot.hears("No") do |ctx|
|
|
53
|
+
ctx.reply("Oh, you disagreed ❌")
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Keyboard Options
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
keyboard = Telegem.keyboard do
|
|
61
|
+
row "Option 1", "Option 2"
|
|
62
|
+
end.resize(false) # Don't resize buttons (keep original size)
|
|
63
|
+
.one_time(true) # Hide keyboard after first use
|
|
64
|
+
.selective(true) # Show only to mentioned users
|
|
65
|
+
|
|
66
|
+
ctx.reply("Choose:", reply_markup: keyboard)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Special Button Types
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
keyboard = Telegem.keyboard do
|
|
73
|
+
request_contact("📞 Share Phone") # Requests phone number
|
|
74
|
+
request_location("📍 Share Location") # Requests GPS location
|
|
75
|
+
request_poll("📊 Create Poll") # Requests to create poll
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
ctx.reply("Share your info:", reply_markup: keyboard)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Removing Keyboards
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
# Method 1: Using helper
|
|
85
|
+
ctx.reply("Keyboard removed", reply_markup: Telegem.remove)
|
|
86
|
+
|
|
87
|
+
# Method 2: After user makes choice
|
|
88
|
+
bot.hears("Done") do |ctx|
|
|
89
|
+
ctx.reply("Finished!", reply_markup: { remove_keyboard: true })
|
|
90
|
+
end
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Real Example: Pizza Order
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
bot.command("order") do |ctx|
|
|
97
|
+
# Step 1: Ask for size
|
|
98
|
+
keyboard = Telegem.keyboard do
|
|
99
|
+
row "Small 🍕", "Medium 🍕", "Large 🍕"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
ctx.session[:step] = "size"
|
|
103
|
+
ctx.reply("Choose pizza size:", reply_markup: keyboard)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Handle size selection
|
|
107
|
+
bot.hears(/Small|Medium|Large/) do |ctx|
|
|
108
|
+
if ctx.session[:step] == "size"
|
|
109
|
+
ctx.session[:size] = ctx.message.text
|
|
110
|
+
|
|
111
|
+
# Step 2: Ask for toppings
|
|
112
|
+
keyboard = Telegem.keyboard do
|
|
113
|
+
row "Cheese 🧀", "Pepperoni 🍖"
|
|
114
|
+
row "Mushroom 🍄", "Veggie 🥦"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
ctx.session[:step] = "topping"
|
|
118
|
+
ctx.reply("Choose toppings:", reply_markup: keyboard)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
🔘 Inline Keyboards
|
|
126
|
+
|
|
127
|
+
Creating Basic Inline Buttons
|
|
128
|
+
|
|
129
|
+
```ruby
|
|
130
|
+
# Create inline keyboard
|
|
131
|
+
inline = Telegem.inline do
|
|
132
|
+
row button "👍 Like", callback_data: "like"
|
|
133
|
+
row button "👎 Dislike", callback_data: "dislike"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Send message with buttons underneath
|
|
137
|
+
ctx.reply("Rate this message:", reply_markup: inline)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
What happens:
|
|
141
|
+
|
|
142
|
+
1. Message appears with buttons under it
|
|
143
|
+
2. User taps "👍 Like"
|
|
144
|
+
3. NO message appears in chat
|
|
145
|
+
4. Bot receives private callback: data: "like"
|
|
146
|
+
|
|
147
|
+
Handling Inline Button Clicks
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
# This handles ALL inline button clicks
|
|
151
|
+
bot.on(:callback_query) do |ctx|
|
|
152
|
+
# ctx.data contains the callback_data from button
|
|
153
|
+
|
|
154
|
+
case ctx.data
|
|
155
|
+
when "like"
|
|
156
|
+
# Show popup notification (disappears after 2 seconds)
|
|
157
|
+
ctx.answer_callback_query(text: "Thanks for liking! ❤️")
|
|
158
|
+
|
|
159
|
+
# Update the original message
|
|
160
|
+
ctx.edit_message_text("✅ You liked this!")
|
|
161
|
+
|
|
162
|
+
when "dislike"
|
|
163
|
+
ctx.answer_callback_query(text: "Sorry you didn't like it 😢")
|
|
164
|
+
ctx.edit_message_text("😞 You disliked this")
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Different Button Types
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
inline = Telegem.inline do
|
|
173
|
+
# Opens URL when clicked
|
|
174
|
+
url "🌐 Visit Website", "https://example.com"
|
|
175
|
+
|
|
176
|
+
# Triggers callback (most common)
|
|
177
|
+
callback "✅ Select", "select_item_123"
|
|
178
|
+
|
|
179
|
+
# Opens web app
|
|
180
|
+
web_app "📱 Open App", "https://app.example.com"
|
|
181
|
+
|
|
182
|
+
# Login with Telegram
|
|
183
|
+
login "🔐 Login", "https://example.com/login"
|
|
184
|
+
|
|
185
|
+
# Switch to inline mode
|
|
186
|
+
switch_inline "🔍 Search", "cats"
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
ctx.reply("Choose action:", reply_markup: inline)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Interactive Quiz Example
|
|
193
|
+
|
|
194
|
+
```ruby
|
|
195
|
+
bot.command("quiz") do |ctx|
|
|
196
|
+
inline = Telegem.inline do
|
|
197
|
+
row button "Paris", callback_data: "answer_paris"
|
|
198
|
+
row button "London", callback_data: "answer_london"
|
|
199
|
+
row button "Berlin", callback_data: "answer_berlin"
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
ctx.reply("What is the capital of France?", reply_markup: inline)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
bot.on(:callback_query) do |ctx|
|
|
206
|
+
case ctx.data
|
|
207
|
+
when "answer_paris"
|
|
208
|
+
ctx.answer_callback_query(text: "✅ Correct! Paris is right!")
|
|
209
|
+
ctx.edit_message_text("🎉 Correct! Paris is the capital of France")
|
|
210
|
+
|
|
211
|
+
when "answer_london", "answer_berlin"
|
|
212
|
+
ctx.answer_callback_query(text: "❌ Wrong! Try again", show_alert: true)
|
|
213
|
+
# Keep same question for retry
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Pagination Example (Multiple Pages)
|
|
219
|
+
|
|
220
|
+
```ruby
|
|
221
|
+
ITEMS = ["Apple", "Banana", "Cherry", "Date", "Elderberry", "Fig", "Grape"]
|
|
222
|
+
|
|
223
|
+
def items_keyboard(page = 0)
|
|
224
|
+
start = page * 3
|
|
225
|
+
page_items = ITEMS[start, 3] || []
|
|
226
|
+
|
|
227
|
+
Telegem.inline do
|
|
228
|
+
# Current page items
|
|
229
|
+
page_items.each do |item|
|
|
230
|
+
row button item, callback_data: "select_#{item}"
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Navigation buttons
|
|
234
|
+
row do
|
|
235
|
+
if page > 0
|
|
236
|
+
button "⬅️ Previous", callback_data: "page_#{page-1}"
|
|
237
|
+
end
|
|
238
|
+
button "📄 Page #{page+1}", callback_data: "current"
|
|
239
|
+
if ITEMS.length > (page + 1) * 3
|
|
240
|
+
button "Next ➡️", callback_data: "page_#{page+1}"
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
bot.command("fruits") do |ctx|
|
|
247
|
+
ctx.reply("Select a fruit:", reply_markup: items_keyboard)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
bot.on(:callback_query) do |ctx|
|
|
251
|
+
if ctx.data.start_with?("select_")
|
|
252
|
+
fruit = ctx.data.split("_").last
|
|
253
|
+
ctx.answer_callback_query(text: "Selected: #{fruit}")
|
|
254
|
+
ctx.edit_message_text("You selected: #{fruit} 🍎")
|
|
255
|
+
|
|
256
|
+
elsif ctx.data.start_with?("page_")
|
|
257
|
+
page = ctx.data.split("_").last.to_i
|
|
258
|
+
ctx.edit_message_reply_markup(items_keyboard(page))
|
|
259
|
+
ctx.answer_callback_query # Empty response (no popup)
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
⚖️ When to Use Which?
|
|
267
|
+
|
|
268
|
+
Use Reply Keyboard When:
|
|
269
|
+
|
|
270
|
+
· Creating a main menu
|
|
271
|
+
· Collecting user information (name, age, etc.)
|
|
272
|
+
· Multi-step forms
|
|
273
|
+
· Simple yes/no questions
|
|
274
|
+
· You want everyone to see the choice
|
|
275
|
+
|
|
276
|
+
Use Inline Keyboard When:
|
|
277
|
+
|
|
278
|
+
· Creating polls or votes
|
|
279
|
+
· Interactive games
|
|
280
|
+
· Confirmation dialogs (delete? confirm?)
|
|
281
|
+
· Paginated lists
|
|
282
|
+
· You want to keep chat clean
|
|
283
|
+
· Private interactions
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
🔧 Best Practices
|
|
288
|
+
|
|
289
|
+
1. Button Text Length
|
|
290
|
+
|
|
291
|
+
```ruby
|
|
292
|
+
# ❌ Too long
|
|
293
|
+
button "Click here to confirm your selection and proceed to checkout"
|
|
294
|
+
|
|
295
|
+
# ✅ Concise and clear
|
|
296
|
+
button "Confirm Purchase"
|
|
297
|
+
button "Proceed to Checkout"
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
2. Logical Grouping
|
|
301
|
+
|
|
302
|
+
```ruby
|
|
303
|
+
# ✅ Group related actions
|
|
304
|
+
Telegem.keyboard do
|
|
305
|
+
row "View Products", "View Cart" # Shopping actions
|
|
306
|
+
row "My Account", "Help" # Account actions
|
|
307
|
+
row "Cancel" # Cancel action
|
|
308
|
+
end
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
3. Callback Data Optimization
|
|
312
|
+
|
|
313
|
+
```ruby
|
|
314
|
+
# Store data efficiently in callback_data
|
|
315
|
+
# Format: "action:id:extra"
|
|
316
|
+
callback_data: "like:post_123:user_456"
|
|
317
|
+
|
|
318
|
+
# In handler
|
|
319
|
+
if ctx.data.start_with?("like:")
|
|
320
|
+
action, post_id, user_id = ctx.data.split(":")
|
|
321
|
+
# Process like
|
|
322
|
+
end
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
4. Error Handling
|
|
326
|
+
|
|
327
|
+
```ruby
|
|
328
|
+
bot.on(:callback_query) do |ctx|
|
|
329
|
+
begin
|
|
330
|
+
# Your button handling logic
|
|
331
|
+
rescue => e
|
|
332
|
+
ctx.answer_callback_query(
|
|
333
|
+
text: "Something went wrong!",
|
|
334
|
+
show_alert: true
|
|
335
|
+
)
|
|
336
|
+
ctx.logger.error("Button error: #{e.message}")
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
🎮 Complete Game Example
|
|
344
|
+
|
|
345
|
+
```ruby
|
|
346
|
+
class NumberGame
|
|
347
|
+
def self.start_game(ctx)
|
|
348
|
+
inline = Telegem.inline do
|
|
349
|
+
row button "1️⃣", callback_data: "guess_1"
|
|
350
|
+
row button "2️⃣", callback_data: "guess_2"
|
|
351
|
+
row button "3️⃣", callback_data: "guess_3"
|
|
352
|
+
row button "4️⃣", callback_data: "guess_4"
|
|
353
|
+
row button "5️⃣", callback_data: "guess_5"
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
ctx.session[:secret_number] = rand(1..5)
|
|
357
|
+
ctx.reply("Guess the number (1-5):", reply_markup: inline)
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def self.handle_guess(ctx)
|
|
361
|
+
guess = ctx.data.split("_").last.to_i
|
|
362
|
+
secret = ctx.session[:secret_number]
|
|
363
|
+
|
|
364
|
+
if guess == secret
|
|
365
|
+
ctx.answer_callback_query(text: "🎉 Correct! You won!")
|
|
366
|
+
ctx.edit_message_text("✅ Correct! The number was #{secret}")
|
|
367
|
+
|
|
368
|
+
# Play again button
|
|
369
|
+
inline = Telegem.inline do
|
|
370
|
+
row button "Play Again", callback_data: "play_again"
|
|
371
|
+
end
|
|
372
|
+
ctx.edit_message_reply_markup(inline)
|
|
373
|
+
|
|
374
|
+
else
|
|
375
|
+
ctx.answer_callback_query(text: "❌ Wrong! Try again")
|
|
376
|
+
# Keep same buttons
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# Register handlers
|
|
382
|
+
bot.command("game") { |ctx| NumberGame.start_game(ctx) }
|
|
383
|
+
bot.on(:callback_query) do |ctx|
|
|
384
|
+
if ctx.data.start_with?("guess_")
|
|
385
|
+
NumberGame.handle_guess(ctx)
|
|
386
|
+
elsif ctx.data == "play_again"
|
|
387
|
+
NumberGame.start_game(ctx)
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
📚 Summary
|
|
395
|
+
|
|
396
|
+
Aspect Reply Keyboard Inline Keyboard
|
|
397
|
+
Position Bottom of chat Under message
|
|
398
|
+
Creates Message Yes No
|
|
399
|
+
Chat Visibility Everyone sees choice Private
|
|
400
|
+
Best For Menus, forms Polls, games
|
|
401
|
+
Response Method bot.hears() bot.on(:callback_query)
|
|
402
|
+
Clean Chat No (adds messages) Yes (no spam)
|
|
403
|
+
|
|
404
|
+
Remember:
|
|
405
|
+
|
|
406
|
+
· Reply keyboards = Public conversation
|
|
407
|
+
· Inline keyboards = Private interaction
|
|
408
|
+
· Choose based on whether you want choices visible to everyone
|
|
409
|
+
|
|
410
|
+
Start with simple keyboards, then add inline buttons for interactive features! 🚀
|
|
411
|
+
|
|
412
|
+
```
|
|
413
|
+
```
|