telegem 1.0.4 → 1.0.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 30803f9ebb55570bae838e9db45dd199763fe54878341d5fed9c32cc204b169c
4
- data.tar.gz: 1e87a27402e8b205d2fefc1fb1ac85aa161a7138166391d5d7cd9804188215d7
3
+ metadata.gz: 3777c7718d621ee9b2905ea456a9255c16cee07d1dbda297521602786088d046
4
+ data.tar.gz: ba5e53faa9da8b44218d709899383d6f932366aa6b9444405489e21062a60b4c
5
5
  SHA512:
6
- metadata.gz: 57ddfbe1edd755e30be1e854fda4c390ea25b3976655de59f0b5e6befa5e2ace3ed135aa463a7b897a179f12f40c10d644e877baf8f4966a7deac99de7ec6d85
7
- data.tar.gz: d3aaab61e1faddc352f8702f7c85c5d7db5ef28a72c2ce70c73b08202b7245429deb9ed2b167f9bfb467172fcd69215ea3ede5907c84b090837f8cad13339536
6
+ metadata.gz: 909c70af6d30e6e780dabb647e320b4986eb518987dcf9740c4194c8941df317afabc0cc5a4ce5ec8be531c5febdfc4e7659c800cbdaf0744e59475d9a4bd0b8
7
+ data.tar.gz: 191ccb89c87d3e8b2584324de82836571f10443f32b79ba3bdce98af496de11e32600b3f4cd31d844dc0f19903bf6f3ab6cf42a5fcb9df47151a5c072e7d127f
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
- · ⚡ True 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
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
- Feature Telegem telegram-bot-ruby Others
91
- Async Architecture True async/await ❌ Thread-based ❌ Blocking
92
- Middleware System ✅ Express.js-style ❌ Limited ❌ None
93
- Scene Management Built-in ❌ Manual ❌ None
94
- Session Stores Multiple Basic ❌ None
95
- Modern DSL Clean & fluent ❌ Verbose ⚠️ Varies
96
- Performance Non-blocking ⚠️ OK ❌ Poor
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
- Async do
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 Core - Non-blocking I/O
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,367 @@
1
+ Webhook Setup Guide for Telegem
2
+
3
+ 📖 Overview
4
+
5
+ This guide covers how to set up and run your Telegram bot using webhooks with Telegem. Webhooks are the preferred method for production bots as they provide faster response times and better reliability compared to polling.
6
+
7
+ 🚀 Quick Start (For Beginners)
8
+
9
+ Basic Webhook Setup
10
+
11
+ Here's the simplest way to get your bot running with webhooks:
12
+
13
+ ```ruby
14
+ # 1. Create your bot
15
+ require 'telegem'
16
+ bot = Telegem::Core::Bot.new('YOUR_BOT_TOKEN')
17
+
18
+ # 2. Add your handlers
19
+ bot.command('start') do |ctx|
20
+ ctx.reply("Hello! I'm your bot.")
21
+ end
22
+
23
+ # 3. Start the webhook server
24
+ server = bot.webhook
25
+ server.run
26
+ ```
27
+
28
+ That's it! Your bot is now:
29
+
30
+ · ✅ Listening for webhook requests
31
+ · ✅ Using the default port (3000)
32
+ · ✅ Available at /webhook/YOUR_BOT_TOKEN
33
+
34
+ Deploying to Render
35
+
36
+ For deploying to Render.com, here's your complete setup:
37
+
38
+ 1. bot.rb or main.rb:
39
+
40
+ ```ruby
41
+ require 'telegem'
42
+
43
+ # Initialize bot
44
+ bot = Telegem::Core::Bot.new(ENV['BOT_TOKEN'])
45
+
46
+ # Add your handlers
47
+ bot.command('start') { |ctx| ctx.reply("Hello!") }
48
+
49
+ # Start webhook server
50
+ server = bot.webhook
51
+ server.run
52
+ ```
53
+
54
+ 2. Gemfile:
55
+
56
+ ```ruby
57
+ source 'https://rubygems.org'
58
+ gem 'telegem'
59
+ ```
60
+
61
+ 3. Render.com Settings:
62
+
63
+ · Start Command: bundle exec ruby bot.rb
64
+ · Environment Variables:
65
+ · BOT_TOKEN: Your Telegram bot token
66
+ · PORT: Automatically set by Render
67
+
68
+ ⚙️ Configuration Options
69
+
70
+ Using a Configuration Block
71
+
72
+ Want to customize your webhook server? Use the block syntax:
73
+
74
+ ```ruby
75
+ server = bot.webhook do |config|
76
+ config.port = 8080 # Custom port
77
+ config.host = '0.0.0.0' # Bind to all interfaces
78
+ config.logger = MyCustomLogger.new # Custom logger
79
+ # No need to set endpoint - it's automatic!
80
+ end
81
+
82
+ server.run
83
+ ```
84
+
85
+ Environment Variables
86
+
87
+ The server automatically reads these environment variables:
88
+
89
+ Variable Purpose Default
90
+ WEBHOOK_URL Full webhook URL for Telegram http://host:port/webhook/token
91
+ PORT Server port 3000
92
+ HOST Server host '0.0.0.0'
93
+
94
+ 🔧 Advanced Configuration
95
+
96
+ Custom Webhook Path
97
+
98
+ Need a different webhook path? Here's how:
99
+
100
+ ```ruby
101
+ # Method 1: Set WEBHOOK_URL environment variable
102
+ ENV['WEBHOOK_URL'] = 'https://your-domain.com/custom/path'
103
+
104
+ # Method 2: Configure Telegram manually
105
+ bot.set_webhook(
106
+ url: 'https://your-domain.com/custom/path',
107
+ max_connections: 40,
108
+ allowed_updates: ['message', 'callback_query']
109
+ )
110
+ ```
111
+
112
+ SSL/HTTPS Setup
113
+
114
+ For production with SSL (required by Telegram for webhooks):
115
+
116
+ ```ruby
117
+ server = bot.webhook do |config|
118
+ config.port = 443
119
+ # SSL certificates (if running standalone)
120
+ # config.ssl_certificate = '/path/to/cert.pem'
121
+ # config.ssl_key = '/path/to/key.pem'
122
+ end
123
+
124
+ # Or use a reverse proxy (recommended):
125
+ # - Nginx
126
+ # - Cloudflare
127
+ # - Render's built-in HTTPS
128
+ ```
129
+
130
+ Integration with Web Frameworks
131
+
132
+ Integrate Telegem into your existing Rails or Sinatra app:
133
+
134
+ ```ruby
135
+ # For Rails: config/application.rb or config.ru
136
+ require 'telegem'
137
+
138
+ bot = Telegem::Core::Bot.new(ENV['BOT_TOKEN'])
139
+
140
+ # Mount as middleware in config.ru:
141
+ # use Telegem::Webhook::Middleware, bot
142
+
143
+ # For Sinatra:
144
+ require 'sinatra'
145
+ require 'telegem'
146
+
147
+ bot = Telegem::Core::Bot.new(ENV['BOT_TOKEN'])
148
+ use Telegem::Webhook::Middleware, bot
149
+
150
+ get '/' do
151
+ "Main app is running!"
152
+ end
153
+ ```
154
+
155
+ 🚨 Troubleshooting
156
+
157
+ Common Issues & Solutions
158
+
159
+ 1. "Undefined method `run'" Error
160
+
161
+ ```ruby
162
+ # ❌ WRONG - Returns Middleware, not Server
163
+ server = bot.webhook(app: something)
164
+ server.run # ERROR!
165
+
166
+ # ✅ CORRECT - Returns Server
167
+ server = bot.webhook # No arguments
168
+ server.run # WORKS!
169
+ ```
170
+
171
+ 2. Webhook Not Receiving Updates
172
+
173
+ ```ruby
174
+ # Check if webhook is set
175
+ info = bot.get_webhook_info
176
+ puts "Webhook URL: #{info.url}"
177
+ puts "Has pending updates: #{info.pending_update_count}"
178
+
179
+ # Set webhook if needed
180
+ bot.set_webhook(url: 'https://your-domain.com/webhook/' + bot.token)
181
+ ```
182
+
183
+ 3. Port Already in Use
184
+
185
+ ```ruby
186
+ # Use a different port
187
+ server = bot.webhook do |config|
188
+ config.port = ENV['PORT'] || 3000 # Use Render's PORT
189
+ end
190
+ ```
191
+
192
+ 4. Slow Response to Telegram
193
+
194
+ ```ruby
195
+ # The server responds immediately (200 OK)
196
+ # then processes updates in background threads
197
+ # This is NORMAL and by design!
198
+ ```
199
+
200
+ Debug Mode
201
+
202
+ Enable detailed logging for troubleshooting:
203
+
204
+ ```ruby
205
+ require 'logger'
206
+
207
+ logger = Logger.new(STDOUT)
208
+ logger.level = Logger::DEBUG
209
+
210
+ bot = Telegem::Core::Bot.new('TOKEN', logger: logger)
211
+ server = bot.webhook do |config|
212
+ config.logger = logger
213
+ end
214
+ ```
215
+
216
+ 📊 Production Best Practices
217
+
218
+ 1. Health Checks
219
+
220
+ The webhook server includes a built-in health endpoint:
221
+
222
+ ```
223
+ GET /health
224
+ ```
225
+
226
+ Returns 200 OK - use this for monitoring.
227
+
228
+ 2. Error Handling
229
+
230
+ ```ruby
231
+ bot.error do |error, ctx|
232
+ # Log to your error tracking service
233
+ Sentry.capture_exception(error)
234
+
235
+ # Optional: notify admin
236
+ bot.api.send_message(
237
+ chat_id: ADMIN_ID,
238
+ text: "Bot error: #{error.message}"
239
+ )
240
+ end
241
+ ```
242
+
243
+ 3. Rate Limiting
244
+
245
+ Consider adding rate limiting middleware:
246
+
247
+ ```ruby
248
+ class RateLimiter
249
+ def initialize(bot, requests_per_minute: 60)
250
+ @bot = bot
251
+ @limits = {}
252
+ end
253
+
254
+ def call(ctx)
255
+ user_id = ctx.from.id
256
+ # Implement your rate limiting logic
257
+ # ...
258
+ yield # Continue to next middleware/handler
259
+ end
260
+ end
261
+
262
+ bot.use RateLimiter
263
+ ```
264
+
265
+ 4. Monitoring
266
+
267
+ Monitor these key metrics:
268
+
269
+ · Response time to Telegram (< 1 second)
270
+ · Error rate (< 1%)
271
+ · Queue length (pending updates)
272
+
273
+ 🔄 Webhook vs Polling
274
+
275
+ Aspect Webhook Polling
276
+ Performance Faster (real-time) Slower (up to 30s delay)
277
+ Resource Use Efficient Constant requests
278
+ Setup More complex Simple
279
+ Production ✅ Recommended Only for dev/testing
280
+ Render.com Web Service Background Worker
281
+
282
+ ```ruby
283
+ # Switching between methods is easy:
284
+ if ENV['RACK_ENV'] == 'production'
285
+ # Use webhooks in production
286
+ bot.webhook.run
287
+ else
288
+ # Use polling in development
289
+ bot.start_polling
290
+ sleep # Keep process alive
291
+ end
292
+ ```
293
+
294
+ 🎯 Example: Complete Production Setup
295
+
296
+ ```ruby
297
+ # production_bot.rb
298
+ require 'telegem'
299
+ require 'logger'
300
+
301
+ # Configure bot
302
+ bot = Telegem::Core::Bot.new(
303
+ ENV['BOT_TOKEN'],
304
+ logger: Logger.new('logs/bot.log', 'daily')
305
+ )
306
+
307
+ # Add middleware
308
+ bot.use MyAuthMiddleware
309
+ bot.use RateLimiter
310
+
311
+ # Add handlers
312
+ bot.command('start') { |ctx| ctx.reply("Welcome!") }
313
+ # ... more handlers
314
+
315
+ # Error handling
316
+ bot.error do |error, ctx|
317
+ logger.error("Error: #{error.message}")
318
+ ctx&.reply("Sorry, something went wrong!")
319
+ end
320
+
321
+ # Get Render's port or use default
322
+ port = ENV['PORT'] || 3000
323
+
324
+ # Start server
325
+ server = bot.webhook do |config|
326
+ config.port = port
327
+ config.host = '0.0.0.0'
328
+ end
329
+
330
+ puts "🚀 Starting bot on port #{port}"
331
+ puts "📡 Webhook URL: #{server.webhook_url}"
332
+ server.run
333
+ ```
334
+
335
+ ❓ FAQ
336
+
337
+ Q: What port should I use?
338
+ A: Use ENV['PORT'] on Render/Heroku, or 3000 for local development.
339
+
340
+ Q: Do I need to call set_webhook?
341
+ A: Not with Telegem! It sets the webhook automatically when you call server.webhook_url.
342
+
343
+ Q: Can I run multiple bots?
344
+ A: Yes! Each needs its own server instance on a different port.
345
+
346
+ Q: How do I handle bot restarts?
347
+ A: The server gracefully shuts down on SIGTERM. Set restart: always in your deployment config.
348
+
349
+ Q: My bot stops after some time?
350
+ A: On Render's free tier, services sleep after inactivity. Upgrade to a paid plan for 24/7 uptime.
351
+
352
+ 📚 Additional Resources
353
+
354
+ · Telegram Bot API Documentation
355
+ · Render.com Deployment Guide
356
+ · Telegem GitHub Repository
357
+ · Webhook vs Polling Explained
358
+
359
+ ---
360
+
361
+ Need Help?
362
+
363
+ · Check the server logs: tail -f logs/bot.log
364
+ · Verify webhook URL with bot.get_webhook_info
365
+ · Test locally with ngrok: ngrok http 3000
366
+
367
+ Happy bot building! 🤖
@@ -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
@@ -79,15 +79,15 @@ module Telegem
79
79
  self
80
80
  end
81
81
 
82
- def webhook(app = nil, &block)
83
- require 'telegem/webhook/server'
82
+ def webhook(app = nil, port: nil, host: '0.0.0.0', logger: nil, &block)
83
+ require 'webhook/server'
84
84
 
85
85
  if block_given?
86
86
  Webhook::Server.new(self, &block)
87
87
  elsif app
88
88
  Webhook::Middleware.new(self, app)
89
89
  else
90
- Webhook::Server.new(self)
90
+ Webhook::Server.new(self, port: port, host: host, logger: logger)
91
91
  end
92
92
  end
93
93
 
data/lib/telegem.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  require 'logger'
3
3
  require 'json'
4
4
  module Telegem
5
- VERSION = "1.0.4".freeze
5
+ VERSION = "1.0.6".freeze
6
6
  end
7
7
 
8
8
  # Load core components
@@ -7,9 +7,9 @@ module Telegem
7
7
  class Server
8
8
  attr_reader :bot, :port, :host, :logger, :server, :running
9
9
 
10
- def initialize(bot, port: 3000, host: '0.0.0.0', logger: nil)
10
+ def initialize(bot, port: nil, host: '0.0.0.0', logger: nil)
11
11
  @bot = bot
12
- @port = port
12
+ @port = port || ENV['PORT'] || 1000
13
13
  @host = host
14
14
  @logger = logger || Logger.new($stdout)
15
15
  @server = nil
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
4
+ version: 1.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Your Name
@@ -87,6 +87,9 @@ files:
87
87
  - docs/Cookbook.md
88
88
  - docs/How_to_use.md
89
89
  - docs/QuickStart.md
90
+ - docs/SETTING_WEBHOOK.md
91
+ - docs/UNDERSTANDING-WEBHOOK-n-POLLING.md
92
+ - docs/Understanding_Scene.md
90
93
  - docs/Usage.md
91
94
  - lib/api/client.rb
92
95
  - lib/api/types.rb