telegem 2.1.0 → 3.0.0

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.
data/docs-src/bot.md CHANGED
@@ -1,295 +1,464 @@
1
+ 🧠 What is bot?
1
2
 
2
- # Bot API Reference
3
+ If ctx is your hands (doing things), bot is your brain (deciding what to do).
3
4
 
4
- The `Telegem::Core::Bot` class is the main controller for your Telegram bot.
5
+ ```ruby
6
+ bot = Telegem.new("YOUR_TOKEN") # Born!
5
7
 
6
- ## Instantiation
8
+ bot.command('start') do |ctx| # Learns to respond to /start
9
+ ctx.reply("Hello!") # Says hello
10
+ end
7
11
 
8
- ```ruby
9
- bot = Telegem.new(token, **options)
10
- # or
11
- bot = Telegem::Core::Bot.new(token, **options)
12
+ bot.start_polling # Starts listening!
12
13
  ```
13
14
 
14
- Bot Control Methods
15
+ 🎯 The 5 Ways Your Bot Listens
15
16
 
16
- start_polling(**options)
17
+ 1. bot.command() - For Slash Commands
17
18
 
18
- Starts the bot in long-polling mode (for development/local use).
19
+ When users type /something:
19
20
 
20
21
  ```ruby
21
- bot.start_polling(
22
- timeout: 30, # Seconds to wait for updates (default: 30)
23
- limit: 100, # Max updates per request (default: 100)
24
- allowed_updates: ['message', 'callback_query'] # Filter updates
25
- )
22
+ # Basic command
23
+ bot.command('start') do |ctx|
24
+ ctx.reply("Welcome! 👋")
25
+ end
26
+
27
+ # With arguments
28
+ bot.command('search') do |ctx|
29
+ query = ctx.command_args # What comes after /search
30
+ if query.empty?
31
+ ctx.reply("Usage: /search <something>")
32
+ else
33
+ ctx.reply("Searching for '#{query}'...")
34
+ end
35
+ end
36
+
37
+ # Multiple commands, same handler
38
+ ['help', 'info', 'about'].each do |cmd|
39
+ bot.command(cmd) do |ctx|
40
+ ctx.reply("I'm a helpful bot! 🤖")
41
+ end
42
+ end
26
43
  ```
27
44
 
28
- shutdown()
45
+ What users see:
46
+
47
+ ```
48
+ User: /start
49
+ Bot: Welcome! 👋
29
50
 
30
- Gracefully stops the bot.
51
+ User: /search cats
52
+ Bot: Searching for 'cats'...
31
53
 
32
- ```ruby
33
- bot.shutdown # Stops polling, closes connections, terminates workers
54
+ User: /search
55
+ Bot: Usage: /search <something>
34
56
  ```
35
57
 
36
- running?()
58
+ 2. bot.hears() - For Text Patterns
37
59
 
38
- Checks if the bot is currently active.
60
+ When users say specific words or patterns:
39
61
 
40
62
  ```ruby
41
- if bot.running?
42
- puts "Bot is online"
43
- else
44
- puts "Bot is offline"
63
+ # Exact match
64
+ bot.hears('hello') do |ctx|
65
+ ctx.reply("Hello there! 😊")
66
+ end
67
+
68
+ # Case-insensitive
69
+ bot.hears(/hello|hi|hey/i) do |ctx|
70
+ ctx.reply("Greetings! 🖐️")
71
+ end
72
+
73
+ # Regex with capture groups
74
+ bot.hears(/my name is (\w+)/i) do |ctx|
75
+ name = ctx.match[1] # Captured part
76
+ ctx.reply("Nice to meet you, #{name}! 👋")
77
+ end
78
+
79
+ # Pattern with wildcard
80
+ bot.hears(/I love (\w+)/) do |ctx|
81
+ thing = ctx.match[1]
82
+ ctx.reply("I love #{thing} too! ❤️")
45
83
  end
46
84
  ```
47
85
 
48
- Handler Registration Methods
86
+ What users see:
49
87
 
50
- command(name, **options, &block)
88
+ ```
89
+ User: hello
90
+ Bot: Hello there! 😊
51
91
 
52
- Registers a command handler.
92
+ User: My name is John
93
+ Bot: Nice to meet you, John! 👋
94
+
95
+ User: I love pizza
96
+ Bot: I love pizza too! ❤️
97
+ ```
98
+
99
+ 3. bot.on() - For Everything Else
100
+
101
+ The Swiss Army knife handler:
53
102
 
54
103
  ```ruby
55
- bot.command('start') do |ctx|
56
- ctx.reply "Welcome! Use /help for commands."
104
+ # Handle ALL messages
105
+ bot.on(:message) do |ctx|
106
+ ctx.reply("I got a message!")
57
107
  end
58
108
 
59
- # With arguments
60
- bot.command('greet') do |ctx|
61
- name = ctx.command_args || 'friend'
62
- ctx.reply "Hello, #{name}!"
109
+ # Handle only photos
110
+ bot.on(:message, photo: true) do |ctx|
111
+ ctx.reply("Nice photo! 📸")
63
112
  end
64
- ```
65
113
 
66
- hears(pattern, **options, &block)
114
+ # Handle only videos
115
+ bot.on(:message, video: true) do |ctx|
116
+ ctx.reply("Cool video! 🎥")
117
+ end
67
118
 
68
- Registers a text pattern handler.
119
+ # Handle only documents
120
+ bot.on(:message, document: true) do |ctx|
121
+ ctx.reply("Thanks for the document! 📄")
122
+ end
69
123
 
70
- ```ruby
71
- # Regex pattern
72
- bot.hears(/hello|hi|hey/i) do |ctx|
73
- ctx.reply "Hello there! 👋"
124
+ # Handle only locations
125
+ bot.on(:message, location: true) do |ctx|
126
+ ctx.reply("Thanks for sharing location! 📍")
74
127
  end
75
128
 
76
- # Exact match
77
- bot.hears('ping') do |ctx|
78
- ctx.reply 'pong 🏓'
129
+ # Filter by chat type
130
+ bot.on(:message, chat_type: 'private') do |ctx|
131
+ ctx.reply("Private chat message!")
132
+ end
133
+
134
+ bot.on(:message, chat_type: 'group') do |ctx|
135
+ ctx.reply("Group chat message!")
79
136
  end
80
137
  ```
81
138
 
82
- on(type, filters = {}, &block)
139
+ 4. bot.on(:callback_query) - For Button Clicks
83
140
 
84
- Generic update handler.
141
+ When users click inline buttons:
85
142
 
86
143
  ```ruby
87
- # Message types
88
- bot.on(:message, photo: true) do |ctx|
89
- ctx.reply "Nice photo! 📸"
144
+ # Handle ALL button clicks
145
+ bot.on(:callback_query) do |ctx|
146
+ ctx.answer_callback_query(text: "Button clicked!")
147
+ ctx.reply("You clicked: #{ctx.data}")
90
148
  end
91
149
 
92
- bot.on(:message, location: true) do |ctx|
93
- ctx.reply "Thanks for sharing your location! 📍"
150
+ # Filter by button data
151
+ bot.on(:callback_query, data: 'yes') do |ctx|
152
+ ctx.answer_callback_query(text: "You said YES! ✅")
153
+ ctx.edit_message_text("Confirmed! ✅")
94
154
  end
95
155
 
96
- # Callback queries (inline button clicks)
97
- bot.on(:callback_query) do |ctx|
98
- ctx.answer_callback_query(text: "You clicked #{ctx.data}")
156
+ bot.on(:callback_query, data: 'no') do |ctx|
157
+ ctx.answer_callback_query(text: "You said NO! ❌")
158
+ ctx.edit_message_text("Cancelled! ")
99
159
  end
100
160
 
101
- # Filter by chat type
102
- bot.on(:message, chat_type: 'private') do |ctx|
103
- ctx.reply "This is a private chat"
161
+ # Pattern matching button data
162
+ bot.on(:callback_query) do |ctx|
163
+ if ctx.data.start_with?('vote_')
164
+ option = ctx.data.split('_').last
165
+ ctx.answer_callback_query(text: "Voted for #{option}!")
166
+ ctx.edit_message_text("You voted: #{option} ✅")
167
+ end
104
168
  end
105
169
  ```
106
170
 
107
- Middleware System
171
+ 5. bot.on(:inline_query) - For @bot Searches
172
+
173
+ When users type @yourbot something:
174
+
175
+ ```ruby
176
+ bot.on(:inline_query) do |ctx|
177
+ query = ctx.query # What they typed after @yourbot
178
+
179
+ results = [
180
+ {
181
+ type: "article",
182
+ id: "1",
183
+ title: "Result for: #{query}",
184
+ input_message_content: {
185
+ message_text: "You searched: #{query}"
186
+ }
187
+ }
188
+ ]
189
+
190
+ ctx.answer_inline_query(results)
191
+ end
192
+ ```
108
193
 
109
- use(middleware, *args, &block)
194
+ 🎭 Real Bot Examples
110
195
 
111
- Adds middleware to the processing chain.
196
+ Example 1: Echo Bot (Beginners)
112
197
 
113
198
  ```ruby
114
- # Built-in session middleware (auto-added)
115
- bot.use(Telegem::Session::Middleware)
199
+ bot = Telegem.new("TOKEN")
116
200
 
117
- # Custom middleware
118
- class LoggerMiddleware
119
- def call(ctx, next_middleware)
120
- puts "Processing update from #{ctx.from.id}"
121
- next_middleware.call(ctx)
201
+ # Respond to /start
202
+ bot.command('start') do |ctx|
203
+ ctx.reply("I echo everything you say! 🔊")
204
+ end
205
+
206
+ # Echo all messages
207
+ bot.on(:message) do |ctx|
208
+ if ctx.message.text
209
+ ctx.reply("You said: #{ctx.message.text}")
122
210
  end
123
211
  end
124
212
 
125
- bot.use(LoggerMiddleware)
213
+ bot.start_polling
126
214
  ```
127
215
 
128
- Scene System
216
+ Example 2: Quiz Bot (Intermediate)
129
217
 
130
- scene(id, &block)
218
+ ```ruby
219
+ bot = Telegem.new("TOKEN")
131
220
 
132
- Defines a scene (multi-step conversation).
221
+ QUESTIONS = [
222
+ { q: "2 + 2?", a: "4", options: ["3", "4", "5"] },
223
+ { q: "Capital of France?", a: "Paris", options: ["London", "Paris", "Berlin"] }
224
+ ]
133
225
 
134
- ```ruby
135
- bot.scene(:survey) do |scene|
136
- scene.step(:start) do |ctx|
137
- ctx.reply "What's your name?"
138
- ctx.session[:step] = :name
139
- end
140
-
141
- scene.step(:name) do |ctx|
142
- name = ctx.message.text
143
- ctx.reply "Hello #{name}! How old are you?"
144
- ctx.session[:name] = name
145
- ctx.session[:step] = :age
226
+ bot.command('quiz') do |ctx|
227
+ question = QUESTIONS.sample
228
+
229
+ inline = Telegem.inline do
230
+ question[:options].each do |option|
231
+ row button option, callback_data: "answer_#{option}"
232
+ end
146
233
  end
234
+
235
+ ctx.reply(question[:q], reply_markup: inline)
236
+ ctx.session[:correct] = question[:a]
147
237
  end
148
238
 
149
- # Enter scene from command
150
- bot.command('survey') do |ctx|
151
- ctx.enter_scene(:survey)
239
+ bot.on(:callback_query) do |ctx|
240
+ answer = ctx.data.split('_').last
241
+ correct = ctx.session[:correct]
242
+
243
+ if answer == correct
244
+ ctx.answer_callback_query(text: "✅ Correct!")
245
+ ctx.edit_message_text("🎉 Correct! The answer is #{correct}")
246
+ else
247
+ ctx.answer_callback_query(text: "❌ Wrong! It's #{correct}")
248
+ end
152
249
  end
153
250
  ```
154
251
 
155
- Webhook Methods
252
+ Example 3: Support Bot (Advanced)
156
253
 
157
- webhook(**options)
254
+ ```ruby
255
+ bot = Telegem.new("TOKEN")
158
256
 
159
- Returns a webhook server instance.
257
+ # Main menu
258
+ bot.command('start') do |ctx|
259
+ keyboard = Telegem.keyboard do
260
+ row "Report Bug", "Request Feature"
261
+ row "Ask Question", "Contact Human"
262
+ end
160
263
 
161
- ```ruby
162
- # Quick setup (auto-detects cloud platform)
163
- server = bot.webhook.run
164
-
165
- # Manual configuration
166
- server = bot.webhook(
167
- port: 3000,
168
- host: '0.0.0.0',
169
- secret_token: ENV['SECRET_TOKEN'],
170
- logger: Logger.new('webhook.log')
171
- )
172
- server.run
173
- server.set_webhook
174
- ```
264
+ ctx.reply("Support Menu:", reply_markup: keyboard)
265
+ end
175
266
 
176
- set_webhook(url, **options)
267
+ # Handle menu choices
268
+ bot.hears("Report Bug") do |ctx|
269
+ ctx.reply("Describe the bug:")
270
+ ctx.state[:collecting_bug] = true
271
+ end
177
272
 
178
- Manually sets webhook URL.
273
+ bot.hears("Ask Question") do |ctx|
274
+ ctx.reply("What's your question?")
275
+ ctx.state[:collecting_question] = true
276
+ end
179
277
 
180
- ```ruby
181
- bot.set_webhook(
182
- 'https://your-app.herokuapp.com/webhook',
183
- secret_token: 'your-secret',
184
- drop_pending_updates: true
185
- )
278
+ # Collect user input
279
+ bot.on(:message) do |ctx|
280
+ if ctx.state[:collecting_bug]
281
+ bug = ctx.message.text
282
+ # Save to database...
283
+ ctx.reply("Bug reported! ID: ##{rand(1000)}")
284
+ ctx.state.delete(:collecting_bug)
285
+
286
+ elsif ctx.state[:collecting_question]
287
+ question = ctx.message.text
288
+ # Save to database...
289
+ ctx.reply("Question logged! We'll reply soon.")
290
+ ctx.state.delete(:collecting_question)
291
+ end
292
+ end
186
293
  ```
187
294
 
188
- delete_webhook()
295
+ ⚡ Pro Tips & Patterns
189
296
 
190
- Removes webhook configuration.
297
+ Tip 1: Organize Your Handlers
191
298
 
192
299
  ```ruby
193
- bot.delete_webhook # Switch back to polling
194
- ```
300
+ # Instead of one giant file:
301
+ # handlers/commands.rb
302
+ def setup_commands(bot)
303
+ bot.command('start') { |ctx| ctx.reply("Start!") }
304
+ bot.command('help') { |ctx| ctx.reply("Help!") }
305
+ end
195
306
 
196
- get_webhook_info()
307
+ # handlers/messages.rb
308
+ def setup_messages(bot)
309
+ bot.on(:message, photo: true) { |ctx| ctx.reply("Photo!") }
310
+ bot.on(:message, video: true) { |ctx| ctx.reply("Video!") }
311
+ end
312
+
313
+ # main.rb
314
+ bot = Telegem.new("TOKEN")
315
+ setup_commands(bot)
316
+ setup_messages(bot)
317
+ ```
197
318
 
198
- Gets current webhook information.
319
+ Tip 2: Use Middleware for Common Tasks
199
320
 
200
321
  ```ruby
201
- info = bot.get_webhook_info
202
- puts "Webhook URL: #{info['url']}"
203
- puts "Has custom certificate: #{info['has_custom_certificate']}"
322
+ # Log all messages
323
+ class LoggerMiddleware
324
+ def call(ctx, next_middleware)
325
+ puts "[#{Time.now}] #{ctx.from.id}: #{ctx.message.text}"
326
+ next_middleware.call(ctx)
327
+ end
328
+ end
329
+
330
+ bot.use(LoggerMiddleware.new)
204
331
  ```
205
332
 
206
- Error Handling
333
+ Tip 3: Rate Limiting
207
334
 
208
- error(&block)
335
+ ```ruby
336
+ bot.command('spam') do |ctx|
337
+ user_id = ctx.from.id
209
338
 
210
- Sets global error handler.
339
+ # Allow only 5 uses per minute
340
+ ctx.session[:spam_count] ||= 0
341
+ ctx.session[:spam_count] += 1
211
342
 
212
- ```ruby
213
- bot.error do |error, ctx|
214
- puts "Error: #{error.message}"
215
- puts "Context: User #{ctx.from&.id}, Chat #{ctx.chat&.id}"
216
-
217
- # Send error to user
218
- ctx.reply "Something went wrong! 😅" if ctx&.chat
343
+ if ctx.session[:spam_count] > 5
344
+ ctx.reply("🚫 Too many requests! Wait a minute.")
345
+ else
346
+ ctx.reply("Spam count: #{ctx.session[:spam_count]}")
347
+ end
219
348
  end
220
349
  ```
221
350
 
222
- Update Processing
351
+ 🚀 Bot Lifecycle Management
223
352
 
224
- process(update_data)
353
+ Starting Your Bot
225
354
 
226
- Manually process an update (useful for testing).
355
+ ```ruby
356
+ # Development (polling)
357
+ bot.start_polling(
358
+ timeout: 30, # Wait 30 seconds for updates
359
+ limit: 100 # Get up to 100 updates at once
360
+ )
361
+
362
+ # Production (webhook)
363
+ server = bot.webhook(port: 3000)
364
+ server.run
365
+ server.set_webhook
366
+ ```
367
+
368
+ Stopping Gracefully
227
369
 
228
370
  ```ruby
229
- test_update = {
230
- 'update_id' => 1,
231
- 'message' => {
232
- 'message_id' => 1,
233
- 'from' => {'id' => 123, 'first_name' => 'Test'},
234
- 'chat' => {'id' => 123},
235
- 'date' => Time.now.to_i,
236
- 'text' => '/start'
237
- }
238
- }
239
-
240
- bot.process(test_update)
371
+ # In your main file
372
+ Signal.trap("INT") do
373
+ puts "\nShutting down..."
374
+ bot.shutdown
375
+ exit
376
+ end
377
+
378
+ # Or handle in code
379
+ begin
380
+ bot.start_polling
381
+ rescue Interrupt
382
+ bot.shutdown
383
+ end
241
384
  ```
242
385
 
243
- Bot Options
386
+ 🎮 Interactive Challenge
387
+
388
+ Build a Fortune Cookie Bot in 15 minutes:
244
389
 
245
- Available options when creating bot:
390
+ 1. /fortune - Gives random fortune
391
+ 2. Hears "tell me a joke" - Tells joke
392
+ 3. Hears "I'm feeling lucky" - Random emoji response
393
+ 4. Button "Get Another" - New fortune
246
394
 
247
395
  ```ruby
248
- bot = Telegem.new('TOKEN',
249
- logger: Logger.new('bot.log'), # Custom logger
250
- timeout: 60, # API timeout in seconds
251
- max_threads: 20, # Worker thread pool size
252
- worker_count: 5, # Update processing workers
253
- session_store: custom_store # Custom session storage
254
- )
396
+ # Starter code
397
+ fortunes = [
398
+ "You will find happiness with a new friend.",
399
+ "A dream you have will come true.",
400
+ "Now is the time to try something new.",
401
+ "Your hard work will pay off soon."
402
+ ]
403
+
404
+ bot.command('fortune') do |ctx|
405
+ fortune = fortunes.sample
406
+ ctx.reply("🔮 #{fortune}")
407
+
408
+ # Your turn: Add inline button "Get Another"
409
+ end
410
+
411
+ bot.hears(/tell me a joke/i) do |ctx|
412
+ # Your turn: Add a joke
413
+ end
414
+
415
+ # Your turn: Add button click handler
255
416
  ```
256
417
 
257
- Complete Example
418
+ 📋 Cheat Sheet
258
419
 
259
- ```ruby
260
- require 'telegem'
420
+ When user... Use... Example
421
+ - Types /command bot.command() bot.command('start')
422
+ - Says exact word bot.hears('word') bot.hears('hello')
423
+ - Says pattern bot.hears(/pattern/) bot.hears(/I love \w+/)
424
+ - Sends any message bot.on(:message) bot.on(:message)
425
+ - Sends photo bot.on(:message, photo: true) Handles only photos
426
+ - Clicks button bot.on(:callback_query) Handles inline buttons
427
+ - Searches @bot bot.on(:inline_query) Inline mode results
261
428
 
262
- bot = Telegem.new(ENV['BOT_TOKEN'])
429
+ 🔧 Debugging Common Issues
263
430
 
264
- # Error handling
265
- bot.error do |error, ctx|
266
- puts "Error: #{error.class}: #{error.message}"
431
+ Handler Not Firing?
432
+
433
+ ```ruby
434
+ # Add logging
435
+ bot.on(:message) do |ctx|
436
+ puts "DEBUG: Got message: #{ctx.message.text}"
437
+ # Your handler...
267
438
  end
439
+ ```
268
440
 
269
- # Commands
270
- bot.command('start') { |ctx| ctx.reply "Welcome!" }
271
- bot.command('help') { |ctx| ctx.reply "Available: /start, /help, /menu" }
441
+ Buttons Not Working?
272
442
 
273
- # Interactive menu
274
- bot.command('menu') do |ctx|
275
- keyboard = Telegem.keyboard do
276
- row "Option 1", "Option 2"
277
- row "Cancel"
278
- end
279
- ctx.reply "Choose:", reply_markup: keyboard
443
+ ```ruby
444
+ bot.on(:callback_query) do |ctx|
445
+ puts "DEBUG: Button data: #{ctx.data}"
446
+ ctx.answer_callback_query # DON'T FORGET THIS!
447
+ # Your handler...
280
448
  end
449
+ ```
281
450
 
282
- # Start based on environment
283
- if ENV['RACK_ENV'] == 'production'
284
- bot.webhook.run
285
- else
286
- bot.start_polling
287
- end
451
+ Multiple Handlers Conflict?
452
+
453
+ Handlers run in order. First matching handler wins!
454
+
455
+ ```ruby
456
+ bot.command('test') { |ctx| ctx.reply("First!") }
457
+ bot.command('test') { |ctx| ctx.reply("Second!") } # Never runs!
288
458
  ```
289
459
 
290
460
  ---
291
461
 
292
- Next: Context Methods | Keyboard API
462
+ Your bot is now ready to listen! Start with one handler, test it, add another. Like teaching a child new words, one at a time. 🧒→🤖
293
463
 
294
- ```
295
- ```
464
+ Remember: Every great bot started with just /start. Build that, then add one more feature. Then another. Consistency beats complexity!