Class: Telegem::Webhook::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/webhook/server.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(bot, port: nil, host: '0.0.0.0', secret_token: nil, logger: nil, ssl: nil) ⇒ Server

Returns a new instance of Server.



15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/webhook/server.rb', line 15

def initialize(bot, port: nil, host: '0.0.0.0', secret_token: nil, logger: nil, ssl: nil)
  @bot = bot
  @port = port || ENV['PORT'] || 3000
  @host = host
  @secret_token = secret_token || ENV['WEBHOOK_SECRET_TOKEN'] || SecureRandom.hex(16)
  @logger = logger || Logger.new($stdout)
  @running = false
  @server = nil

  @ssl_mode, @ssl_context = determine_ssl_mode(ssl)
  log_configuration
  validate_ssl_setup
end

Instance Attribute Details

#botObject (readonly)

Returns the value of attribute bot.



13
14
15
# File 'lib/webhook/server.rb', line 13

def bot
  @bot
end

#hostObject (readonly)

Returns the value of attribute host.



13
14
15
# File 'lib/webhook/server.rb', line 13

def host
  @host
end

#loggerObject (readonly)

Returns the value of attribute logger.



13
14
15
# File 'lib/webhook/server.rb', line 13

def logger
  @logger
end

#portObject (readonly)

Returns the value of attribute port.



13
14
15
# File 'lib/webhook/server.rb', line 13

def port
  @port
end

#runningObject (readonly)

Returns the value of attribute running.



13
14
15
# File 'lib/webhook/server.rb', line 13

def running
  @running
end

#secret_tokenObject (readonly)

Returns the value of attribute secret_token.



13
14
15
# File 'lib/webhook/server.rb', line 13

def secret_token
  @secret_token
end

#serverObject (readonly)

Returns the value of attribute server.



13
14
15
# File 'lib/webhook/server.rb', line 13

def server
  @server
end

#ssl_modeObject (readonly)

Returns the value of attribute ssl_mode.



13
14
15
# File 'lib/webhook/server.rb', line 13

def ssl_mode
  @ssl_mode
end

Instance Method Details

#delete_webhookObject



172
173
174
175
# File 'lib/webhook/server.rb', line 172

def delete_webhook
  @bot.delete_webhook
  @logger.info("Webhook deleted")
end

#determine_ssl_mode(ssl_options) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/webhook/server.rb', line 29

def determine_ssl_mode(ssl_options)
  return [:none, nil] if ssl_options == false

  if File.exist?('.telegem-ssl')
    config = YAML.load_file('.telegem-ssl')
    cert_path = config['cert_path']
    key_path = config['key_path']

    if cert_path && key_path && File.exist?(cert_path) && File.exist?(key_path)
      return [:cli, load_certificate_files(cert_path, key_path)]
    end
  end

  if ssl_options && ssl_options[:cert_path] && ssl_options[:key_path]
    return [:manual, load_certificate_files(ssl_options[:cert_path], ssl_options[:key_path])]
  end

  if ENV['TELEGEM_WEBHOOK_URL'] && URI(ENV['TELEGEM_WEBHOOK_URL']).scheme == 'https'
    return [:cloud, nil]
  end

  [:none, nil]
end

#get_webhook_infoObject



177
178
179
# File 'lib/webhook/server.rb', line 177

def get_webhook_info
  @bot.get_webhook_info
end

#handle_request(request) ⇒ Object



106
107
108
109
110
111
112
113
114
115
# File 'lib/webhook/server.rb', line 106

def handle_request(request)
  case request.path
  when @secret_token, "/#{@secret_token}"
    handle_webhook_request(request)
  when '/health', '/healthz'
    health_endpoint(request)
  else
    [404, {}, ["Not Found"]]
  end
end

#handle_webhook_request(request) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/webhook/server.rb', line 117

def handle_webhook_request(request)
  return [405, {}, ["Method Not Allowed"]] unless request.post?

  begin
    body = request.body.read
    update_data = JSON.parse(body)
     process_webhook_update(update_data) 
    [200, {}, ["OK"]]
  rescue
    [500, {}, ["Internal Server Error"]]
  end
end

#health_endpoint(request) ⇒ Object



136
137
138
139
140
141
142
# File 'lib/webhook/server.rb', line 136

def health_endpoint(request)
  [200, { 'Content-Type' => 'application/json' }, [{
    status: 'ok',
    mode: @ssl_mode.to_s,
    ssl: @ssl_mode != :none
  }.to_json]]
end

#load_certificate_files(cert_path, key_path) ⇒ Object



53
54
55
56
57
58
59
60
# File 'lib/webhook/server.rb', line 53

def load_certificate_files(cert_path, key_path)
  ctx = OpenSSL::SSL::SSLContext.new
  ctx.cert = OpenSSL::X509::Certificate.new(File.read(cert_path))
  ctx.key = OpenSSL::PKey::RSA.new(File.read(key_path))
  ctx
rescue
  nil
end

#log_configurationObject



72
73
74
75
76
77
78
# File 'lib/webhook/server.rb', line 72

def log_configuration
  @logger.info("Webhook Server Configuration:")
  @logger.info("  Mode: #{@ssl_mode.to_s.upcase}")
  @logger.info("  Port: #{@port}")
  @logger.info("  Host: #{@host}")
  @logger.info("  Secret: #{@secret_token[0..8]}...")
end

#process_webhook_update(update_data) ⇒ Object



130
131
132
133
134
# File 'lib/webhook/server.rb', line 130

def process_webhook_update(update_data)
  @bot.process(update_data)
rescue => e
  @logger.error("Process error: #{e}")
end

#runObject



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/webhook/server.rb', line 80

def run
  return if @running
  @running = true

  case @ssl_mode
  when :cli, :manual
    endpoint = Async::HTTP::Endpoint.parse("https://#{@host}:#{@port}", ssl_context: @ssl_context)
    @logger.info("Starting HTTPS server with local certificates")
  when :cloud
    endpoint = Async::HTTP::Endpoint.parse("http://#{@host}:#{@port}")
    @logger.info("Starting HTTP server (cloud platform handles SSL)")
  else
    endpoint = Async::HTTP::Endpoint.parse("http://#{@host}:#{@port}")
    @logger.warn("Starting HTTP server (Telegram requires HTTPS)")
  end

  @server = Async::HTTP::Server.for(endpoint) do |request|
    handle_request(request)
  end

  Async do |task|
    @server.run
    task.sleep while @running
  end
end

#running?Boolean

Returns:

  • (Boolean)


181
182
183
# File 'lib/webhook/server.rb', line 181

def running?
  @running
end

#set_webhook(**options) ⇒ Object



164
165
166
167
168
169
170
# File 'lib/webhook/server.rb', line 164

def set_webhook(**options)
  url = webhook_url
  params = { url: url }.merge(options)
  @bot.set_webhook(**params)
  @logger.info("Webhook set to: #{url}")
  url
end

#stopObject



144
145
146
147
148
149
150
# File 'lib/webhook/server.rb', line 144

def stop
  return unless @running
  @running = false
  @server&.close
  @logger.info("Server stopped")
  @server = nil
end

#validate_ssl_setupObject



62
63
64
65
66
67
68
69
70
# File 'lib/webhook/server.rb', line 62

def validate_ssl_setup
  case @ssl_mode
  when :cli, :manual
    raise "SSL certificate files not found or invalid" if @ssl_context.nil?
  when :cloud
    url = URI(ENV['TELEGEM_WEBHOOK_URL'])
    raise "TELEGEM_WEBHOOK_URL must be HTTPS" unless url.scheme == 'https'
  end
end

#webhook_urlObject



152
153
154
155
156
157
158
159
160
161
162
# File 'lib/webhook/server.rb', line 152

def webhook_url
  case @ssl_mode
  when :cli, :manual
    "https://#{@host}:#{@port}#{@secret_token}"
  when :cloud
    cloud_url = ENV['TELEGEM_WEBHOOK_URL'].chomp('/')
    "#{cloud_url}#{@secret_token}"
  else
    "http://#{@host}:#{@port}#{@secret_token}"
  end
end