telegem 3.0.0 → 3.0.2
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/bin/.gitkeep +0 -0
- data/bin/telegem-ssl +44 -0
- data/docs-src/webhook.md +341 -0
- data/lib/api/client.rb +74 -30
- data/lib/core/bot.rb +22 -11
- data/lib/telegem.rb +1 -1
- data/lib/webhook/server.rb +128 -72
- data/public/.gitkeep +0 -0
- data/public/index.html +481 -0
- metadata +16 -9
- data/setup.sh +0 -30
data/lib/webhook/server.rb
CHANGED
|
@@ -1,110 +1,128 @@
|
|
|
1
|
+
# lib/telegem/webhook/server.rb
|
|
1
2
|
require 'async/http/server'
|
|
2
3
|
require 'async/http/endpoint'
|
|
4
|
+
require 'openssl'
|
|
5
|
+
require 'yaml'
|
|
3
6
|
require 'json'
|
|
4
|
-
require '
|
|
7
|
+
require 'securerandom'
|
|
8
|
+
require 'uri'
|
|
5
9
|
|
|
6
10
|
module Telegem
|
|
7
11
|
module Webhook
|
|
8
12
|
class Server
|
|
9
|
-
attr_reader :bot, :port, :host, :logger, :secret_token, :running, :server
|
|
13
|
+
attr_reader :bot, :port, :host, :logger, :secret_token, :running, :server, :ssl_mode
|
|
10
14
|
|
|
11
|
-
def initialize(bot, port: nil, host: '0.0.0.0', secret_token: nil, logger: nil)
|
|
15
|
+
def initialize(bot, port: nil, host: '0.0.0.0', secret_token: nil, logger: nil, ssl: nil)
|
|
12
16
|
@bot = bot
|
|
13
|
-
@port = port || 3000
|
|
17
|
+
@port = port || ENV['PORT'] || 3000
|
|
14
18
|
@host = host
|
|
15
|
-
@secret_token = secret_token
|
|
19
|
+
@secret_token = secret_token || ENV['WEBHOOK_SECRET_TOKEN'] || SecureRandom.hex(16)
|
|
16
20
|
@logger = logger || Logger.new($stdout)
|
|
17
21
|
@running = false
|
|
18
22
|
@server = nil
|
|
19
|
-
end
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
@ssl_mode, @ssl_context = determine_ssl_mode(ssl)
|
|
25
|
+
log_configuration
|
|
26
|
+
validate_ssl_setup
|
|
27
|
+
end
|
|
23
28
|
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
def determine_ssl_mode(ssl_options)
|
|
30
|
+
return [:none, nil] if ssl_options == false
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
if File.exist?('.telegem-ssl')
|
|
33
|
+
config = YAML.load_file('.telegem-ssl')
|
|
34
|
+
cert_path = config['cert_path']
|
|
35
|
+
key_path = config['key_path']
|
|
28
36
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
when '/webhook'
|
|
32
|
-
handle_webhook_request(request)
|
|
33
|
-
when '/health'
|
|
34
|
-
health_endpoint(request)
|
|
35
|
-
else
|
|
36
|
-
[404, {}, ["Not Found"]]
|
|
37
|
+
if cert_path && key_path && File.exist?(cert_path) && File.exist?(key_path)
|
|
38
|
+
return [:cli, load_certificate_files(cert_path, key_path)]
|
|
37
39
|
end
|
|
38
40
|
end
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
task.sleep while @running
|
|
42
|
+
if ssl_options && ssl_options[:cert_path] && ssl_options[:key_path]
|
|
43
|
+
return [:manual, load_certificate_files(ssl_options[:cert_path], ssl_options[:key_path])]
|
|
43
44
|
end
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
if ENV['TELEGEM_WEBHOOK_URL'] && URI(ENV['TELEGEM_WEBHOOK_URL']).scheme == 'https'
|
|
47
|
+
return [:cloud, nil]
|
|
48
|
+
end
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
[:none, nil]
|
|
51
|
+
end
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
def load_certificate_files(cert_path, key_path)
|
|
54
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
|
55
|
+
ctx.cert = OpenSSL::X509::Certificate.new(File.read(cert_path))
|
|
56
|
+
ctx.key = OpenSSL::PKey::RSA.new(File.read(key_path))
|
|
57
|
+
ctx
|
|
58
|
+
rescue
|
|
59
|
+
nil
|
|
55
60
|
end
|
|
56
61
|
|
|
57
|
-
def
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
def validate_ssl_setup
|
|
63
|
+
case @ssl_mode
|
|
64
|
+
when :cli, :manual
|
|
65
|
+
raise "SSL certificate files not found or invalid" if @ssl_context.nil?
|
|
66
|
+
when :cloud
|
|
67
|
+
url = URI(ENV['TELEGEM_WEBHOOK_URL'])
|
|
68
|
+
raise "TELEGEM_WEBHOOK_URL must be HTTPS" unless url.scheme == 'https'
|
|
62
69
|
end
|
|
63
70
|
end
|
|
64
71
|
|
|
65
|
-
def
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
@bot.set_webhook(**params)
|
|
72
|
-
@logger.info "Webhook set to #{url}"
|
|
72
|
+
def log_configuration
|
|
73
|
+
@logger.info("Webhook Server Configuration:")
|
|
74
|
+
@logger.info(" Mode: #{@ssl_mode.to_s.upcase}")
|
|
75
|
+
@logger.info(" Port: #{@port}")
|
|
76
|
+
@logger.info(" Host: #{@host}")
|
|
77
|
+
@logger.info(" Secret: #{@secret_token[0..8]}...")
|
|
73
78
|
end
|
|
74
79
|
|
|
75
|
-
def
|
|
76
|
-
@
|
|
77
|
-
@
|
|
78
|
-
end
|
|
80
|
+
def run
|
|
81
|
+
return if @running
|
|
82
|
+
@running = true
|
|
79
83
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
case @ssl_mode
|
|
85
|
+
when :cli, :manual
|
|
86
|
+
endpoint = Async::HTTP::Endpoint.parse("https://#{@host}:#{@port}", ssl_context: @ssl_context)
|
|
87
|
+
@logger.info("Starting HTTPS server with local certificates")
|
|
88
|
+
when :cloud
|
|
89
|
+
endpoint = Async::HTTP::Endpoint.parse("http://#{@host}:#{@port}")
|
|
90
|
+
@logger.info("Starting HTTP server (cloud platform handles SSL)")
|
|
91
|
+
else
|
|
92
|
+
endpoint = Async::HTTP::Endpoint.parse("http://#{@host}:#{@port}")
|
|
93
|
+
@logger.warn("Starting HTTP server (Telegram requires HTTPS)")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
@server = Async::HTTP::Server.for(endpoint) do |request|
|
|
97
|
+
handle_request(request)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
Async do |task|
|
|
101
|
+
@server.run
|
|
102
|
+
task.sleep while @running
|
|
103
|
+
end
|
|
84
104
|
end
|
|
85
105
|
|
|
86
|
-
|
|
106
|
+
def handle_request(request)
|
|
107
|
+
case request.path
|
|
108
|
+
when @secret_token, "/#{@secret_token}"
|
|
109
|
+
handle_webhook_request(request)
|
|
110
|
+
when '/health', '/healthz'
|
|
111
|
+
health_endpoint(request)
|
|
112
|
+
else
|
|
113
|
+
[404, {}, ["Not Found"]]
|
|
114
|
+
end
|
|
115
|
+
end
|
|
87
116
|
|
|
88
117
|
def handle_webhook_request(request)
|
|
89
118
|
return [405, {}, ["Method Not Allowed"]] unless request.post?
|
|
90
119
|
|
|
91
|
-
unless validate_secret_token(request)
|
|
92
|
-
return [403, {}, ["Forbidden"]]
|
|
93
|
-
end
|
|
94
|
-
|
|
95
120
|
begin
|
|
96
121
|
body = request.body.read
|
|
97
122
|
update_data = JSON.parse(body)
|
|
98
|
-
|
|
99
|
-
Async do |task|
|
|
100
|
-
process_webhook_update(update_data)
|
|
101
|
-
end
|
|
102
|
-
|
|
123
|
+
Async { process_webhook_update(update_data) }
|
|
103
124
|
[200, {}, ["OK"]]
|
|
104
|
-
rescue
|
|
105
|
-
[400, {}, ["Bad Request"]]
|
|
106
|
-
rescue => e
|
|
107
|
-
@logger.error "Webhook error: #{e.message}"
|
|
125
|
+
rescue
|
|
108
126
|
[500, {}, ["Internal Server Error"]]
|
|
109
127
|
end
|
|
110
128
|
end
|
|
@@ -112,18 +130,56 @@ module Telegem
|
|
|
112
130
|
def process_webhook_update(update_data)
|
|
113
131
|
@bot.process(update_data)
|
|
114
132
|
rescue => e
|
|
115
|
-
@logger.error
|
|
133
|
+
@logger.error("Process error: #{e}")
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def health_endpoint(request)
|
|
137
|
+
[200, { 'Content-Type' => 'application/json' }, [{
|
|
138
|
+
status: 'ok',
|
|
139
|
+
mode: @ssl_mode.to_s,
|
|
140
|
+
ssl: @ssl_mode != :none
|
|
141
|
+
}.to_json]]
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def stop
|
|
145
|
+
return unless @running
|
|
146
|
+
@running = false
|
|
147
|
+
@server&.close
|
|
148
|
+
@logger.info("Server stopped")
|
|
149
|
+
@server = nil
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def webhook_url
|
|
153
|
+
case @ssl_mode
|
|
154
|
+
when :cli, :manual
|
|
155
|
+
"https://#{@host}:#{@port}#{@secret_token}"
|
|
156
|
+
when :cloud
|
|
157
|
+
cloud_url = ENV['TELEGEM_WEBHOOK_URL'].chomp('/')
|
|
158
|
+
"#{cloud_url}#{@secret_token}"
|
|
159
|
+
else
|
|
160
|
+
"http://#{@host}:#{@port}#{@secret_token}"
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def set_webhook(**options)
|
|
165
|
+
url = webhook_url
|
|
166
|
+
params = { url: url }.merge(options)
|
|
167
|
+
@bot.set_webhook(**params)
|
|
168
|
+
@logger.info("Webhook set to: #{url}")
|
|
169
|
+
url
|
|
116
170
|
end
|
|
117
171
|
|
|
118
|
-
def
|
|
119
|
-
|
|
172
|
+
def delete_webhook
|
|
173
|
+
@bot.delete_webhook
|
|
174
|
+
@logger.info("Webhook deleted")
|
|
175
|
+
end
|
|
120
176
|
|
|
121
|
-
|
|
122
|
-
|
|
177
|
+
def get_webhook_info
|
|
178
|
+
@bot.get_webhook_info
|
|
123
179
|
end
|
|
124
180
|
|
|
125
|
-
def
|
|
126
|
-
|
|
181
|
+
def running?
|
|
182
|
+
@running
|
|
127
183
|
end
|
|
128
184
|
end
|
|
129
185
|
end
|
data/public/.gitkeep
ADDED
|
File without changes
|