telegem 0.2.5 → 1.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/Cookbook.md ADDED
@@ -0,0 +1,407 @@
1
+
2
+ # 🍳 Telegem Cookbook
3
+
4
+ Quick copy-paste recipes for common bot tasks.
5
+ Each recipe is standalone and ready to use!
6
+
7
+ ## 📋 Table of Contents
8
+ - [Sending Media](#-sending-media)
9
+ - [Handling Files](#-handling-files)
10
+ - [Building Forms](#-building-forms)
11
+ - [Admin Commands](#-admin-commands)
12
+ - [Database Patterns](#-database-patterns)
13
+ - [Utility Helpers](#-utility-helpers)
14
+
15
+ ---
16
+
17
+ ## 📸 Sending Media
18
+
19
+ ### Send Photo from URL
20
+ ```ruby
21
+ bot.command('cat') do |ctx|
22
+ ctx.reply "Here's a random cat! 🐱"
23
+ ctx.photo("https://cataas.com/cat")
24
+ end
25
+ ```
26
+
27
+ Send Photo from File
28
+
29
+ ```ruby
30
+ bot.command('logo') do |ctx|
31
+ File.open("logo.png", "rb") do |file|
32
+ ctx.photo(file, caption: "Our logo!")
33
+ end
34
+ end
35
+ ```
36
+
37
+ Send Multiple Photos (Album)
38
+
39
+ ```ruby
40
+ bot.command('album') do |ctx|
41
+ photos = [
42
+ "https://example.com/photo1.jpg",
43
+ "https://example.com/photo2.jpg",
44
+ InputFile.new(File.open("local.jpg"))
45
+ ]
46
+
47
+ # Send as media group
48
+ ctx.api.call('sendMediaGroup', {
49
+ chat_id: ctx.chat.id,
50
+ media: photos.map { |p| { type: 'photo', media: p } }
51
+ })
52
+ end
53
+ ```
54
+
55
+ ---
56
+
57
+ 📁 Handling Files
58
+
59
+ Receive and Save Document
60
+
61
+ ```ruby
62
+ bot.on(:message) do |ctx|
63
+ if ctx.message.document
64
+ file_id = ctx.message.document.file_id
65
+
66
+ # Get file info
67
+ file = ctx.api.call('getFile', file_id: file_id)
68
+
69
+ # Download file
70
+ file_url = "https://api.telegram.org/file/bot#{ctx.bot.token}/#{file['file_path']}"
71
+ download_and_save(file_url, "uploads/#{file_id}")
72
+
73
+ ctx.reply "✅ File saved!"
74
+ end
75
+ end
76
+ ```
77
+
78
+ File Size Check
79
+
80
+ ```ruby
81
+ MAX_SIZE = 20 * 1024 * 1024 # 20MB
82
+
83
+ bot.on(:message) do |ctx|
84
+ if ctx.message.document&.file_size.to_i > MAX_SIZE
85
+ ctx.reply "⚠️ File too large (max 20MB)"
86
+ return
87
+ end
88
+ end
89
+ ```
90
+
91
+ ---
92
+
93
+ 📝 Building Forms
94
+
95
+ Simple Contact Form
96
+
97
+ ```ruby
98
+ bot.scene :contact_form do
99
+ step :ask_name do |ctx|
100
+ ctx.reply "What's your name?"
101
+ end
102
+
103
+ step :ask_email do |ctx|
104
+ ctx.session[:name] = ctx.message.text
105
+ ctx.reply "What's your email?"
106
+ end
107
+
108
+ step :ask_message do |ctx|
109
+ ctx.session[:email] = ctx.message.text
110
+ ctx.reply "What's your message?"
111
+ end
112
+
113
+ step :submit do |ctx|
114
+ ctx.session[:message] = ctx.message.text
115
+
116
+ # Send to admin
117
+ admin_message = <<~MSG
118
+ 📨 New Contact Form:
119
+
120
+ Name: #{ctx.session[:name]}
121
+ Email: #{ctx.session[:email]}
122
+ Message: #{ctx.session[:message]}
123
+ MSG
124
+
125
+ ctx.api.call('sendMessage', {
126
+ chat_id: ADMIN_ID,
127
+ text: admin_message
128
+ })
129
+
130
+ ctx.reply "✅ Message sent! We'll reply soon."
131
+ ctx.leave_scene
132
+ end
133
+ end
134
+ ```
135
+
136
+ ---
137
+
138
+ 👑 Admin Commands
139
+
140
+ Admin Middleware
141
+
142
+ ```ruby
143
+ ADMIN_IDS = [123456, 789012].freeze
144
+
145
+ class AdminOnly
146
+ def call(ctx, next_middleware)
147
+ if ADMIN_IDS.include?(ctx.from.id)
148
+ next_middleware.call(ctx)
149
+ else
150
+ ctx.reply "⛔ Admin only"
151
+ end
152
+ end
153
+ end
154
+
155
+ bot.use AdminOnly.new
156
+ ```
157
+
158
+ Broadcast to All Users
159
+
160
+ ```ruby
161
+ bot.command('broadcast') do |ctx|
162
+ # Get all user IDs from database
163
+ user_ids = User.pluck(:telegram_id)
164
+
165
+ ctx.reply "Broadcasting to #{user_ids.size} users..."
166
+
167
+ Async do
168
+ user_ids.each do |user_id|
169
+ begin
170
+ ctx.api.call('sendMessage', {
171
+ chat_id: user_id,
172
+ text: "📢 Announcement: #{ctx.message.text.sub('/broadcast ', '')}"
173
+ })
174
+ sleep(0.1) # Rate limiting
175
+ rescue => e
176
+ logger.error("Failed to send to #{user_id}: #{e.message}")
177
+ end
178
+ end
179
+ end
180
+
181
+ ctx.reply "✅ Broadcast sent!"
182
+ end
183
+ ```
184
+
185
+ ---
186
+
187
+ 🗄️ Database Patterns
188
+
189
+ ActiveRecord Integration
190
+
191
+ ```ruby
192
+ class User < ActiveRecord::Base
193
+ def self.from_telegram(ctx)
194
+ find_or_create_by(telegram_id: ctx.from.id) do |user|
195
+ user.username = ctx.from.username
196
+ user.first_name = ctx.from.first_name
197
+ user.last_name = ctx.from.last_name
198
+ end
199
+ end
200
+ end
201
+
202
+ bot.command('profile') do |ctx|
203
+ user = User.from_telegram(ctx)
204
+
205
+ profile = <<~PROFILE
206
+ 👤 Your Profile:
207
+
208
+ ID: #{user.telegram_id}
209
+ Name: #{user.first_name} #{user.last_name}
210
+ Joined: #{user.created_at.strftime('%Y-%m-%d')}
211
+ Messages: #{user.messages_count}
212
+ PROFILE
213
+
214
+ ctx.reply profile
215
+ end
216
+ ```
217
+
218
+ Redis Session Store
219
+
220
+ ```ruby
221
+ require 'redis'
222
+ require 'json'
223
+
224
+ class RedisSessionStore
225
+ def initialize(redis = Redis.new)
226
+ @redis = redis
227
+ @prefix = "telegem:session"
228
+ end
229
+
230
+ def get(user_id)
231
+ data = @redis.get("#{@prefix}:#{user_id}")
232
+ data ? JSON.parse(data, symbolize_names: true) : {}
233
+ end
234
+
235
+ def set(user_id, data)
236
+ @redis.setex("#{@prefix}:#{user_id}", 3600, data.to_json)
237
+ end
238
+ end
239
+
240
+ # Use it
241
+ bot = Telegem.new(token, session_store: RedisSessionStore.new)
242
+ ```
243
+
244
+ ---
245
+
246
+ 🛠️ Utility Helpers
247
+
248
+ Formatting Helper
249
+
250
+ ```ruby
251
+ module Formatters
252
+ def self.markdown(text)
253
+ # Escape MarkdownV2 special characters
254
+ text.gsub(/[_*[\]()~`>#+\-=|{}.!]/, '\\\\\0')
255
+ end
256
+
257
+ def self.html(text)
258
+ # Simple HTML escaping
259
+ text.gsub('&', '&amp;')
260
+ .gsub('<', '&lt;')
261
+ .gsub('>', '&gt;')
262
+ end
263
+ end
264
+
265
+ bot.command('bold') do |ctx|
266
+ text = ctx.message.text.sub('/bold ', '')
267
+ ctx.reply "*#{Formatters.markdown(text)}*", parse_mode: 'MarkdownV2'
268
+ end
269
+ ```
270
+
271
+ Pagination Helper
272
+
273
+ ```ruby
274
+ class Paginator
275
+ def initialize(items, per_page: 5)
276
+ @items = items
277
+ @per_page = per_page
278
+ end
279
+
280
+ def page(number)
281
+ start = (number - 1) * @per_page
282
+ @items.slice(start, @per_page)
283
+ end
284
+
285
+ def total_pages
286
+ (@items.size.to_f / @per_page).ceil
287
+ end
288
+ end
289
+
290
+ bot.command('list') do |ctx|
291
+ items = (1..50).to_a # Your data
292
+ paginator = Paginator.new(items)
293
+ page_num = ctx.session[:page] || 1
294
+
295
+ # Show page
296
+ page_items = paginator.page(page_num)
297
+ ctx.reply "Page #{page_num}/#{paginator.total_pages}\n#{page_items.join(', ')}"
298
+
299
+ # Add navigation buttons
300
+ keyboard = Telegem::Markup.inline do
301
+ row callback("⬅️ Prev", "page_#{page_num-1}") if page_num > 1
302
+ row callback("Next ➡️", "page_#{page_num+1}") if page_num < paginator.total_pages
303
+ end
304
+
305
+ ctx.reply "Navigate:", reply_markup: keyboard
306
+ end
307
+ ```
308
+
309
+ ---
310
+
311
+ 🚀 Deployment Recipes
312
+
313
+ Dockerfile
314
+
315
+ ```dockerfile
316
+ FROM ruby:3.2-alpine
317
+
318
+ WORKDIR /app
319
+
320
+ # Install dependencies
321
+ RUN apk add --no-cache build-base git
322
+
323
+ # Install gems
324
+ COPY Gemfile Gemfile.lock ./
325
+ RUN bundle install --jobs=4 --retry=3
326
+
327
+ # Copy app
328
+ COPY . .
329
+
330
+ # Run bot
331
+ CMD ["ruby", "bot.rb"]
332
+ ```
333
+
334
+ Docker Compose
335
+
336
+ ```yaml
337
+ version: '3.8'
338
+ services:
339
+ bot:
340
+ build: .
341
+ environment:
342
+ - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
343
+ - REDIS_URL=redis://redis:6379
344
+ - DATABASE_URL=postgres://postgres:password@db:5432/bot
345
+ depends_on:
346
+ - redis
347
+ - db
348
+
349
+ redis:
350
+ image: redis:alpine
351
+ ports:
352
+ - "6379:6379"
353
+
354
+ db:
355
+ image: postgres:15-alpine
356
+ environment:
357
+ - POSTGRES_PASSWORD=password
358
+ - POSTGRES_DB=bot
359
+ volumes:
360
+ - postgres_data:/var/lib/postgresql/data
361
+
362
+ volumes:
363
+ postgres_data:
364
+ ```
365
+
366
+ ---
367
+
368
+ 🆘 Troubleshooting
369
+
370
+ "Token Invalid" Error
371
+
372
+ ```ruby
373
+ # Check your token format
374
+ token = ENV['TELEGRAM_BOT_TOKEN']
375
+
376
+ # Should be: 1234567890:ABCdefGHIjklMNOpqrSTUvwxYZ
377
+ # Has two parts separated by colon
378
+ if token.nil? || token.split(':').size != 2
379
+ puts "❌ Invalid token format!"
380
+ exit 1
381
+ end
382
+ ```
383
+
384
+ "Bot Not Responding"
385
+
386
+ ```ruby
387
+ # Add logging middleware
388
+ bot.use do |ctx, next_middleware|
389
+ puts "📨 Received: #{ctx.message&.text}"
390
+ next_middleware.call(ctx)
391
+ puts "✅ Handled"
392
+ end
393
+ ```
394
+
395
+ ---
396
+
397
+ 🎯 Quick Search
398
+
399
+ Looking for something specific?
400
+
401
+ • Photos → Send Photo from URL
402
+ • Files → Receive and Save Document
403
+ • Database → ActiveRecord Integration
404
+ • Admin → Admin Middleware
405
+ • Deploy → Dockerfile
406
+
407
+ ---