telegram-bot-api/bot_handler.go
2024-01-30 21:46:49 +02:00

149 lines
3.2 KiB
Go

package tgbotapi
import (
"encoding/json"
"errors"
"net/http"
"time"
)
type HandlerConfig struct {
shutdownChannel chan struct{}
bufferSize int
}
var defaultConfig = HandlerConfig{
shutdownChannel: make(chan struct{}),
bufferSize: 100,
}
type PollingHandler struct {
bot *BotAPI
HandlerConfig
updateConfig UpdateConfig
}
func NewPollingHandler(bot *BotAPI, updateConfig UpdateConfig) *PollingHandler {
return &PollingHandler{
bot: bot,
HandlerConfig: defaultConfig,
updateConfig: updateConfig,
}
}
type WebhookHandler struct {
bot *BotAPI
HandlerConfig
}
func NewWebhookHandler(bot *BotAPI) *WebhookHandler {
return &WebhookHandler{
bot: bot,
HandlerConfig: defaultConfig,
}
}
// InitUpdatesChannel starts and returns a channel for getting updates.
func (h *PollingHandler) InitUpdatesChannel() (UpdatesChannel, error) {
w, err := h.bot.GetWebhookInfo()
if err == nil && w.IsSet() {
return nil, errors.New("webhook was set, can't use polling")
}
ch := make(chan Update, h.bufferSize)
go func() {
for {
select {
case <-h.shutdownChannel:
close(ch)
return
default:
}
updates, err := h.bot.GetUpdates(h.updateConfig)
if err != nil {
log.Println(err)
log.Println("Failed to get updates, retrying in 3 seconds...")
time.Sleep(time.Second * 3)
continue
}
for _, update := range updates {
if update.UpdateID >= h.updateConfig.Offset {
h.updateConfig.Offset = update.UpdateID + 1
ch <- update
}
}
}
}()
return ch, nil
}
// Stop stops the go routine which receives updates
func (h *PollingHandler) Stop() {
if h.bot.GetConfig().GetDebug() {
log.Println("Stopping the update receiver routine...")
}
close(h.shutdownChannel)
}
// ListenForWebhook registers a http handler for a webhook.
func (h *WebhookHandler) ListenForWebhook(pattern string) UpdatesChannel {
ch := make(chan Update, h.bufferSize)
http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
update, err := UnmarshalUpdate(r)
if err != nil {
errMsg, _ := json.Marshal(map[string]string{"error": err.Error()})
w.WriteHeader(http.StatusBadRequest)
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(errMsg)
return
}
ch <- *update
})
return ch
}
// ListenForWebhookRespReqFormat registers a http handler for a single incoming webhook.
func (h *WebhookHandler) ListenForWebhookRespReqFormat(w http.ResponseWriter, r *http.Request) UpdatesChannel {
ch := make(chan Update, h.bufferSize)
func(w http.ResponseWriter, r *http.Request) {
defer close(ch)
update, err := UnmarshalUpdate(r)
if err != nil {
errMsg, _ := json.Marshal(map[string]string{"error": err.Error()})
w.WriteHeader(http.StatusBadRequest)
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(errMsg)
return
}
ch <- *update
}(w, r)
return ch
}
// UnmarshalUpdate parses and returns update received via webhook
func UnmarshalUpdate(r *http.Request) (*Update, error) {
if r.Method != http.MethodPost {
err := errors.New("wrong HTTP method required POST")
return nil, err
}
var update Update
err := json.NewDecoder(r.Body).Decode(&update)
if err != nil {
return nil, err
}
return &update, nil
}