telegem 3.3.1 → 3.4.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +57 -0
- data/CHANGELOG.md +19 -1
- data/README.md +147 -0
- data/bin/telegem-ssl +71 -25
- data/contributing.md +375 -0
- data/docs/api.md +663 -0
- data/docs/bot.md +332 -0
- data/docs/changelog.md +182 -0
- data/docs/context.md +554 -0
- data/docs/core_concepts.md +218 -0
- data/docs/deployment.md +702 -0
- data/docs/error_handling.md +435 -0
- data/docs/examples.md +752 -0
- data/docs/getting_started.md +151 -0
- data/docs/handlers.md +580 -0
- data/docs/keyboards.md +446 -0
- data/docs/middleware.md +536 -0
- data/docs/plugins.md +384 -0
- data/docs/scenes.md +517 -0
- data/docs/sessions.md +544 -0
- data/docs/testing.md +612 -0
- data/docs/troubleshooting.md +574 -0
- data/docs/types.md +538 -0
- data/docs/webhooks.md +456 -0
- data/lib/api/client.rb +38 -10
- data/lib/api/types.rb +129 -106
- data/lib/core/composer.rb +3 -3
- data/lib/core/context.rb +17 -11
- data/lib/plugins/cc +3 -0
- data/lib/plugins/translate.rb +43 -0
- data/lib/session/redis.rb +91 -0
- data/lib/telegem.rb +3 -3
- data/lib/webhook/server.rb +5 -3
- metadata +41 -29
- data/Contributing.md +0 -161
- data/Readme.md +0 -298
- data/bin/telegem-init +0 -32
- data/examples/.gitkeep +0 -0
- data/public/.gitkeep +0 -0
data/docs/deployment.md
ADDED
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
# Deployment
|
|
2
|
+
|
|
3
|
+
Deploy Telegem bots to production with proper configuration, monitoring, and scaling.
|
|
4
|
+
|
|
5
|
+
## Environment Setup
|
|
6
|
+
|
|
7
|
+
### Production Environment Variables
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Required
|
|
11
|
+
export TELEGRAM_BOT_TOKEN="your_bot_token_here"
|
|
12
|
+
export TELEGEM_ENV="production"
|
|
13
|
+
|
|
14
|
+
# Optional
|
|
15
|
+
export REDIS_URL="redis://localhost:6379/0"
|
|
16
|
+
export LOG_LEVEL="info"
|
|
17
|
+
export WEBHOOK_URL="https://your-domain.com/webhook"
|
|
18
|
+
export SSL_CERT_PATH="/path/to/cert.pem"
|
|
19
|
+
export SSL_KEY_PATH="/path/to/key.pem"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Ruby Version Management
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
# Gemfile
|
|
26
|
+
ruby '3.1.0'
|
|
27
|
+
|
|
28
|
+
# Use specific Ruby version
|
|
29
|
+
source 'https://rubygems.org'
|
|
30
|
+
|
|
31
|
+
gem 'telegem'
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### System Dependencies
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Ubuntu/Debian
|
|
38
|
+
sudo apt-get update
|
|
39
|
+
sudo apt-get install ruby ruby-dev build-essential redis-server
|
|
40
|
+
|
|
41
|
+
# macOS
|
|
42
|
+
brew install ruby redis
|
|
43
|
+
|
|
44
|
+
# Install bundler
|
|
45
|
+
gem install bundler
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Webhook Deployment
|
|
49
|
+
|
|
50
|
+
### Basic Webhook Setup
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
require 'telegem'
|
|
54
|
+
|
|
55
|
+
bot = Telegem.new(token: ENV['TELEGRAM_BOT_TOKEN'])
|
|
56
|
+
|
|
57
|
+
# Configure webhook
|
|
58
|
+
bot.webhook_url = ENV['WEBHOOK_URL']
|
|
59
|
+
bot.webhook_cert = ENV['SSL_CERT_PATH'] if ENV['SSL_CERT_PATH']
|
|
60
|
+
|
|
61
|
+
# Add handlers
|
|
62
|
+
bot.command('start') do |ctx|
|
|
63
|
+
ctx.reply("Bot is running!")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Start webhook server
|
|
67
|
+
bot.start_webhook
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### SSL Configuration
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
# Self-signed certificate (development only)
|
|
74
|
+
bot.webhook_cert = './ssl/cert.pem'
|
|
75
|
+
bot.webhook_key = './ssl/key.pem'
|
|
76
|
+
|
|
77
|
+
# Let's Encrypt certificate (production)
|
|
78
|
+
bot.webhook_cert = '/etc/letsencrypt/live/yourdomain.com/fullchain.pem'
|
|
79
|
+
bot.webhook_key = '/etc/letsencrypt/live/yourdomain.com/privkey.pem'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Webhook Server Configuration
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
# Custom server configuration
|
|
86
|
+
server = Telegem::Webhook::Server.new(
|
|
87
|
+
host: '0.0.0.0',
|
|
88
|
+
port: 8443,
|
|
89
|
+
ssl_cert: cert_path,
|
|
90
|
+
ssl_key: key_path,
|
|
91
|
+
max_connections: 100,
|
|
92
|
+
timeout: 30
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
bot.start_webhook(server: server)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Polling Deployment
|
|
99
|
+
|
|
100
|
+
### Long-Running Process
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
require 'telegem'
|
|
104
|
+
|
|
105
|
+
bot = Telegem.new(token: ENV['TELEGRAM_BOT_TOKEN'])
|
|
106
|
+
|
|
107
|
+
# Configure polling
|
|
108
|
+
bot.poll_timeout = 30
|
|
109
|
+
bot.poll_limit = 100
|
|
110
|
+
|
|
111
|
+
# Add handlers
|
|
112
|
+
bot.command('ping') do |ctx|
|
|
113
|
+
ctx.reply("Pong!")
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Start polling
|
|
117
|
+
bot.start_polling
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Process Management
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# systemd service file (/etc/systemd/system/telegem-bot.service)
|
|
124
|
+
[Unit]
|
|
125
|
+
Description=Telegem Bot
|
|
126
|
+
After=network.target redis.service
|
|
127
|
+
|
|
128
|
+
[Service]
|
|
129
|
+
Type=simple
|
|
130
|
+
User=telegem
|
|
131
|
+
WorkingDirectory=/path/to/bot
|
|
132
|
+
ExecStart=/usr/local/bin/bundle exec ruby bot.rb
|
|
133
|
+
Restart=always
|
|
134
|
+
RestartSec=5
|
|
135
|
+
Environment=TELEGEM_ENV=production
|
|
136
|
+
|
|
137
|
+
[Install]
|
|
138
|
+
WantedBy=multi-user.target
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Enable and start service
|
|
143
|
+
sudo systemctl enable telegem-bot
|
|
144
|
+
sudo systemctl start telegem-bot
|
|
145
|
+
sudo systemctl status telegem-bot
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Docker Deployment
|
|
149
|
+
|
|
150
|
+
### Dockerfile
|
|
151
|
+
|
|
152
|
+
```dockerfile
|
|
153
|
+
FROM ruby:3.1-slim
|
|
154
|
+
|
|
155
|
+
# Install system dependencies
|
|
156
|
+
RUN apt-get update && apt-get install -y \
|
|
157
|
+
build-essential \
|
|
158
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
159
|
+
|
|
160
|
+
# Set working directory
|
|
161
|
+
WORKDIR /app
|
|
162
|
+
|
|
163
|
+
# Copy Gemfile and install dependencies
|
|
164
|
+
COPY Gemfile Gemfile.lock ./
|
|
165
|
+
RUN bundle install --without development test
|
|
166
|
+
|
|
167
|
+
# Copy application code
|
|
168
|
+
COPY . .
|
|
169
|
+
|
|
170
|
+
# Create non-root user
|
|
171
|
+
RUN useradd --create-home --shell /bin/bash telegem
|
|
172
|
+
USER telegem
|
|
173
|
+
|
|
174
|
+
# Expose port for webhook
|
|
175
|
+
EXPOSE 8443
|
|
176
|
+
|
|
177
|
+
# Health check
|
|
178
|
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
|
179
|
+
CMD curl -f http://localhost:8443/health || exit 1
|
|
180
|
+
|
|
181
|
+
# Start the bot
|
|
182
|
+
CMD ["bundle", "exec", "ruby", "bot.rb"]
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Docker Compose
|
|
186
|
+
|
|
187
|
+
```yaml
|
|
188
|
+
version: '3.8'
|
|
189
|
+
|
|
190
|
+
services:
|
|
191
|
+
bot:
|
|
192
|
+
build: .
|
|
193
|
+
environment:
|
|
194
|
+
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
|
|
195
|
+
- REDIS_URL=redis://redis:6379/0
|
|
196
|
+
- TELEGEM_ENV=production
|
|
197
|
+
depends_on:
|
|
198
|
+
- redis
|
|
199
|
+
restart: unless-stopped
|
|
200
|
+
|
|
201
|
+
redis:
|
|
202
|
+
image: redis:7-alpine
|
|
203
|
+
volumes:
|
|
204
|
+
- redis_data:/data
|
|
205
|
+
restart: unless-stopped
|
|
206
|
+
|
|
207
|
+
volumes:
|
|
208
|
+
redis_data:
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Multi-Stage Build
|
|
212
|
+
|
|
213
|
+
```dockerfile
|
|
214
|
+
# Build stage
|
|
215
|
+
FROM ruby:3.1-slim as builder
|
|
216
|
+
|
|
217
|
+
RUN apt-get update && apt-get install -y build-essential
|
|
218
|
+
WORKDIR /app
|
|
219
|
+
COPY Gemfile Gemfile.lock ./
|
|
220
|
+
RUN bundle install --without development test && bundle clean --force
|
|
221
|
+
|
|
222
|
+
# Runtime stage
|
|
223
|
+
FROM ruby:3.1-slim
|
|
224
|
+
|
|
225
|
+
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
|
|
226
|
+
COPY --from=builder /usr/local/bundle /usr/local/bundle
|
|
227
|
+
WORKDIR /app
|
|
228
|
+
COPY . .
|
|
229
|
+
|
|
230
|
+
USER nobody
|
|
231
|
+
EXPOSE 8443
|
|
232
|
+
CMD ["bundle", "exec", "ruby", "bot.rb"]
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Cloud Deployment
|
|
236
|
+
|
|
237
|
+
### Heroku
|
|
238
|
+
|
|
239
|
+
```ruby
|
|
240
|
+
# Gemfile
|
|
241
|
+
source 'https://rubygems.org'
|
|
242
|
+
ruby '3.1.0'
|
|
243
|
+
|
|
244
|
+
gem 'telegem'
|
|
245
|
+
gem 'redis'
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
```yaml
|
|
249
|
+
# Procfile
|
|
250
|
+
web: bundle exec ruby bot.rb
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
```ruby
|
|
254
|
+
# bot.rb
|
|
255
|
+
require 'telegem'
|
|
256
|
+
|
|
257
|
+
bot = Telegem.new(token: ENV['TELEGRAM_BOT_TOKEN'])
|
|
258
|
+
|
|
259
|
+
# Use Redis for sessions
|
|
260
|
+
bot.session_store = Telegem::Session::RedisStore.new(ENV['REDIS_URL'])
|
|
261
|
+
|
|
262
|
+
# Webhook for Heroku
|
|
263
|
+
bot.webhook_url = "https://#{ENV['HEROKU_APP_NAME']}.herokuapp.com/webhook"
|
|
264
|
+
|
|
265
|
+
bot.command('start') do |ctx|
|
|
266
|
+
ctx.reply("Bot deployed on Heroku!")
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
bot.start_webhook
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
# Deploy
|
|
274
|
+
heroku create your-bot-name
|
|
275
|
+
heroku config:set TELEGRAM_BOT_TOKEN=your_token_here
|
|
276
|
+
git push heroku main
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### AWS ECS
|
|
280
|
+
|
|
281
|
+
```json
|
|
282
|
+
{
|
|
283
|
+
"family": "telegem-bot",
|
|
284
|
+
"taskRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
|
|
285
|
+
"executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
|
|
286
|
+
"networkMode": "awsvpc",
|
|
287
|
+
"requiresCompatibilities": ["FARGATE"],
|
|
288
|
+
"cpu": "256",
|
|
289
|
+
"memory": "512",
|
|
290
|
+
"containerDefinitions": [
|
|
291
|
+
{
|
|
292
|
+
"name": "telegem-bot",
|
|
293
|
+
"image": "your-registry/telegem-bot:latest",
|
|
294
|
+
"essential": true,
|
|
295
|
+
"environment": [
|
|
296
|
+
{"name": "TELEGRAM_BOT_TOKEN", "value": "your_token"},
|
|
297
|
+
{"name": "TELEGEM_ENV", "value": "production"}
|
|
298
|
+
],
|
|
299
|
+
"logConfiguration": {
|
|
300
|
+
"logDriver": "awslogs",
|
|
301
|
+
"options": {
|
|
302
|
+
"awslogs-group": "/ecs/telegem-bot",
|
|
303
|
+
"awslogs-region": "us-east-1",
|
|
304
|
+
"awslogs-stream-prefix": "ecs"
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
]
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Google Cloud Run
|
|
313
|
+
|
|
314
|
+
```yaml
|
|
315
|
+
apiVersion: serving.knative.dev/v1
|
|
316
|
+
kind: Service
|
|
317
|
+
metadata:
|
|
318
|
+
name: telegem-bot
|
|
319
|
+
spec:
|
|
320
|
+
template:
|
|
321
|
+
spec:
|
|
322
|
+
containers:
|
|
323
|
+
- image: gcr.io/your-project/telegem-bot:latest
|
|
324
|
+
env:
|
|
325
|
+
- name: TELEGRAM_BOT_TOKEN
|
|
326
|
+
value: "your_token"
|
|
327
|
+
- name: TELEGEM_ENV
|
|
328
|
+
value: "production"
|
|
329
|
+
ports:
|
|
330
|
+
- containerPort: 8443
|
|
331
|
+
resources:
|
|
332
|
+
limits:
|
|
333
|
+
cpu: 1000m
|
|
334
|
+
memory: 512Mi
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Railway
|
|
338
|
+
|
|
339
|
+
```toml
|
|
340
|
+
# railway.toml
|
|
341
|
+
[build]
|
|
342
|
+
builder = "dockerfile"
|
|
343
|
+
|
|
344
|
+
[deploy]
|
|
345
|
+
healthcheckPath = "/health"
|
|
346
|
+
healthcheckTimeout = 300
|
|
347
|
+
restartPolicyType = "ON_FAILURE"
|
|
348
|
+
restartPolicyMaxRetries = 10
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Monitoring and Logging
|
|
352
|
+
|
|
353
|
+
### Structured Logging
|
|
354
|
+
|
|
355
|
+
```ruby
|
|
356
|
+
require 'logger'
|
|
357
|
+
|
|
358
|
+
class ProductionLogger
|
|
359
|
+
def initialize
|
|
360
|
+
@logger = Logger.new(STDOUT)
|
|
361
|
+
@logger.level = ENV['LOG_LEVEL'] == 'debug' ? Logger::DEBUG : Logger::INFO
|
|
362
|
+
@logger.formatter = proc do |severity, datetime, progname, msg|
|
|
363
|
+
{
|
|
364
|
+
timestamp: datetime.iso8601,
|
|
365
|
+
level: severity,
|
|
366
|
+
message: msg,
|
|
367
|
+
service: 'telegem-bot'
|
|
368
|
+
}.to_json + "\n"
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def info(msg); @logger.info(msg); end
|
|
373
|
+
def error(msg); @logger.error(msg); end
|
|
374
|
+
def debug(msg); @logger.debug(msg); end
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
bot.logger = ProductionLogger.new
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Health Checks
|
|
381
|
+
|
|
382
|
+
```ruby
|
|
383
|
+
bot.get('/health') do |ctx|
|
|
384
|
+
# Check database connectivity
|
|
385
|
+
redis_ping = begin
|
|
386
|
+
bot.session_store.redis.ping == 'PONG'
|
|
387
|
+
rescue
|
|
388
|
+
false
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
# Check bot API
|
|
392
|
+
api_check = begin
|
|
393
|
+
bot.api.call('getMe')['ok']
|
|
394
|
+
rescue
|
|
395
|
+
false
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
status = redis_ping && api_check ? 200 : 503
|
|
399
|
+
ctx.response.status = status
|
|
400
|
+
ctx.response.body = {
|
|
401
|
+
status: status == 200 ? 'healthy' : 'unhealthy',
|
|
402
|
+
timestamp: Time.now.to_i,
|
|
403
|
+
checks: {
|
|
404
|
+
redis: redis_ping,
|
|
405
|
+
telegram_api: api_check
|
|
406
|
+
}
|
|
407
|
+
}.to_json
|
|
408
|
+
end
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Metrics Collection
|
|
412
|
+
|
|
413
|
+
```ruby
|
|
414
|
+
class MetricsMiddleware
|
|
415
|
+
def initialize
|
|
416
|
+
@metrics = {
|
|
417
|
+
requests_total: 0,
|
|
418
|
+
errors_total: 0,
|
|
419
|
+
response_time_sum: 0
|
|
420
|
+
}
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
def call(ctx, next_middleware)
|
|
424
|
+
start_time = Time.now
|
|
425
|
+
|
|
426
|
+
begin
|
|
427
|
+
@metrics[:requests_total] += 1
|
|
428
|
+
next_middleware.call(ctx)
|
|
429
|
+
rescue => e
|
|
430
|
+
@metrics[:errors_total] += 1
|
|
431
|
+
raise
|
|
432
|
+
ensure
|
|
433
|
+
response_time = Time.now - start_time
|
|
434
|
+
@metrics[:response_time_sum] += response_time
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
def stats
|
|
439
|
+
avg_response_time = @metrics[:requests_total] > 0 ?
|
|
440
|
+
@metrics[:response_time_sum] / @metrics[:requests_total] : 0
|
|
441
|
+
|
|
442
|
+
{
|
|
443
|
+
requests_total: @metrics[:requests_total],
|
|
444
|
+
errors_total: @metrics[:errors_total],
|
|
445
|
+
avg_response_time: avg_response_time
|
|
446
|
+
}
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
metrics = MetricsMiddleware.new
|
|
451
|
+
bot.use metrics
|
|
452
|
+
|
|
453
|
+
bot.get('/metrics') do |ctx|
|
|
454
|
+
ctx.response.body = metrics.stats.to_json
|
|
455
|
+
end
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
## Scaling Considerations
|
|
459
|
+
|
|
460
|
+
### Horizontal Scaling
|
|
461
|
+
|
|
462
|
+
```ruby
|
|
463
|
+
# Multiple bot instances
|
|
464
|
+
instances = ENV['INSTANCES']&.to_i || 1
|
|
465
|
+
|
|
466
|
+
instances.times do |i|
|
|
467
|
+
fork do
|
|
468
|
+
bot = Telegem.new(token: ENV['TELEGRAM_BOT_TOKEN'])
|
|
469
|
+
# Configure instance-specific settings
|
|
470
|
+
bot.instance_id = i
|
|
471
|
+
bot.start_polling
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
Process.waitall
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Load Balancing
|
|
479
|
+
|
|
480
|
+
```ruby
|
|
481
|
+
# Use Redis for distributed sessions
|
|
482
|
+
bot.session_store = Telegem::Session::RedisStore.new(ENV['REDIS_URL'])
|
|
483
|
+
|
|
484
|
+
# Distributed locks for critical operations
|
|
485
|
+
require 'redis-mutex'
|
|
486
|
+
|
|
487
|
+
bot.command('critical') do |ctx|
|
|
488
|
+
Redis::Mutex.with_lock('critical_operation') do
|
|
489
|
+
# Critical operation
|
|
490
|
+
ctx.reply("Operation completed")
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### Database Connection Pooling
|
|
496
|
+
|
|
497
|
+
```ruby
|
|
498
|
+
# For database-backed session stores
|
|
499
|
+
bot.session_store = Telegem::Session::DatabaseStore.new(
|
|
500
|
+
pool_size: ENV['DB_POOL_SIZE']&.to_i || 5,
|
|
501
|
+
timeout: 5
|
|
502
|
+
)
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
## Security Best Practices
|
|
506
|
+
|
|
507
|
+
### Environment Variables
|
|
508
|
+
|
|
509
|
+
```bash
|
|
510
|
+
# Never commit secrets
|
|
511
|
+
echo ".env" >> .gitignore
|
|
512
|
+
|
|
513
|
+
# Use strong tokens
|
|
514
|
+
export TELEGRAM_BOT_TOKEN="$(openssl rand -hex 32)"
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### SSL/TLS
|
|
518
|
+
|
|
519
|
+
```ruby
|
|
520
|
+
# Force HTTPS in production
|
|
521
|
+
bot.use SSLMiddleware.new if ENV['TELEGEM_ENV'] == 'production'
|
|
522
|
+
|
|
523
|
+
class SSLMiddleware
|
|
524
|
+
def call(ctx, next_middleware)
|
|
525
|
+
if ctx.request.scheme != 'https'
|
|
526
|
+
ctx.response.status = 301
|
|
527
|
+
ctx.response.headers['Location'] = ctx.request.url.sub('http:', 'https:')
|
|
528
|
+
return
|
|
529
|
+
end
|
|
530
|
+
next_middleware.call(ctx)
|
|
531
|
+
end
|
|
532
|
+
end
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### Input Validation
|
|
536
|
+
|
|
537
|
+
```ruby
|
|
538
|
+
bot.use InputValidationMiddleware.new
|
|
539
|
+
|
|
540
|
+
class InputValidationMiddleware
|
|
541
|
+
def call(ctx, next_middleware)
|
|
542
|
+
# Sanitize input
|
|
543
|
+
if ctx.text
|
|
544
|
+
ctx.text = sanitize_input(ctx.text)
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
# Rate limiting
|
|
548
|
+
if rate_limited?(ctx.from.id)
|
|
549
|
+
ctx.response.status = 429
|
|
550
|
+
return
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
next_middleware.call(ctx)
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
private
|
|
557
|
+
|
|
558
|
+
def sanitize_input(text)
|
|
559
|
+
# Remove dangerous characters
|
|
560
|
+
text.gsub(/[<>'"&]/, '')
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
def rate_limited?(user_id)
|
|
564
|
+
# Implement rate limiting logic
|
|
565
|
+
false
|
|
566
|
+
end
|
|
567
|
+
end
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
## Performance Optimization
|
|
571
|
+
|
|
572
|
+
### Connection Pooling
|
|
573
|
+
|
|
574
|
+
```ruby
|
|
575
|
+
# HTTP client configuration
|
|
576
|
+
bot.api.http_client = Async::HTTP::Client.new(
|
|
577
|
+
pool_size: 10,
|
|
578
|
+
timeout: 30
|
|
579
|
+
)
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
### Caching
|
|
583
|
+
|
|
584
|
+
```ruby
|
|
585
|
+
require 'redis'
|
|
586
|
+
|
|
587
|
+
class CacheMiddleware
|
|
588
|
+
def initialize(redis_url)
|
|
589
|
+
@redis = Redis.new(url: redis_url)
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
def call(ctx, next_middleware)
|
|
593
|
+
cache_key = "response:#{ctx.update.update_id}"
|
|
594
|
+
|
|
595
|
+
if cached = @redis.get(cache_key)
|
|
596
|
+
ctx.response.body = cached
|
|
597
|
+
return
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
next_middleware.call(ctx)
|
|
601
|
+
|
|
602
|
+
# Cache successful responses
|
|
603
|
+
if ctx.response.status == 200
|
|
604
|
+
@redis.setex(cache_key, 300, ctx.response.body) # 5 min cache
|
|
605
|
+
end
|
|
606
|
+
end
|
|
607
|
+
end
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
### Background Processing
|
|
611
|
+
|
|
612
|
+
```ruby
|
|
613
|
+
require 'async'
|
|
614
|
+
|
|
615
|
+
bot.command('heavy_task') do |ctx|
|
|
616
|
+
ctx.reply("Processing in background...")
|
|
617
|
+
|
|
618
|
+
Async do
|
|
619
|
+
result = perform_heavy_computation(ctx.text)
|
|
620
|
+
ctx.reply("Result: #{result}")
|
|
621
|
+
end
|
|
622
|
+
end
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
## Backup and Recovery
|
|
626
|
+
|
|
627
|
+
### Database Backups
|
|
628
|
+
|
|
629
|
+
```bash
|
|
630
|
+
# Redis backup script
|
|
631
|
+
#!/bin/bash
|
|
632
|
+
DATE=$(date +%Y%m%d_%H%M%S)
|
|
633
|
+
redis-cli --rdb /backup/redis_$DATE.rdb
|
|
634
|
+
|
|
635
|
+
# Clean old backups
|
|
636
|
+
find /backup -name "redis_*.rdb" -mtime +7 -delete
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
### Configuration Backup
|
|
640
|
+
|
|
641
|
+
```ruby
|
|
642
|
+
# Backup bot configuration
|
|
643
|
+
task :backup do
|
|
644
|
+
config = {
|
|
645
|
+
token: ENV['TELEGRAM_BOT_TOKEN'],
|
|
646
|
+
webhook_url: ENV['WEBHOOK_URL'],
|
|
647
|
+
session_store: bot.session_store.class.name,
|
|
648
|
+
timestamp: Time.now.to_i
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
File.write("/backup/config_#{Time.now.to_i}.json", config.to_json)
|
|
652
|
+
end
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
## Troubleshooting
|
|
656
|
+
|
|
657
|
+
### Common Issues
|
|
658
|
+
|
|
659
|
+
**Webhook not receiving updates:**
|
|
660
|
+
- Check SSL certificate validity
|
|
661
|
+
- Verify webhook URL is accessible
|
|
662
|
+
- Check firewall settings
|
|
663
|
+
|
|
664
|
+
**High memory usage:**
|
|
665
|
+
- Monitor for memory leaks
|
|
666
|
+
- Use connection pooling
|
|
667
|
+
- Implement proper garbage collection
|
|
668
|
+
|
|
669
|
+
**Rate limiting:**
|
|
670
|
+
- Implement exponential backoff
|
|
671
|
+
- Cache frequent requests
|
|
672
|
+
- Use webhooks instead of polling
|
|
673
|
+
|
|
674
|
+
**Database connection issues:**
|
|
675
|
+
- Check connection pool size
|
|
676
|
+
- Implement connection retry logic
|
|
677
|
+
- Monitor database performance
|
|
678
|
+
|
|
679
|
+
### Debug Mode
|
|
680
|
+
|
|
681
|
+
```ruby
|
|
682
|
+
if ENV['DEBUG'] == 'true'
|
|
683
|
+
bot.logger.level = :debug
|
|
684
|
+
bot.api.debug = true
|
|
685
|
+
end
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
### Log Analysis
|
|
689
|
+
|
|
690
|
+
```bash
|
|
691
|
+
# Search for errors
|
|
692
|
+
grep "ERROR" /var/log/telegem-bot.log
|
|
693
|
+
|
|
694
|
+
# Count requests per hour
|
|
695
|
+
grep "INFO.*request" /var/log/telegem-bot.log | cut -d' ' -f1 | uniq -c
|
|
696
|
+
|
|
697
|
+
# Monitor response times
|
|
698
|
+
grep "response_time" /var/log/telegem-bot.log | awk '{sum+=$2} END {print sum/NR}'
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
Production deployment requires careful consideration of security, performance, monitoring, and scalability to ensure reliable bot operation.</content>
|
|
702
|
+
<parameter name="filePath">/home/slick/telegem/docs/deployment.md
|