telegem 3.0.0 → 3.0.1

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: a044b22212b4380cb47006c7f5bf18b06ee732c01d1baf8e18cd46bec92e6b32
4
- data.tar.gz: e5bb919a62f17c531241f94ed45fe7331708f4485dae46423b9a8811941d07a9
3
+ metadata.gz: 2663e7e2e3bbafeb9855747f7e93726c48c41b1ff30d3ae20d5933964f4d09d1
4
+ data.tar.gz: 479cc2f2cb14233af4deacdb0453861078d5bcad79822a5fd12c4a027894225c
5
5
  SHA512:
6
- metadata.gz: a4b801f8c4999e6d3f9bb77798e5f6b4a1d02d4a2d38d2c9f4d138a5cc0589b59983a78b16a58787a9b387fce9e66edf7da343446dd2fdbb0f772ecacdf786f7
7
- data.tar.gz: ed36cec7c19a5f0cf68f5e0415ef890f92fcc1e95f793a21a56949facc50ee68264f11a3f35c54f7b02583d6c8768b082efb487ba30b53e3c8a6d307f5d0ceb4
6
+ metadata.gz: 6e6891ef4c4afb5da86def0a602711bfe8d3562eaa3b3e01fab7c89df5b335da1a65afaf707459daec7b84d4761f22bc83bd3651532328a30c5bd05a75541bc0
7
+ data.tar.gz: 8f8c3ebadd7c227c94f61f10e00bec56ffe60efb0f21924899e6410640c918f9fff308fad7e2b29c5306fde98eec262a000e17fba723075de162e6a7745bb246
data/bin/.gitkeep ADDED
File without changes
data/bin/telegem-ssl ADDED
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+ # bin/telegem-ssl
3
+
4
+ require 'fileutils'
5
+
6
+ puts "šŸ” Telegem SSL Setup"
7
+ puts "=" * 10
8
+
9
+ # Get domain
10
+ domain = ARGV[0]
11
+ unless domain
12
+ print "🌐 Enter domain (e.g., bot.example.com): "
13
+ domain = gets.chomp
14
+ end
15
+
16
+ # Get email
17
+ email = ARGV[1] || "admin@#{domain}"
18
+
19
+ # Check certbot
20
+ unless system("which certbot > /dev/null")
21
+ puts "šŸ“¦ Installing certbot..."
22
+ system("sudo apt update && sudo apt install -y certbot")
23
+ end
24
+
25
+ # Generate certificate
26
+ puts "\nšŸ“„ Getting SSL certificate..."
27
+ cert_cmd = "sudo certbot certonly --standalone -d #{domain} --email #{email} --agree-tos --non-interactive"
28
+ puts "Running: #{cert_cmd}"
29
+
30
+ if system(cert_cmd)
31
+ # Create config
32
+ config = {
33
+ domain: domain,
34
+ cert_path: "/etc/letsencrypt/live/#{domain}/fullchain.pem",
35
+ key_path: "/etc/letsencrypt/live/#{domain}/privkey.pem"
36
+ }
37
+
38
+ File.write(".telegem-ssl", config.to_yaml)
39
+ puts "āœ… SSL configured! Certificate valid for 90 days."
40
+ puts "šŸ“ Config saved to .telegem-ssl"
41
+ puts "\nšŸš€ Start your bot: ruby bot.rb"
42
+ else
43
+ puts "āŒ Failed to get certificate. Check domain and try again."
44
+ end
@@ -0,0 +1,341 @@
1
+ Webhook Guide for Telegem
2
+
3
+ This guide explains how to set up and use webhooks with Telegem. Telegram bots require HTTPS webhooks, and this guide covers all setup options.
4
+
5
+ šŸŽÆ Quick Start
6
+
7
+ Choose your hosting type and follow the appropriate section:
8
+
9
+ - VPS/Dedicated Server → Use the CLI tool for automatic SSL
10
+ - Cloud Platforms (Render, Railway, Heroku) → Use environment variables
11
+ - Manual Setup → Provide your own certificates
12
+ - Development/Testing → Use polling instead
13
+
14
+ šŸ” SSL Requirements
15
+
16
+ Important: Telegram requires HTTPS webhooks. HTTP will not work. You must configure SSL using one of these methods:
17
+
18
+ Method 1: VPS/Dedicated Server (Recommended)
19
+
20
+ For servers where you have shell access and can run commands:
21
+
22
+ ```bash
23
+ # 1. Install Telegem
24
+ gem install telegem
25
+
26
+ # 2. Run the SSL setup tool
27
+ telegem-ssl your-domain.com your-email@example.com
28
+
29
+ # Example:
30
+ telegem-ssl bot.myapp.com admin@myapp.com
31
+ ```
32
+
33
+ What this does:
34
+
35
+ - Installs certbot if not present
36
+ - Gets a free SSL certificate from Let's Encrypt
37
+ - Creates .telegem-ssl configuration file
38
+ - Certificates auto-renew every 90 days
39
+
40
+ Your bot code:
41
+
42
+ ```ruby
43
+ require 'telegem'
44
+
45
+ bot = Telegem.new(ENV['TELEGRAM_BOT_TOKEN'])
46
+
47
+ # Start webhook - automatically detects .telegem-ssl file
48
+ bot.webhook
49
+
50
+ # Or get the webhook server for more control
51
+ server = bot.webhook
52
+ server.set_webhook
53
+ ```
54
+
55
+ Method 2: Cloud Platforms (Render, Railway, Heroku)
56
+
57
+ For platforms that provide SSL automatically:
58
+
59
+ ```bash
60
+ # Set these environment variables
61
+ export TELEGRAM_BOT_TOKEN=your_bot_token
62
+ export TELEGEM_WEBHOOK_URL=https://your-app.onrender.com
63
+ export WEBHOOK_SECRET_TOKEN=optional_secret_token
64
+ ```
65
+
66
+ Your bot code:
67
+
68
+ ```ruby
69
+ require 'telegem'
70
+
71
+ bot = Telegem.new(ENV['TELEGRAM_BOT_TOKEN'])
72
+
73
+ # Start webhook - detects TELEGEM_WEBHOOK_URL
74
+ server = bot.webhook
75
+
76
+ # Set the webhook with Telegram
77
+ webhook_url = server.set_webhook
78
+ puts "Webhook URL: #{webhook_url}"
79
+ ```
80
+
81
+ Cloud Platform Specifics:
82
+
83
+ Platform TELEGEM_WEBHOOK_URL Example
84
+ Render https://your-app.onrender.com
85
+ Railway https://your-app.railway.app
86
+ Heroku https://your-app.herokuapp.com
87
+ Fly.io https://your-app.fly.dev
88
+
89
+ Method 3: Manual Certificate Setup
90
+
91
+ If you already have SSL certificates:
92
+
93
+ ```ruby
94
+ require 'telegem'
95
+
96
+ bot = Telegem.new(ENV['TELEGRAM_BOT_TOKEN'])
97
+
98
+ # Provide certificate paths
99
+ server = bot.webhook(ssl: {
100
+ cert_path: "/path/to/certificate.pem",
101
+ key_path: "/path/to/private-key.pem"
102
+ })
103
+
104
+ server.set_webhook
105
+ ```
106
+
107
+ šŸ“” Webhook Server Methods
108
+
109
+ Once you have a webhook server instance, you can use these methods:
110
+
111
+ ```ruby
112
+ server = bot.webhook # or bot.webhook(options)
113
+
114
+ # Start the server (runs in background)
115
+ server.run
116
+
117
+ # Get the webhook URL for Telegram
118
+ url = server.webhook_url
119
+ # => "https://your-domain.com/webhook/abc123def456"
120
+
121
+ # Set webhook with Telegram (recommended)
122
+ server.set_webhook
123
+
124
+ # Additional options for set_webhook
125
+ server.set_webhook(
126
+ max_connections: 40,
127
+ allowed_updates: ["message", "callback_query"]
128
+ )
129
+
130
+ # Check webhook info from Telegram
131
+ info = server.get_webhook_info
132
+
133
+ # Delete webhook
134
+ server.delete_webhook
135
+
136
+ # Stop the server
137
+ server.stop
138
+
139
+ # Check if server is running
140
+ server.running? # => true/false
141
+
142
+ # Health check endpoint
143
+ # Your server automatically provides: /health
144
+ ```
145
+
146
+ šŸ”’ Security: Secret Tokens
147
+
148
+ Telegem automatically generates and uses secret tokens to prevent unauthorized access:
149
+
150
+ ```ruby
151
+ # Auto-generated (recommended)
152
+ server = bot.webhook
153
+ puts server.secret_token # => "abc123def456"
154
+
155
+ # Custom token
156
+ server = bot.webhook(secret_token: "my_custom_secret")
157
+
158
+ # Via environment variable
159
+ export WEBHOOK_SECRET_TOKEN=my_custom_secret
160
+ ```
161
+
162
+ Webhook URL format:
163
+
164
+ ```
165
+ https://your-domain.com/abc123def456
166
+ ```
167
+
168
+ Only requests to this exact path will be processed. All other paths return 404.
169
+
170
+ šŸ„ Health Monitoring
171
+
172
+ Your webhook server automatically provides a health endpoint:
173
+
174
+ ```bash
175
+ curl https://your-domain.com/health
176
+ # Returns: {"status":"ok","mode":"cli","ssl":true}
177
+ ```
178
+
179
+ Use this for:
180
+
181
+ - Platform health checks (Render, Railway)
182
+ - Load balancer health checks
183
+ - Monitoring and alerts
184
+
185
+ šŸ”§ Configuration Options
186
+
187
+ Full list of webhook method options:
188
+
189
+ ```ruby
190
+ bot.webhook(
191
+ port: 3000, # Port to listen on
192
+ host: '0.0.0.0', # Host to bind to
193
+ secret_token: nil, # Custom secret token
194
+ logger: custom_logger, # Custom logger
195
+ ssl: { # SSL options
196
+ cert_path: '/path/to/cert.pem',
197
+ key_path: '/path/to/key.pem'
198
+ }
199
+ )
200
+ ```
201
+
202
+ 🚫 Common Errors & Solutions
203
+
204
+ Error: "No SSL configured"
205
+
206
+ Solution: Choose one:
207
+
208
+ 1. Run telegem-ssl your-domain.com (VPS)
209
+ 2. Set TELEGEM_WEBHOOK_URL environment variable (Cloud)
210
+ 3. Provide certificate paths manually
211
+ 4. Use bot.start_polling() instead
212
+
213
+ Error: "Telegram webhook failed"
214
+
215
+ Solution:
216
+
217
+ ```ruby
218
+ # Check current webhook info
219
+ info = server.get_webhook_info
220
+ puts info.inspect
221
+
222
+ # Delete and retry
223
+ server.delete_webhook
224
+ server.set_webhook
225
+ ```
226
+
227
+ Error: "Certificate not found"
228
+
229
+ Solution:
230
+
231
+ ```bash
232
+ # Regenerate certificates
233
+ telegem-ssl your-domain.com --force
234
+
235
+ # Or check file permissions
236
+ ls -la /etc/letsencrypt/live/your-domain.com/
237
+ ```
238
+
239
+ šŸ”„ Polling vs Webhook
240
+
241
+ When to use each:
242
+
243
+ -Use Case Recommended Method
244
+ Development/Testing bot.start_polling()
245
+ -Production (VPS) bot.webhook() with CLI
246
+ -Production (Cloud) bot.webhook() with env var
247
+ -Limited Resources bot.start_polling()
248
+ -High Traffic bot.webhook()
249
+
250
+ Polling example (for development):
251
+
252
+ ```ruby
253
+ bot.start_polling(
254
+ timeout: 30,
255
+ limit: 100,
256
+ allowed_updates: ["message"]
257
+ )
258
+ ```
259
+
260
+ šŸ“‹ Complete Example
261
+
262
+ VPS Production Setup (bot.rb):
263
+
264
+ ```ruby
265
+ #!/usr/bin/env ruby
266
+ require 'telegem'
267
+
268
+ bot = Telegem.new(ENV['TELEGRAM_BOT_TOKEN'])
269
+
270
+ # Command handlers
271
+ bot.command("start") do |ctx|
272
+ ctx.reply("Welcome!")
273
+ end
274
+
275
+ bot.command("help") do |ctx|
276
+ ctx.reply("Available commands: /start, /help")
277
+ end
278
+
279
+ # Start webhook
280
+ server = bot.webhook
281
+
282
+ # Set webhook with Telegram
283
+ server.set_webhook(
284
+ max_connections: 40,
285
+ allowed_updates: ["message", "callback_query"]
286
+ )
287
+
288
+ puts "āœ… Bot running with webhook: #{server.webhook_url}"
289
+ puts "🩺 Health check: #{server.webhook_url.gsub(/\/[^\/]+$/, '/health')}"
290
+
291
+ # Keep the script running
292
+ sleep while true
293
+ ```
294
+
295
+ Cloud Platform Setup (bot.rb):
296
+
297
+ ```ruby
298
+ #!/usr/bin/env ruby
299
+ require 'telegem'
300
+
301
+ bot = Telegem.new(ENV['TELEGRAM_BOT_TOKEN'])
302
+
303
+ # Add your command handlers here
304
+ bot.command("start") { |ctx| ctx.reply("Hello from cloud!") }
305
+
306
+ # Start webhook (auto-detects cloud mode)
307
+ server = bot.webhook
308
+
309
+ # Output the webhook URL
310
+ puts "Webhook URL: #{server.webhook_url}"
311
+
312
+ # Keep running
313
+ sleep while true
314
+ ```
315
+
316
+ šŸš€ Deployment Checklist
317
+
318
+ Before deploying to production:
319
+
320
+ - SSL is configured (CLI, Cloud, or Manual)
321
+ - TELEGRAM_BOT_TOKEN is set
322
+ - WEBHOOK_SECRET_TOKEN is set (optional but recommended)
323
+ - TELEGEM_WEBHOOK_URL is set (for cloud platforms)
324
+ - Port is correctly configured (usually ENV['PORT'] on cloud)
325
+ - Health endpoint is responding (/health)
326
+ - Webhook is set with Telegram (server.set_webhook)
327
+
328
+ šŸ“ž Getting Help
329
+
330
+ If you encounter issues:
331
+
332
+ 1. Check the logs: tail -f log/bot.log
333
+ 2. Verify SSL: curl https://your-domain.com/health
334
+ 3. Check Telegram webhook: server.get_webhook_info
335
+ 4. Enable debug logging: Logger.new($stdout, level: :debug)
336
+
337
+ For more help, visit the Telegem GitLab repository.
338
+
339
+ ---
340
+
341
+ Next Steps: After setting up your webhook, learn about Middleware, Scenes, and Advanced Features in the Telegem framework.
data/lib/api/client.rb CHANGED
@@ -14,7 +14,7 @@ module Telegem
14
14
  @logger = options[:logger] || Logger.new($stdout)
15
15
  timeout = options[:timeout] || 30
16
16
 
17
- @http = HTTPX.with(
17
+ @http = HTTPX.plugin(:callbacks).with(
18
18
  timeout: {
19
19
  request_timeout: timeout,
20
20
  connect_timeout: 10,
@@ -28,36 +28,80 @@ module Telegem
28
28
  )
29
29
  end
30
30
  def call(method, params = {})
31
- url = "#{BASE_URL}/bot#{@token}/#{method}"
32
- @logger.debug("API Call: #{method}") if @logger
33
- @http.post(url, json: params.compact).wait
34
- end
35
- def call!(method, params, &callback)
36
- url = "#{BASE_URL}/bot#{@token}/#{method}"
37
-
38
- @http.post(url, json: params)
39
- .on_complete do |response|
40
- if response.status == 200
41
- json = response.json
42
- if json && json['ok']
43
- callback.call(json['result']) if callback
44
- @logger.debug("API Response: #{json}") if @logger
45
- else
46
- error_msg = json ? json['description'] : "No JSON response"
47
- error_code = json['error_code'] if json
48
- raise APIError.new("API Error: #{error_msg}", error_code)
49
- end
50
- else
51
- raise NetworkError.new("HTTP #{response.status}")
52
- end
53
- rescue JSON::ParserError
54
- raise NetworkError.new("Invalid JSON response")
55
- rescue => e
56
- raise e
57
- end
58
- .on_error { |error| callback.call(nil, error) if callback }
31
+ url = "#{BASE_URL}/bot#{@token}/#{method}"
32
+ @logger.debug("API Call: #{method}") if @logger
33
+
34
+ http_sync = HTTPX.with(
35
+ timeout: {
36
+ request_timeout: 30,
37
+ connect_timeout: 10,
38
+ write_timeout: 10,
39
+ read_timeout: 30
40
+ },
41
+ headers: {
42
+ 'Content-Type' => 'application/json',
43
+ 'User-Agent' => "Telegem/#{Telegem::VERSION}"
44
+ }
45
+ )
46
+
47
+ response = http_sync.post(url, json: params.compact).wait
48
+ response.json
49
+ end
50
+ def call!(method, params = {}, &callback)
51
+ url = "#{BASE_URL}/bot#{@token}/#{method}"
52
+
53
+ session = HTTPX.plugin(:callbacks).with(
54
+ timeout: {
55
+ request_timeout: 30,
56
+ connect_timeout: 10,
57
+ write_timeout: 10,
58
+ read_timeout: 30
59
+ },
60
+ headers: {
61
+ 'Content-Type' => 'application/json',
62
+ 'User-Agent' => "Telegem/#{Telegem::VERSION}"
63
+ }
64
+ )
65
+
66
+ # Set up the callbacks BEFORE making the request
67
+ if callback
68
+ session.on_response_completed do |request, response|
69
+ begin
70
+ if response.status == 200
71
+ json = response.json
72
+ if json && json['ok']
73
+ # Success: callback with result
74
+ callback.call(json['result'], nil)
75
+ @logger.debug("API Response: #{json}") if @logger
76
+ else
77
+ # API error (non-200 OK)
78
+ error_msg = json ? json['description'] : "No JSON response"
79
+ error_code = json['error_code'] if json
80
+ callback.call(nil, APIError.new("API Error: #{error_msg}", error_code))
81
+ end
82
+ else
83
+ # HTTP error
84
+ callback.call(nil, NetworkError.new("HTTP #{response.status}"))
59
85
  end
60
-
86
+ rescue JSON::ParserError
87
+ callback.call(nil, NetworkError.new("Invalid JSON response"))
88
+ rescue => e
89
+ callback.call(nil, e)
90
+ end
91
+ end
92
+
93
+ session.on_request_error do |request, error|
94
+ # Network/connection errors
95
+ callback.call(nil, error)
96
+ end
97
+ end
98
+
99
+ # Make the request and return immediately (async)
100
+ session.post(url, json: params.compact)
101
+
102
+ # Return the session if needed (optional)
103
+ # session
104
+ end
61
105
  def upload(method, params)
62
106
  url = "#{BASE_URL}/bot#{@token}/#{method}"
63
107
 
data/lib/core/bot.rb CHANGED
@@ -30,9 +30,12 @@ module Telegem
30
30
 
31
31
  @running = false
32
32
  @offset = 0
33
+ @polling_options = options.slice(:timeout, :limit, :allowed_updates) || {}
33
34
  end
34
35
 
35
36
  def start_polling(**options)
37
+ @running = true
38
+ @polling_options = options
36
39
  Async do
37
40
  poll_loop # Now runs in Async context
38
41
  end
@@ -43,7 +46,8 @@ module Telegem
43
46
 
44
47
  @logger.info "šŸ›‘ Shutting down bot..."
45
48
  @running = false
46
- enc
49
+ sleep 0.1
50
+ end
47
51
  def running?
48
52
  @running
49
53
  end
@@ -139,19 +143,21 @@ module Telegem
139
143
 
140
144
  @logger.debug "Fetching updates with offset: #{@offset}"
141
145
 
142
- @api.call!('getUpdates', params) do |updates_array|
146
+ @api.call!('getUpdates', params) do |updates_array, error|
147
+ if error
148
+ @logger.error "Polling error: #{error.message}"
149
+ completion_callback.call(nil, error) if completion_callback
150
+ else
151
+
143
152
  # Success
144
153
  if updates_array && updates_array.is_a?(Array)
145
154
  result = { 'ok' => true, 'result' => updates_array }
146
- completion_callback.call(result) if completion_callback
155
+ completion_callback.call(result, nil) if completion_callback
147
156
  else
148
- completion_callback.call(nil) if completion_callback
157
+ completion_callback.call(nil, nil) if completion_callback
149
158
  end
150
- end.on_error do |error|
151
- # Network/HTTP error
152
- @logger.error "Polling error: #{error.message}"
153
- completion_callback.call(nil, error) if completion_callback
154
- end
159
+ end
160
+ end
155
161
  end
156
162
 
157
163
 
@@ -173,6 +179,12 @@ module Telegem
173
179
  end
174
180
 
175
181
  def process_update(update)
182
+ if update.message&.text && @logger
183
+ user = update.message.from
184
+ cmd = update.message.text.split.first
185
+ @logger.info("#{cmd} - #{user.username || user.first_name}")
186
+ end
187
+
176
188
  ctx = Context.new(update, self)
177
189
 
178
190
  begin
@@ -281,5 +293,4 @@ module Telegem
281
293
  end
282
294
  end
283
295
  end
284
- end
285
- end
296
+ end
data/lib/telegem.rb CHANGED
@@ -3,7 +3,7 @@ require 'logger'
3
3
  require 'json'
4
4
 
5
5
  module Telegem
6
- VERSION = "3.0.0".freeze
6
+ VERSION = "3.0.1".freeze
7
7
  end
8
8
 
9
9
  # Load core components