mirror of
https://github.com/tucnak/telebot
synced 2024-11-05 06:00:58 +00:00
151 lines
4.1 KiB
Go
151 lines
4.1 KiB
Go
package telebot
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
)
|
|
|
|
// A WebhookTLS specifies the path to a key and a cert so the poller can open
|
|
// a TLS listener
|
|
type WebhookTLS struct {
|
|
Key string
|
|
Cert string
|
|
}
|
|
|
|
// A WebhookEndpoint describes the endpoint to which telegram will send its requests.
|
|
// This must be a public URL and can be a loadbalancer or something similar. If the
|
|
// endpoint uses TLS and the certificate is selfsigned you have to add the certificate
|
|
// path of this certificate so telegram will trust it. This field can be ignored if you
|
|
// have a trusted certifcate (letsencrypt, ...).
|
|
type WebhookEndpoint struct {
|
|
PublicURL string
|
|
Cert string
|
|
}
|
|
|
|
// A Webhook configures the poller for webhooks. It opens a port on the given
|
|
// listen adress. If TLS is filled, the listener will use the key and cert to open
|
|
// a secure port. Otherwise it will use plain HTTP.
|
|
// If you have a loadbalancer ore other infrastructure in front of your service, you
|
|
// must fill the Endpoint structure so this poller will send this data to telegram. If
|
|
// you leave these values empty, your local adress will be sent to telegram which is mostly
|
|
// not what you want (at least while developing). If you have a single instance of your
|
|
// bot you should consider to use the LongPoller instead of a WebHook.
|
|
// You can also leave the Listen field empty. In this case it is up to the caller to
|
|
// add the Webhook to a http-mux.
|
|
type Webhook struct {
|
|
Listen string
|
|
TLS *WebhookTLS
|
|
Endpoint *WebhookEndpoint
|
|
dest chan<- Update
|
|
bot *Bot
|
|
}
|
|
|
|
type registerResult struct {
|
|
Ok bool `json:"ok"`
|
|
ErrorCode int `json:"error_code"`
|
|
Description string `json:"description"`
|
|
}
|
|
|
|
func (h *Webhook) getFiles() map[string]File {
|
|
m := make(map[string]File)
|
|
|
|
if h.TLS != nil {
|
|
m["certificate"] = FromDisk(h.TLS.Cert)
|
|
}
|
|
// check if it is overwritten by an endpoint
|
|
if h.Endpoint != nil {
|
|
if h.Endpoint.Cert == "" {
|
|
// this can be the case if there is a loadbalancer or reverseproxy in
|
|
// front with a public cert. in this case we do not need to upload it
|
|
// to telegram. we delete the certificate from the map, because someone
|
|
// can have an internal TLS listener with a private cert
|
|
delete(m, "certificate")
|
|
} else {
|
|
// someone configured a certificate
|
|
m["certificate"] = FromDisk(h.Endpoint.Cert)
|
|
}
|
|
}
|
|
return m
|
|
}
|
|
|
|
func (h *Webhook) getParams() map[string]string {
|
|
param := make(map[string]string)
|
|
if h.TLS != nil {
|
|
param["url"] = "https://" + h.Listen
|
|
} else {
|
|
// this will not work with telegram, they want TLS
|
|
// but i allow this because telegram will send an error
|
|
// when you register this hook. in their docs they write
|
|
// that port 80/http is allowed ...
|
|
param["url"] = "http://" + h.Listen
|
|
}
|
|
if h.Endpoint != nil {
|
|
param["url"] = h.Endpoint.PublicURL
|
|
}
|
|
return param
|
|
}
|
|
|
|
func (h *Webhook) Poll(b *Bot, dest chan Update, stop chan struct{}) {
|
|
res, err := b.sendFiles("setWebhook", h.getFiles(), h.getParams())
|
|
if err != nil {
|
|
b.debug(fmt.Errorf("setWebhook failed %q: %v", string(res), err))
|
|
close(stop)
|
|
return
|
|
}
|
|
var result registerResult
|
|
err = json.Unmarshal(res, &result)
|
|
if err != nil {
|
|
b.debug(fmt.Errorf("bad json data %q: %v", string(res), err))
|
|
close(stop)
|
|
return
|
|
}
|
|
if !result.Ok {
|
|
b.debug(fmt.Errorf("cannot register webhook: %s", result.Description))
|
|
close(stop)
|
|
return
|
|
}
|
|
// store the variables so the HTTP-handler can use 'em
|
|
h.dest = dest
|
|
h.bot = b
|
|
|
|
if h.Listen == "" {
|
|
h.waitForStop(stop)
|
|
return
|
|
}
|
|
|
|
s := &http.Server{
|
|
Addr: h.Listen,
|
|
Handler: h,
|
|
}
|
|
|
|
go func(stop chan struct{}) {
|
|
h.waitForStop(stop)
|
|
s.Shutdown(context.Background())
|
|
}(stop)
|
|
|
|
if h.TLS != nil {
|
|
s.ListenAndServeTLS(h.TLS.Cert, h.TLS.Key)
|
|
} else {
|
|
s.ListenAndServe()
|
|
}
|
|
}
|
|
|
|
func (h *Webhook) waitForStop(stop chan struct{}) {
|
|
<-stop
|
|
close(stop)
|
|
}
|
|
|
|
// The handler simply reads the update from the body of the requests
|
|
// and writes them to the update channel.
|
|
func (h *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
var update Update
|
|
err := json.NewDecoder(r.Body).Decode(&update)
|
|
if err != nil {
|
|
h.bot.debug(fmt.Errorf("cannot decode update: %v", err))
|
|
return
|
|
}
|
|
h.dest <- update
|
|
}
|