telegem 1.0.4 → 1.0.5
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 +19 -19
- data/docs/UNDERSTANDING-WEBHOOK-n-POLLING.md +241 -0
- data/docs/Understanding_Scene.md +434 -0
- data/lib/core/bot.rb +1 -1
- data/lib/telegem.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 962341055439e51fd1eafe1d15cffffb8d3c7b752f962e66cc16394e3950ae68
|
|
4
|
+
data.tar.gz: 8dcf816cf6311a294668100ad7cdc4f12e5127fd1b6fd592842c8514e2962177
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8e02edba543e2bf5939a008f192c8149b1be2f28e949dee12956fa4d7f0dd7e0466f429c9a7fc16d26839e05adac0f182755fd2778e483489d057a7d2ab192ab
|
|
7
|
+
data.tar.gz: 155c9426032522dd6f604d1e7e9235c3a4ab2052a2d0c5bc03164cfc4b70f00341914352c69ffa9d0c73ff6ea66ce7ee7d5269ec79b70ede7b97fe831d53f262
|
data/Readme.md
CHANGED
|
@@ -10,15 +10,15 @@ Blazing-fast, modern Telegram Bot framework for Ruby. Inspired by Telegraf.js, b
|
|
|
10
10
|
|
|
11
11
|
✨ Features
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
- ⚡ True httpx(Async) I/O - Built on async gem, not blocking threads
|
|
14
|
+
- 🎯 Telegraf-style DSL - Familiar API for JavaScript developers
|
|
15
|
+
- 🔌 Middleware System - Compose behavior like Express.js
|
|
16
|
+
- 🧙 Scene System - Multi-step conversations (wizards/forms)
|
|
17
|
+
- 💾 Session Management - Redis, memory, or custom stores
|
|
18
|
+
- ⌨️ Keyboard DSL - Clean markup builders with fluent API
|
|
19
|
+
- 🌐 Webhook Server - Production-ready async HTTP server
|
|
20
|
+
- 🏗️ Type-Safe Objects - Ruby classes for all Telegram types
|
|
21
|
+
- 📦 Minimal Dependencies - Just async gems + mime-types
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
@@ -87,13 +87,14 @@ Example bot with interactive keyboard and scene-based ordering flow
|
|
|
87
87
|
|
|
88
88
|
vs. Other Ruby Telegram Libraries
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
90
|
+
| Telegem | telegram-bot-ruby |
|
|
91
|
+
| ------ | ------ |
|
|
92
|
+
| fast async | multiple thread |
|
|
93
|
+
| non blocking request | slow thread based |
|
|
94
|
+
| clean telegraf dsl | no dsl |
|
|
95
|
+
| scene management. | verbose |
|
|
96
|
+
| middleware | raw json |
|
|
97
|
+
| clean markup dsl|. |
|
|
97
98
|
|
|
98
99
|
Perfect For:
|
|
99
100
|
|
|
@@ -194,8 +195,7 @@ server = bot.webhook_server(
|
|
|
194
195
|
)
|
|
195
196
|
|
|
196
197
|
# Set webhook automatically
|
|
197
|
-
|
|
198
|
-
await bot.set_webhook(
|
|
198
|
+
bot.set_webhook(
|
|
199
199
|
url: "https://#{ENV['DOMAIN']}/webhook/#{bot.token}",
|
|
200
200
|
max_connections: 40
|
|
201
201
|
)
|
|
@@ -302,7 +302,7 @@ Coming Soon
|
|
|
302
302
|
|
|
303
303
|
In Progress
|
|
304
304
|
|
|
305
|
-
- Async
|
|
305
|
+
- httpx(Async) - Non-blocking I/O
|
|
306
306
|
- Scene System - Multi-step conversations
|
|
307
307
|
- Middleware Pipeline - Extensible architecture
|
|
308
308
|
- Webhook Server - Production deployment
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
📡 Setting Up Webhooks - Complete Beginner's Guide
|
|
2
|
+
|
|
3
|
+
Webhooks sound complicated, but they're actually simple. Let me explain with an analogy:
|
|
4
|
+
|
|
5
|
+
🍕 The Pizza Delivery Analogy
|
|
6
|
+
|
|
7
|
+
Polling (The Old Way)
|
|
8
|
+
|
|
9
|
+
You call the pizza shop every minute: "Is my pizza ready yet? No? OK, I'll call again in a minute..."
|
|
10
|
+
|
|
11
|
+
Code: bot.start_polling() - Your bot calls Telegram every second asking for new messages.
|
|
12
|
+
|
|
13
|
+
Webhooks (The Smart Way)
|
|
14
|
+
|
|
15
|
+
You give the pizza shop your address. When pizza is ready, they deliver it to you.
|
|
16
|
+
|
|
17
|
+
Code: bot.set_webhook(url) - You give Telegram your server's address. When there's a message, they send it to you.
|
|
18
|
+
|
|
19
|
+
🚀 Quick Examples
|
|
20
|
+
|
|
21
|
+
Example 1: Polling (Easiest for Beginners)
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
require 'telegem'
|
|
25
|
+
|
|
26
|
+
bot = Telegem.new('YOUR_TOKEN')
|
|
27
|
+
|
|
28
|
+
bot.command('start') do |ctx|
|
|
29
|
+
ctx.reply("Hello! I'm using polling 🍕")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Just ONE line - that's it!
|
|
33
|
+
bot.start_polling
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Run it:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
ruby bot.rb
|
|
40
|
+
# Bot starts checking for messages every second
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Example 2: Webhook (For Production)
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
require 'telegem'
|
|
47
|
+
|
|
48
|
+
bot = Telegem.new('YOUR_TOKEN')
|
|
49
|
+
|
|
50
|
+
bot.command('start') do |ctx|
|
|
51
|
+
ctx.reply("Hello! I'm using webhooks 🚚")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Step 1: Start your "delivery address" (server)
|
|
55
|
+
server = bot.webhook(port: 3000)
|
|
56
|
+
server.run
|
|
57
|
+
|
|
58
|
+
# Step 2: Tell Telegram your address
|
|
59
|
+
bot.set_webhook!("https://your-domain.com/webhook/#{bot.token}")
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Run it:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
ruby bot.rb
|
|
66
|
+
# Bot waits for Telegram to deliver messages
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
🔄 Hot-Swap Between Polling & Webhooks
|
|
70
|
+
|
|
71
|
+
Here's how to switch while your bot is running:
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
require 'telegem'
|
|
75
|
+
|
|
76
|
+
bot = Telegem.new('YOUR_TOKEN')
|
|
77
|
+
|
|
78
|
+
bot.command('mode') do |ctx|
|
|
79
|
+
ctx.reply("Use /polling or /webhook to switch modes")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
bot.command('polling') do |ctx|
|
|
83
|
+
# Switch TO polling
|
|
84
|
+
bot.shutdown # Stop current mode
|
|
85
|
+
bot.start_polling # Start polling
|
|
86
|
+
ctx.reply("✅ Switched to polling mode")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
bot.command('webhook') do |ctx|
|
|
90
|
+
# Switch TO webhook
|
|
91
|
+
bot.shutdown # Stop current mode
|
|
92
|
+
|
|
93
|
+
server = bot.webhook(port: 3000)
|
|
94
|
+
server.run
|
|
95
|
+
|
|
96
|
+
# For demo, use ngrok URL (get yours at ngrok.com)
|
|
97
|
+
bot.set_webhook!("https://abc123.ngrok.io/webhook/#{bot.token}")
|
|
98
|
+
|
|
99
|
+
ctx.reply("✅ Switched to webhook mode")
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Start with polling by default
|
|
103
|
+
bot.start_polling
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
📊 Polling vs Webhooks: Side-by-Side Comparison
|
|
107
|
+
|
|
108
|
+
|Polling 🍕| Webhooks 🚚 |
|
|
109
|
+
| ------- | ------- |
|
|
110
|
+
| Bot asks Telegram: "New messages?" | Telegram sends messages to bot |
|
|
111
|
+
| Setup bot.start_polling() | Server + set_webhook() |
|
|
112
|
+
| Best for Development, testing | Production, high traffic |
|
|
113
|
+
| Speed Up to 1-second delay | Instant delivery |
|
|
114
|
+
| SSL Required? ❌ No | ✅ Yes (Telegram requires HTTPS) |
|
|
115
|
+
| Server Needed? ❌ No | ✅ Yes |
|
|
116
|
+
| Battery/CPU ⚠️ Uses more (always checking) | ✅ Efficient (sleeps until delivery) |
|
|
117
|
+
| Can Switch? ✅ Yes (hot-swap) | ✅ Yes (hot-swap) |
|
|
118
|
+
|
|
119
|
+
🎯 When to Use Which?
|
|
120
|
+
|
|
121
|
+
Use Polling When:
|
|
122
|
+
|
|
123
|
+
· 👶 You're learning - Keep it simple
|
|
124
|
+
· 💻 Developing locally - No server setup needed
|
|
125
|
+
· 🧪 Testing new features - Quick restarts
|
|
126
|
+
· 📱 Running on your laptop - No public URL needed
|
|
127
|
+
|
|
128
|
+
Use Webhooks When:
|
|
129
|
+
|
|
130
|
+
· 🚀 Going to production - Better performance
|
|
131
|
+
· 📈 Expecting many users - Handles traffic better
|
|
132
|
+
· ☁️ Hosting on a server - You have a public URL
|
|
133
|
+
· 🔋 Saving resources - Uses less CPU/battery
|
|
134
|
+
|
|
135
|
+
🔧 Webhook Setup for Beginners
|
|
136
|
+
|
|
137
|
+
Step 1: Get a Public URL (Development)
|
|
138
|
+
|
|
139
|
+
For development, use ngrok (free):
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Install ngrok, then run:
|
|
143
|
+
ngrok http 3000
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
You'll get a URL like: https://abc123.ngrok.io
|
|
147
|
+
|
|
148
|
+
Step 2: Simple Webhook Bot
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
# webhook_bot.rb
|
|
152
|
+
require 'telegem'
|
|
153
|
+
|
|
154
|
+
bot = Telegem.new('YOUR_TOKEN')
|
|
155
|
+
|
|
156
|
+
bot.command('start') do |ctx|
|
|
157
|
+
ctx.reply("I'm using webhooks! Try /info")
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
bot.command('info') do |ctx|
|
|
161
|
+
info = bot.get_webhook_info!
|
|
162
|
+
ctx.reply("Webhook info: #{info}")
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Start server
|
|
166
|
+
server = bot.webhook(port: 3000)
|
|
167
|
+
server.run
|
|
168
|
+
|
|
169
|
+
puts "🚀 Server running! Set webhook to:"
|
|
170
|
+
puts "https://YOUR_NGROK_URL.ngrok.io/webhook/#{bot.token}"
|
|
171
|
+
puts ""
|
|
172
|
+
puts "Run this command to set webhook:"
|
|
173
|
+
puts "bot.set_webhook!('https://YOUR_NGROK_URL.ngrok.io/webhook/#{bot.token}')"
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Step 3: Set the Webhook
|
|
177
|
+
|
|
178
|
+
Open IRB in another terminal:
|
|
179
|
+
|
|
180
|
+
```ruby
|
|
181
|
+
require 'telegem'
|
|
182
|
+
bot = Telegem.new('YOUR_TOKEN')
|
|
183
|
+
bot.set_webhook!('https://abc123.ngrok.io/webhook/YOUR_TOKEN')
|
|
184
|
+
# => true (means success!)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
❓ Common Questions
|
|
188
|
+
|
|
189
|
+
"Do I need to keep my computer on?"
|
|
190
|
+
|
|
191
|
+
· Polling: ❌ Yes, bot must keep asking
|
|
192
|
+
· Webhook: ✅ No, your SERVER needs to be on
|
|
193
|
+
|
|
194
|
+
"Can I use webhook without a server?"
|
|
195
|
+
|
|
196
|
+
No, you need somewhere for Telegram to deliver messages. But many free options:
|
|
197
|
+
|
|
198
|
+
· Railway (free tier) - railway.app
|
|
199
|
+
· Render (free tier) - render.com
|
|
200
|
+
· Heroku (free tier) - heroku.com
|
|
201
|
+
|
|
202
|
+
"Which is faster?"
|
|
203
|
+
|
|
204
|
+
Webhooks! Messages arrive instantly instead of up to 1-second delay.
|
|
205
|
+
|
|
206
|
+
🎮 Try It Yourself Exercise
|
|
207
|
+
|
|
208
|
+
Create mode_demo.rb:
|
|
209
|
+
|
|
210
|
+
```ruby
|
|
211
|
+
require 'telegem'
|
|
212
|
+
|
|
213
|
+
bot = Telegem.new('YOUR_TOKEN')
|
|
214
|
+
|
|
215
|
+
bot.command('help') do |ctx|
|
|
216
|
+
ctx.reply(<<~TEXT
|
|
217
|
+
🤖 Mode Demo Bot
|
|
218
|
+
|
|
219
|
+
/polling - Switch to polling mode
|
|
220
|
+
/webhook - Switch to webhook mode
|
|
221
|
+
/current - Show current mode
|
|
222
|
+
/test - Send test message
|
|
223
|
+
TEXT
|
|
224
|
+
)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Start in polling mode (easiest)
|
|
228
|
+
bot.start_polling
|
|
229
|
+
puts "Bot started in polling mode. Try /help"
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Run it and try switching modes while the bot is running!
|
|
233
|
+
|
|
234
|
+
📚 Remember This:
|
|
235
|
+
|
|
236
|
+
· Start with polling - It's easier
|
|
237
|
+
· Switch to webhooks when going to production
|
|
238
|
+
· You can always switch back - It's not permanent
|
|
239
|
+
· Your bot code stays the same - Only the delivery method changes
|
|
240
|
+
|
|
241
|
+
Happy bot building! 🎉
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
🧠 Telegem Scenes: The Complete Guide (Without the Confusion)
|
|
2
|
+
|
|
3
|
+
Scenes are conversation flows in Telegem. They handle back-and-forth interactions like forms, surveys, or onboarding. Let's remove all confusion.
|
|
4
|
+
|
|
5
|
+
📖 The Two-Step Dance: Define vs. Start
|
|
6
|
+
|
|
7
|
+
Scenes work in two distinct phases:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
# PHASE 1: DEFINE (Write the script)
|
|
11
|
+
bot.scene :registration do
|
|
12
|
+
step :ask_name { |ctx| ctx.reply "What's your name?" }
|
|
13
|
+
step :ask_email { |ctx| ctx.reply "What's your email?" }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# PHASE 2: START (Run the script)
|
|
17
|
+
bot.command('register') do |ctx|
|
|
18
|
+
ctx.enter_scene(:registration) # This actually begins the scene
|
|
19
|
+
end
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
🎯 The Analogy That Makes Sense
|
|
23
|
+
|
|
24
|
+
· bot.scene = Writing a movie script
|
|
25
|
+
· ctx.enter_scene = Yelling "Action!" on set
|
|
26
|
+
· The scene steps = What the actors actually do
|
|
27
|
+
|
|
28
|
+
🔧 The Complete Working Template
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
require 'telegem'
|
|
32
|
+
|
|
33
|
+
bot = Telegem.new('YOUR_BOT_TOKEN')
|
|
34
|
+
|
|
35
|
+
# =============== DEFINE THE SCENE ===============
|
|
36
|
+
bot.scene :feedback do
|
|
37
|
+
# Optional: Runs when scene starts
|
|
38
|
+
on_enter do |ctx|
|
|
39
|
+
ctx.session[:feedback] = { user_id: ctx.from.id }
|
|
40
|
+
ctx.reply "📝 Let's collect your feedback!"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Step 1: Ask for rating
|
|
44
|
+
step :ask_rating do |ctx|
|
|
45
|
+
keyboard = Telegem::Markup.inline do
|
|
46
|
+
row callback("⭐️ 1", "rating_1"), callback("⭐️ 2", "rating_2")
|
|
47
|
+
row callback("⭐️ 3", "rating_3"), callback("⭐️ 4", "rating_4"), callback("⭐️ 5", "rating_5")
|
|
48
|
+
end
|
|
49
|
+
ctx.reply "How would you rate us? (1-5 stars)", reply_markup: keyboard
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Step 2: Handle rating choice
|
|
53
|
+
step :handle_rating do |ctx|
|
|
54
|
+
if ctx.data&.start_with?('rating_')
|
|
55
|
+
rating = ctx.data.split('_').last.to_i
|
|
56
|
+
ctx.session[:feedback][:rating] = rating
|
|
57
|
+
ctx.reply "Thanks! Now please share your comments:"
|
|
58
|
+
else
|
|
59
|
+
ctx.reply "Please use the buttons above ⬆️"
|
|
60
|
+
return # Stay on this step
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Step 3: Collect comments
|
|
65
|
+
step :collect_comments do |ctx|
|
|
66
|
+
if ctx.message.text
|
|
67
|
+
ctx.session[:feedback][:comments] = ctx.message.text
|
|
68
|
+
|
|
69
|
+
# Show summary
|
|
70
|
+
summary = <<~SUMMARY
|
|
71
|
+
📋 **Feedback Summary:**
|
|
72
|
+
|
|
73
|
+
Rating: #{ctx.session[:feedback][:rating]} stars
|
|
74
|
+
Comments: #{ctx.session[:feedback][:comments]}
|
|
75
|
+
|
|
76
|
+
Submit? (yes/no)
|
|
77
|
+
SUMMARY
|
|
78
|
+
|
|
79
|
+
ctx.reply summary, parse_mode: 'Markdown'
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Step 4: Final confirmation
|
|
84
|
+
step :confirm do |ctx|
|
|
85
|
+
if ctx.message.text&.downcase == 'yes'
|
|
86
|
+
save_feedback(ctx.session[:feedback])
|
|
87
|
+
ctx.reply "✅ Feedback submitted! Thank you."
|
|
88
|
+
else
|
|
89
|
+
ctx.reply "❌ Feedback cancelled."
|
|
90
|
+
end
|
|
91
|
+
ctx.leave_scene
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Optional: Runs when scene ends (success or cancel)
|
|
95
|
+
on_leave do |ctx|
|
|
96
|
+
ctx.session.delete(:feedback)
|
|
97
|
+
ctx.reply "Back to main menu!"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# =============== START THE SCENE ===============
|
|
102
|
+
bot.command('feedback') do |ctx|
|
|
103
|
+
ctx.enter_scene(:feedback) # THIS actually starts it
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Start the bot
|
|
107
|
+
bot.start_polling
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
📦 4 Production-Ready Scene Templates
|
|
111
|
+
|
|
112
|
+
Template 1: Simple Form (Beginner Friendly)
|
|
113
|
+
|
|
114
|
+
Perfect for basic data collection.
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
bot.scene :quick_survey do
|
|
118
|
+
on_enter { |ctx| ctx.reply "Quick 3-question survey:" }
|
|
119
|
+
|
|
120
|
+
step :q1 do |ctx|
|
|
121
|
+
ctx.reply "1. What's your favorite language? (Ruby/JS/Python)"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
step :q2 do |ctx|
|
|
125
|
+
ctx.session[:lang] = ctx.message.text
|
|
126
|
+
ctx.reply "2. How many years experience?"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
step :q3 do |ctx|
|
|
130
|
+
ctx.session[:exp] = ctx.message.text
|
|
131
|
+
ctx.reply "3. What's your biggest challenge?"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
step :finish do |ctx|
|
|
135
|
+
ctx.session[:challenge] = ctx.message.text
|
|
136
|
+
save_survey(ctx.session)
|
|
137
|
+
ctx.reply "✅ Survey complete! Thanks for sharing."
|
|
138
|
+
ctx.leave_scene
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Usage: /survey
|
|
143
|
+
bot.command('survey') { |ctx| ctx.enter_scene(:quick_survey) }
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Template 2: Menu Navigator (With Branching)
|
|
147
|
+
|
|
148
|
+
For complex flows with different paths.
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
bot.scene :support_ticket do
|
|
152
|
+
step :ask_type do |ctx|
|
|
153
|
+
keyboard = Telegem::Markup.keyboard do
|
|
154
|
+
row "🐛 Bug Report", "✨ Feature Request"
|
|
155
|
+
row "❓ Question", "🔧 Technical Issue"
|
|
156
|
+
end
|
|
157
|
+
ctx.reply "What type of support do you need?", reply_markup: keyboard
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
step :collect_details do |ctx|
|
|
161
|
+
type = ctx.message.text
|
|
162
|
+
ctx.session[:ticket_type] = type
|
|
163
|
+
|
|
164
|
+
case type
|
|
165
|
+
when "🐛 Bug Report"
|
|
166
|
+
ctx.reply "Describe the bug (steps to reproduce):"
|
|
167
|
+
ctx.session[:next_action] = :save_bug
|
|
168
|
+
when "✨ Feature Request"
|
|
169
|
+
ctx.reply "Describe the feature you'd like:"
|
|
170
|
+
ctx.session[:next_action] = :save_feature
|
|
171
|
+
else
|
|
172
|
+
ctx.reply "Describe your issue:"
|
|
173
|
+
ctx.session[:next_action] = :save_general
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
step :process_ticket do |ctx|
|
|
178
|
+
description = ctx.message.text
|
|
179
|
+
ticket_id = "TICKET-#{SecureRandom.hex(4).upcase}"
|
|
180
|
+
|
|
181
|
+
case ctx.session[:next_action]
|
|
182
|
+
when :save_bug
|
|
183
|
+
save_to_database(type: 'bug', desc: description, id: ticket_id)
|
|
184
|
+
ctx.reply "🐛 Bug logged as #{ticket_id}"
|
|
185
|
+
when :save_feature
|
|
186
|
+
save_to_database(type: 'feature', desc: description, id: ticket_id)
|
|
187
|
+
ctx.reply "✨ Feature requested as #{ticket_id}"
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
ctx.leave_scene
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
on_leave do |ctx|
|
|
194
|
+
ctx.reply "Support will contact you soon. Use /status to check ticket."
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Usage: /support
|
|
199
|
+
bot.command('support') { |ctx| ctx.enter_scene(:support_ticket) }
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Template 3: Async Data Fetching (Advanced)
|
|
203
|
+
|
|
204
|
+
For scenes that need to fetch external data.
|
|
205
|
+
|
|
206
|
+
```ruby
|
|
207
|
+
bot.scene :github_analyzer do
|
|
208
|
+
step :ask_repo do |ctx|
|
|
209
|
+
ctx.reply "Enter a GitHub repo URL (e.g., https://github.com/user/repo):"
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
step :fetch_data do |ctx|
|
|
213
|
+
url = ctx.message.text
|
|
214
|
+
|
|
215
|
+
# Show loading
|
|
216
|
+
ctx.reply "⏳ Fetching repo data..."
|
|
217
|
+
|
|
218
|
+
# Async HTTP request
|
|
219
|
+
Async do
|
|
220
|
+
begin
|
|
221
|
+
# Fetch from GitHub API using httpx
|
|
222
|
+
data = await fetch_github_data(url)
|
|
223
|
+
|
|
224
|
+
# Show results
|
|
225
|
+
info = <<~INFO
|
|
226
|
+
📊 **Repo Analysis:**
|
|
227
|
+
|
|
228
|
+
Name: #{data[:name]}
|
|
229
|
+
Stars: #{data[:stars]}
|
|
230
|
+
Language: #{data[:language]}
|
|
231
|
+
Description: #{data[:description]}
|
|
232
|
+
|
|
233
|
+
Last updated: #{data[:updated_at]}
|
|
234
|
+
INFO
|
|
235
|
+
|
|
236
|
+
ctx.reply info, parse_mode: 'Markdown'
|
|
237
|
+
|
|
238
|
+
rescue => e
|
|
239
|
+
ctx.reply "❌ Error: #{e.message}"
|
|
240
|
+
ensure
|
|
241
|
+
ctx.leave_scene
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Usage: /analyze
|
|
248
|
+
bot.command('analyze') { |ctx| ctx.enter_scene(:github_analyzer) }
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Template 4: Multi-Step With Validation (Production Ready)
|
|
252
|
+
|
|
253
|
+
Includes proper error handling and validation.
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
bot.scene :user_registration, timeout: 300 do # 5 minute timeout
|
|
257
|
+
on_enter do |ctx|
|
|
258
|
+
ctx.session[:registration] = {
|
|
259
|
+
started_at: Time.now,
|
|
260
|
+
attempts: 0,
|
|
261
|
+
data: {}
|
|
262
|
+
}
|
|
263
|
+
ctx.reply "👤 Registration Process\n\nLet's get started!"
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
step :ask_email do |ctx|
|
|
267
|
+
ctx.reply "Enter your email address:"
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
step :validate_email do |ctx|
|
|
271
|
+
email = ctx.message.text.strip
|
|
272
|
+
|
|
273
|
+
unless email.include?('@') && email.include?('.')
|
|
274
|
+
ctx.session[:registration][:attempts] += 1
|
|
275
|
+
|
|
276
|
+
if ctx.session[:registration][:attempts] >= 3
|
|
277
|
+
ctx.reply "❌ Too many attempts. Registration cancelled."
|
|
278
|
+
ctx.leave_scene
|
|
279
|
+
return
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
ctx.reply "❌ Invalid email format. Please try again:"
|
|
283
|
+
return # Stay on this step
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Email is valid
|
|
287
|
+
ctx.session[:registration][:data][:email] = email
|
|
288
|
+
ctx.session[:registration][:attempts] = 0
|
|
289
|
+
ctx.reply "✅ Email accepted!"
|
|
290
|
+
ctx.reply "Enter your full name:"
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
step :ask_password do |ctx|
|
|
294
|
+
ctx.session[:registration][:data][:name] = ctx.message.text
|
|
295
|
+
ctx.reply "Create a password (min 8 characters):"
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
step :confirm_registration do |ctx|
|
|
299
|
+
password = ctx.message.text
|
|
300
|
+
|
|
301
|
+
if password.length < 8
|
|
302
|
+
ctx.reply "❌ Password too short. Min 8 characters:"
|
|
303
|
+
return
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
ctx.session[:registration][:data][:password] = password
|
|
307
|
+
|
|
308
|
+
# Show summary
|
|
309
|
+
summary = <<~SUMMARY
|
|
310
|
+
📋 **Registration Summary:**
|
|
311
|
+
|
|
312
|
+
Email: #{ctx.session[:registration][:data][:email]}
|
|
313
|
+
Name: #{ctx.session[:registration][:data][:name]}
|
|
314
|
+
|
|
315
|
+
Confirm registration? (yes/no)
|
|
316
|
+
SUMMARY
|
|
317
|
+
|
|
318
|
+
ctx.reply summary, parse_mode: 'Markdown'
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
step :finalize do |ctx|
|
|
322
|
+
if ctx.message.text&.downcase == 'yes'
|
|
323
|
+
# Save to database
|
|
324
|
+
user_id = create_user(ctx.session[:registration][:data])
|
|
325
|
+
ctx.reply "✅ Registration complete!\nYour ID: #{user_id}"
|
|
326
|
+
else
|
|
327
|
+
ctx.reply "❌ Registration cancelled."
|
|
328
|
+
end
|
|
329
|
+
ctx.leave_scene
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
on_timeout do |ctx|
|
|
333
|
+
ctx.reply "⏰ Registration timed out. Please start over with /register."
|
|
334
|
+
ctx.session.delete(:registration)
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
on_leave do |ctx|
|
|
338
|
+
# Cleanup
|
|
339
|
+
ctx.session.delete(:registration)
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Usage: /register
|
|
344
|
+
bot.command('register') { |ctx| ctx.enter_scene(:user_registration) }
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
🚀 Common Patterns Cheat Sheet
|
|
348
|
+
|
|
349
|
+
Pattern 1: Basic Form Flow
|
|
350
|
+
|
|
351
|
+
```ruby
|
|
352
|
+
bot.scene :simple_form
|
|
353
|
+
bot.command('start_form') { |ctx| ctx.enter_scene(:simple_form) }
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Pattern 2: Inline Button Navigation
|
|
357
|
+
|
|
358
|
+
```ruby
|
|
359
|
+
bot.scene :menu_driven
|
|
360
|
+
# Uses callback buttons for choices
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
Pattern 3: Async Operations
|
|
364
|
+
|
|
365
|
+
```ruby
|
|
366
|
+
bot.scene :async_task
|
|
367
|
+
# Uses Async { } for HTTP/DB operations
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Pattern 4: Validated Input
|
|
371
|
+
|
|
372
|
+
```ruby
|
|
373
|
+
bot.scene :validated_input
|
|
374
|
+
# Uses return to stay on step when validation fails
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
🔧 Debugging Scenes: Quick Fixes
|
|
378
|
+
|
|
379
|
+
❌ "Scene doesn't start!"
|
|
380
|
+
|
|
381
|
+
Check: Did you call ctx.enter_scene?
|
|
382
|
+
|
|
383
|
+
```ruby
|
|
384
|
+
bot.command('start') do |ctx|
|
|
385
|
+
ctx.enter_scene(:your_scene) # ← MUST BE PRESENT
|
|
386
|
+
end
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
❌ "Steps skip unexpectedly!"
|
|
390
|
+
|
|
391
|
+
Cause: Missing return on validation failure
|
|
392
|
+
Fix:
|
|
393
|
+
|
|
394
|
+
```ruby
|
|
395
|
+
step :collect_age do |ctx|
|
|
396
|
+
age = ctx.message.text.to_i
|
|
397
|
+
if age < 18
|
|
398
|
+
ctx.reply "Must be 18+. Try again:"
|
|
399
|
+
return # ← THIS keeps you on the same step
|
|
400
|
+
end
|
|
401
|
+
# Continues only if age >= 18
|
|
402
|
+
end
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
❌ "Data lost between steps!"
|
|
406
|
+
|
|
407
|
+
Solution: Use ctx.session
|
|
408
|
+
|
|
409
|
+
```ruby
|
|
410
|
+
step :one do |ctx|
|
|
411
|
+
ctx.session[:temp] = "saved data" # ← Save
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
step :two do |ctx|
|
|
415
|
+
data = ctx.session[:temp] # ← Retrieve
|
|
416
|
+
end
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
📝 Remember This Mental Model
|
|
420
|
+
|
|
421
|
+
1. Define once (bot.scene) - Teach the bot a conversation pattern
|
|
422
|
+
2. Start many times (ctx.enter_scene) - Begin that conversation with users
|
|
423
|
+
3. Steps auto-advance - After user responds, Telegem moves to next step
|
|
424
|
+
4. Use ctx.session - Store data between steps
|
|
425
|
+
5. Clean up in on_leave - Remove session data when done
|
|
426
|
+
|
|
427
|
+
🎯 Your Homework (Test Understanding)
|
|
428
|
+
|
|
429
|
+
1. Create a pizza ordering scene with size and toppings
|
|
430
|
+
2. Add validation (size must be S/M/L, max 3 toppings)
|
|
431
|
+
3. Add a timeout of 10 minutes
|
|
432
|
+
4. Test it with /order_pizza
|
|
433
|
+
|
|
434
|
+
You now have everything you need to build robust scenes. The key is practice—start simple, then add complexity one piece at a time.
|
data/lib/core/bot.rb
CHANGED
data/lib/telegem.rb
CHANGED
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: 1.0.
|
|
4
|
+
version: 1.0.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Your Name
|
|
@@ -87,6 +87,8 @@ files:
|
|
|
87
87
|
- docs/Cookbook.md
|
|
88
88
|
- docs/How_to_use.md
|
|
89
89
|
- docs/QuickStart.md
|
|
90
|
+
- docs/UNDERSTANDING-WEBHOOK-n-POLLING.md
|
|
91
|
+
- docs/Understanding_Scene.md
|
|
90
92
|
- docs/Usage.md
|
|
91
93
|
- lib/api/client.rb
|
|
92
94
|
- lib/api/types.rb
|