telegem 2.0.9 → 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.
@@ -1,326 +1,129 @@
1
- # lib/webhook/server.rb - V2.0.0 (CLOUD-READY)
1
+ require 'async/http/server'
2
+ require 'async/http/endpoint'
2
3
  require 'json'
3
- require 'rack'
4
+ require 'logger'
4
5
 
5
6
  module Telegem
6
7
  module Webhook
7
8
  class Server
8
- attr_reader :bot, :port, :host, :logger, :running, :secret_token
9
-
10
- def initialize(bot, port: nil, host: '0.0.0.0', logger: nil, max_threads: 10, secret_token: nil)
9
+ attr_reader :bot, :port, :host, :logger, :secret_token, :running, :server
10
+
11
+ def initialize(bot, port: nil, host: '0.0.0.0', secret_token: nil, logger: nil)
11
12
  @bot = bot
12
- @port = port || ENV['PORT'] || 3000
13
+ @port = port || 3000
13
14
  @host = host
15
+ @secret_token = secret_token
14
16
  @logger = logger || Logger.new($stdout)
15
- @max_threads = max_threads
16
- @thread_pool = Concurrent::FixedThreadPool.new(max_threads)
17
- @secret_token = secret_token || generate_secret_token
18
- @server = nil
19
17
  @running = false
20
- @webhook_path = "/webhook"
18
+ @server = nil
21
19
  end
22
-
23
- # Production server with Puma
20
+
24
21
  def run
25
22
  return if @running
26
-
27
- @logger.info "🚀 Starting Telegem webhook server on #{@host}:#{@port}"
28
- @logger.info "📝 Set Telegram webhook to: #{webhook_url}"
29
- @logger.info "🔐 Secret token: #{@secret_token}"
30
- @logger.info "✅ Using Puma (production-ready)"
31
- @logger.info "☁️ Cloud-ready: #{cloud_platform}"
32
-
23
+
33
24
  @running = true
34
-
35
- app = build_rack_app
36
- start_puma_server(app)
37
-
38
- self
25
+ @logger.info "Starting webhook server on #{@host}:#{@port}"
26
+
27
+ endpoint = Async::HTTP::Endpoint.parse("http://#{@host}:#{@port}")
28
+
29
+ @server = Async::HTTP::Server.for(endpoint) do |request|
30
+ case request.path
31
+ when '/webhook'
32
+ handle_webhook_request(request)
33
+ when '/health'
34
+ health_endpoint(request)
35
+ else
36
+ [404, {}, ["Not Found"]]
37
+ end
38
+ end
39
+
40
+ Async do |task|
41
+ @server.run
42
+ task.sleep while @running
43
+ end
44
+
45
+ @logger.info "Webhook server running on http://#{@host}:#{@port}"
39
46
  end
40
-
47
+
41
48
  def stop
42
49
  return unless @running
43
-
44
- @logger.info "🛑 Stopping webhook server..."
50
+
45
51
  @running = false
46
-
47
- @server&.stop
48
- @thread_pool.shutdown
49
- @thread_pool.wait_for_termination(5)
50
-
51
- @logger.info "✅ Webhook server stopped"
52
- end
53
-
54
- alias_method :shutdown, :stop
55
-
56
- def running?
57
- @running
52
+ @server&.close
53
+ @logger.info "Webhook server stopped"
54
+ @server = nil
58
55
  end
59
-
56
+
60
57
  def webhook_url
61
- # For cloud platforms, use their URL
62
- if cloud_url = detect_cloud_url
63
- "#{cloud_url}#{@webhook_path}"
64
- elsif ENV['WEBHOOK_URL']
65
- ENV['WEBHOOK_URL']
58
+ if @secret_token
59
+ "https://#{@host}:#{@port}/webhook?secret_token=#{@secret_token}"
66
60
  else
67
- # Local development
68
- "https://#{@host}:#{@port}#{@webhook_path}"
61
+ "https://#{@host}:#{@port}/webhook"
69
62
  end
70
63
  end
71
-
64
+
72
65
  def set_webhook(**options)
73
- params = {
74
- url: webhook_url,
75
- secret_token: @secret_token,
76
- drop_pending_updates: true
77
- }.merge(options)
78
-
79
- @bot.api.call!('setWebhook', params)
66
+ url = webhook_url
67
+ params = { url: url }
68
+ params[:secret_token] = @secret_token if @secret_token
69
+ params.merge!(options)
70
+
71
+ @bot.set_webhook(**params)
72
+ @logger.info "Webhook set to #{url}"
80
73
  end
81
-
74
+
82
75
  def delete_webhook
83
- @bot.api.call!('deleteWebhook', {})
76
+ @bot.delete_webhook
77
+ @logger.info "Webhook deleted"
84
78
  end
85
-
86
- # Quick setup helper
87
- def self.setup(bot, **options)
88
- server = new(bot, **options)
89
- server.run
90
- server.set_webhook
91
- server
79
+
80
+ def get_webhook_info
81
+ info = @bot.get_webhook_info
82
+ @logger.info "Webhook info retrieved"
83
+ info
92
84
  end
93
-
85
+
94
86
  private
95
-
96
- def build_rack_app
97
- Rack::Builder.new do
98
- use Rack::CommonLogger
99
- use Rack::ShowExceptions if ENV['RACK_ENV'] == 'development'
100
-
101
- # Health endpoint (required by Render/Railway/Heroku)
102
- map "/health" do
103
- run ->(env) {
104
- [200, {
105
- 'Content-Type' => 'application/json',
106
- 'Cache-Control' => 'no-cache'
107
- }, [{
108
- status: 'healthy',
109
- timestamp: Time.now.iso8601,
110
- service: 'telegem-webhook',
111
- version: Telegem::VERSION
112
- }.to_json]]
113
- }
114
- end
115
-
116
- # Webhook endpoint
117
- map "/webhook" do
118
- run ->(env) do
119
- server = env['telegem.server']
120
- req = Rack::Request.new(env)
121
-
122
- if req.post?
123
- server.handle_rack_request(req)
124
- else
125
- [405, { 'Content-Type' => 'text/plain' }, ['Method Not Allowed']]
126
- end
127
- end
128
- end
129
-
130
- # Root landing page
131
- map "/" do
132
- run ->(env) do
133
- server = env['telegem.server']
134
- [200, { 'Content-Type' => 'text/html' }, [server.landing_page]]
135
- end
136
- end
137
- end
138
- end
139
-
140
- def start_puma_server(app)
141
- require 'puma'
142
-
143
- # Cloud platforms set PORT environment variable
144
- port = ENV['PORT'] || @port
145
-
146
- config = {
147
- Host: @host,
148
- Port: port,
149
- Threads: "0:#{@max_threads}",
150
- workers: ENV['WEB_CONCURRENCY']&.to_i || 1,
151
- daemonize: false,
152
- silent: false,
153
- environment: ENV['RACK_ENV'] || 'production',
154
- # Cloud platforms handle SSL termination
155
- ssl_bind: nil # Let platform handle SSL
156
- }
157
-
158
- @server = Puma::Server.new(app)
159
- @server.add_tcp_listener(@host, port)
160
-
161
- @server.app = ->(env) do
162
- env['telegem.server'] = self
163
- app.call(env)
164
- end
165
-
166
- Thread.new do
167
- begin
168
- @server.run
169
- rescue => e
170
- @logger.error "❌ Puma server error: #{e.message}"
171
- @running = false
172
- raise
173
- end
174
- end
175
-
176
- sleep 1 until @server.running
177
- end
178
-
179
- def handle_rack_request(req)
180
- # Validate secret token from Telegram
181
- telegram_token = req.get_header('X-Telegram-Bot-Api-Secret-Token')
182
-
183
- if @secret_token && telegram_token != @secret_token
184
- @logger.warn "⚠️ Invalid secret token from #{req.ip}"
185
- return [403, { 'Content-Type' => 'text/plain' }, ['Forbidden']]
87
+
88
+ def handle_webhook_request(request)
89
+ return [405, {}, ["Method Not Allowed"]] unless request.post?
90
+
91
+ unless validate_secret_token(request)
92
+ return [403, {}, ["Forbidden"]]
186
93
  end
187
-
94
+
188
95
  begin
189
- body = req.body.read
96
+ body = request.body.read
190
97
  update_data = JSON.parse(body)
191
-
192
- # Process in thread pool (not spawn new threads!)
193
- @thread_pool.post do
194
- begin
195
- @bot.process(update_data)
196
- rescue => e
197
- @logger.error "Error processing update: #{e.message}"
198
- end
98
+
99
+ Async do |task|
100
+ process_webhook_update(update_data)
199
101
  end
200
-
201
- [200, { 'Content-Type' => 'text/plain' }, ['OK']]
102
+
103
+ [200, {}, ["OK"]]
202
104
  rescue JSON::ParserError
203
- [400, { 'Content-Type' => 'text/plain' }, ['Bad Request']]
105
+ [400, {}, ["Bad Request"]]
204
106
  rescue => e
205
107
  @logger.error "Webhook error: #{e.message}"
206
- [500, { 'Content-Type' => 'text/plain' }, ['Internal Server Error']]
108
+ [500, {}, ["Internal Server Error"]]
207
109
  end
208
110
  end
209
-
210
- def landing_page
211
- <<~HTML
212
- <!DOCTYPE html>
213
- <html>
214
- <head>
215
- <title>Telegem Webhook Server</title>
216
- <style>
217
- body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
218
- max-width: 800px; margin: 0 auto; padding: 20px; }
219
- .status { background: #4CAF50; color: white; padding: 5px 10px; border-radius: 3px; }
220
- code { background: #f5f5f5; padding: 2px 5px; border-radius: 3px; }
221
- a { color: #2196F3; text-decoration: none; }
222
- </style>
223
- </head>
224
- <body>
225
- <h1>🤖 Telegem Webhook Server</h1>
226
- <p><strong>Status:</strong> <span class="status">Running</span></p>
227
- <p><strong>Webhook URL:</strong> <code>#{webhook_url}</code></p>
228
- <p><strong>Health Check:</strong> <a href="/health">/health</a></p>
229
- <p><strong>Platform:</strong> #{cloud_platform}</p>
230
- <hr>
231
- <p>To set up your Telegram bot:</p>
232
- <pre><code>bot = Telegem.new('YOUR_TOKEN')
233
- server = bot.webhook
234
- server.run
235
- server.set_webhook</code></pre>
236
- </body>
237
- </html>
238
- HTML
239
- end
240
-
241
- def generate_secret_token
242
- SecureRandom.hex(32)
243
- end
244
-
245
- def detect_cloud_url
246
- # Render
247
- if ENV['RENDER_EXTERNAL_URL']
248
- ENV['RENDER_EXTERNAL_URL']
249
- # Railway
250
- elsif ENV['RAILWAY_STATIC_URL']
251
- ENV['RAILWAY_STATIC_URL']
252
- # Heroku
253
- elsif ENV['HEROKU_APP_NAME']
254
- "https://#{ENV['HEROKU_APP_NAME']}.herokuapp.com"
255
- # Fly.io
256
- elsif ENV['FLY_APP_NAME']
257
- "https://#{ENV['FLY_APP_NAME']}.fly.dev"
258
- # Vercel (if using serverless)
259
- elsif ENV['VERCEL_URL']
260
- "https://#{ENV['VERCEL_URL']}"
261
- else
262
- nil
263
- end
264
- end
265
-
266
- def cloud_platform
267
- if ENV['RENDER'] then 'Render'
268
- elsif ENV['RAILWAY'] then 'Railway'
269
- elsif ENV['HEROKU_APP_NAME'] then 'Heroku'
270
- elsif ENV['FLY_APP_NAME'] then 'Fly.io'
271
- elsif ENV['VERCEL'] then 'Vercel'
272
- elsif ENV['DYNO'] then 'Heroku'
273
- else 'Local/Unknown'
274
- end
275
- end
276
- end
277
-
278
- # Rack Middleware for existing apps
279
- class Middleware
280
- def initialize(app, bot, secret_token: nil)
281
- @app = app
282
- @bot = bot
283
- @secret_token = secret_token || ENV['TELEGRAM_SECRET_TOKEN']
284
- @thread_pool = Concurrent::FixedThreadPool.new(10)
111
+
112
+ def process_webhook_update(update_data)
113
+ @bot.process(update_data)
114
+ rescue => e
115
+ @logger.error "Error processing update: #{e.message}"
285
116
  end
286
-
287
- def call(env)
288
- req = Rack::Request.new(env)
289
-
290
- if req.post? && req.path == "/webhook"
291
- handle_webhook(req)
292
- else
293
- @app.call(env)
294
- end
117
+
118
+ def validate_secret_token(request)
119
+ return true unless @secret_token
120
+
121
+ token = request.query['secret_token'] || request.headers['x-telegram-bot-api-secret-token']
122
+ token == @secret_token
295
123
  end
296
-
297
- private
298
-
299
- def handle_webhook(req)
300
- telegram_token = req.get_header('X-Telegram-Bot-Api-Secret-Token')
301
-
302
- if @secret_token && telegram_token != @secret_token
303
- return [403, { 'Content-Type' => 'text/plain' }, ['Forbidden']]
304
- end
305
-
306
- begin
307
- update_data = JSON.parse(req.body.read)
308
-
309
- @thread_pool.post do
310
- begin
311
- @bot.process(update_data)
312
- rescue => e
313
- @bot.logger&.error("Webhook error: #{e.message}")
314
- end
315
- end
316
-
317
- [200, { 'Content-Type' => 'text/plain' }, ['OK']]
318
- rescue JSON::ParserError
319
- [400, { 'Content-Type' => 'text/plain' }, ['Bad Request']]
320
- rescue => e
321
- @bot.logger&.error("Webhook error: #{e.message}")
322
- [500, { 'Content-Type' => 'text/plain' }, ['Internal Server Error']]
323
- end
124
+
125
+ def health_endpoint(request)
126
+ [200, { 'Content-Type' => 'application/json' }, [{ status: 'ok' }.to_json]]
324
127
  end
325
128
  end
326
129
  end
data/setup.sh ADDED
@@ -0,0 +1,30 @@
1
+ #!/bin/bash
2
+ # Clean Git history and push fresh
3
+
4
+ echo "🧹 Cleaning Git history..."
5
+ echo "⚠️ This removes ALL Git history but keeps your code"
6
+
7
+ # Remove .git directory
8
+ rm -rf .git
9
+
10
+ # Reinitialize fresh Git
11
+ git init
12
+
13
+ # Add your files
14
+ git add .
15
+
16
+ # Commit
17
+ git commit -m "Telegem v3.0.0
18
+ - Major rewrite
19
+ -introduced async
20
+ - removed thread_poll
21
+ "
22
+
23
+ # Set GitLab remote
24
+ git remote add origin https://oauth2:glpat-31Bznn8ioRy85WZ5lDwGJ286MQp1Omd5aHc5Cw.01.1217avfnk@gitlab.com/ruby-telegem/telegem.git
25
+
26
+ # Force push (since new repo)
27
+ git push -u origin main --force
28
+
29
+ echo "✅ Fresh push complete!"
30
+ echo "🌐 View at: https://gitlab.com/ruby-telegem/telegem.git"
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: 2.0.9
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - sick_phantom
@@ -77,6 +77,20 @@ dependencies:
77
77
  - - "<"
78
78
  - !ruby/object:Gem::Version
79
79
  version: '7.0'
80
+ - !ruby/object:Gem::Dependency
81
+ name: async
82
+ requirement: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - "~>"
85
+ - !ruby/object:Gem::Version
86
+ version: '2.1'
87
+ type: :runtime
88
+ prerelease: false
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - "~>"
92
+ - !ruby/object:Gem::Version
93
+ version: '2.1'
80
94
  - !ruby/object:Gem::Dependency
81
95
  name: rake
82
96
  requirement: !ruby/object:Gem::Requirement
@@ -163,31 +177,17 @@ files:
163
177
  - Gemfile.lock
164
178
  - LICENSE
165
179
  - Readme.md
166
- - Test-Projects/.gitkeep
167
- - Test-Projects/Movie-tracker-bot/.gitkeep
168
- - Test-Projects/Movie-tracker-bot/Gemfile
169
- - Test-Projects/Movie-tracker-bot/bot.rb
170
- - Test-Projects/Movie-tracker-bot/handlers/.gitkeep
171
- - Test-Projects/Movie-tracker-bot/handlers/add_1_.rb
172
- - Test-Projects/Movie-tracker-bot/handlers/add_2_.rb
173
- - Test-Projects/Movie-tracker-bot/handlers/premium.rb
174
- - Test-Projects/Movie-tracker-bot/handlers/report.rb
175
- - Test-Projects/Movie-tracker-bot/handlers/search.rb
176
- - Test-Projects/Movie-tracker-bot/handlers/sponsor.rb
177
- - Test-Projects/Movie-tracker-bot/handlers/start.rb
178
- - Test-Projects/Movie-tracker-bot/handlers/watch.rb
179
- - Test-Projects/Test-submitted-by-marvel/.gitkeep
180
- - Test-Projects/Test-submitted-by-marvel/Marvel-bot.md
181
- - Test-Projects/bot_test1.rb
182
- - Test-Projects/pizza_test_bot_guide.md
180
+ - Starts_HallofFame.md
181
+ - Test-Projects/bot.md
182
+ - Test-Projects/bot1.rb
183
183
  - docs-src/.gitkeep
184
184
  - docs-src/Bot-registration_.PNG
185
185
  - docs-src/bot.md
186
186
  - docs-src/context|ctx|.md
187
+ - docs-src/ctx.md
187
188
  - docs-src/getting-started.md
188
189
  - docs-src/keyboard_inline.md
189
190
  - docs-src/scene.md
190
- - docs-src/understanding-ctx.md
191
191
  - lib/api/client.rb
192
192
  - lib/api/types.rb
193
193
  - lib/core/bot.rb
@@ -201,6 +201,7 @@ files:
201
201
  - lib/telegem.rb
202
202
  - lib/webhook/.gitkeep
203
203
  - lib/webhook/server.rb
204
+ - setup.sh
204
205
  homepage: https://gitlab.com/ruby-telegem/telegem
205
206
  licenses:
206
207
  - MIT
@@ -211,7 +212,7 @@ metadata:
211
212
  bug_tracker_uri: https://gitlab.com/ruby-telegem/telegem/-/issues
212
213
  documentation_uri: https://gitlab.com/ruby-telegem/telegem/-/tree/main/docs-src?ref_type=heads
213
214
  rubygems_mfa_required: 'false'
214
- post_install_message: "Thanks for installing Telegem 2.0.9!\nDocumentation: https://gitlab.com/ruby-telegem/telegem\nHappy
215
+ post_install_message: "Thanks for installing Telegem 3.0.0!\nDocumentation: https://gitlab.com/ruby-telegem/telegem\nHappy
215
216
  bot building! \U0001F916\n"
216
217
  rdoc_options: []
217
218
  require_paths:
File without changes
File without changes
@@ -1,2 +0,0 @@
1
-
2
- ruby "3.1.0"
@@ -1,62 +0,0 @@
1
- require 'json'
2
- require 'telegem'
3
- require 'sqlite3'
4
- require 'fileutils'
5
-
6
-
7
- require_relative './handlers/start'
8
- require_relative './handlers/add_1_'
9
- require_relative './handlers/premium'
10
- require_relative './handlers/add_2_'
11
- require_relative './handlers/sponsor'
12
- require_relative './handlers/watch'
13
- require_relative './handlers/report'
14
- require_relative './handlers/search'
15
-
16
- bot_token = ENV['BOT_TOKEN']
17
-
18
- bot = Telegem.new(ENV['BOT_TOKEN'])
19
- puts "token is #{bot_token[0..10]}"
20
- # In your code:
21
- db = SQLite3::Database.new "movieflix.db"
22
-
23
- puts "started sqlite"
24
-
25
- StartHandler.new(bot, db)
26
- SearchHandler.new(bot, db)
27
- WatchHandler.new(bot, db)
28
- HelpHandler.new(bot)
29
- PremiumHandler.new(bot)
30
- SponsorHandler.new(bot)
31
- AddHandler.new(bot, db)
32
- AddHears.new(bot, db)
33
-
34
- port = (ENV['PORT'] || 3000).to_i
35
- puts " 📍 Using port: #{port}"
36
- puts " 🌐 Bind host: 0.0.0.0"
37
-
38
- # Start polling
39
- bot.start_polling
40
- puts " started polling"
41
-
42
- # Minimal health check server
43
- Thread.new do
44
- require 'socket'
45
- server = TCPServer.new('0.0.0.0', port || 10000)
46
- puts "✅ Health check server listening on port #{ENV['PORT'] || 10000}"
47
-
48
- loop do
49
- client = server.accept
50
- request = client.gets
51
-
52
- # Simple response for any request
53
- client.puts "HTTP/1.1 200 OK\r\n"
54
- client.puts "Content-Type: text/plain\r\n"
55
- client.puts "\r\n"
56
- client.puts "Telegram Bot is running"
57
- client.close
58
- end
59
- puts "bot is running "
60
- end
61
- sleep
62
- puts "bot is running alive now "
File without changes