All checks were successful
Docker Build / build-and-push-image (push) Successful in 42s
DisallowUnknownFields rejected real Telegram payloads (entities, from, date, etc. that our minimal structs don't cover). Lenient decode is the right default for an upstream webhook we don't control.
77 lines
1.9 KiB
Go
77 lines
1.9 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
type Server struct {
|
|
registry *Registry
|
|
}
|
|
|
|
func NewServer(r *Registry) *Server {
|
|
return &Server{registry: r}
|
|
}
|
|
|
|
func (s *Server) Routes() http.Handler {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/healthz", s.health)
|
|
mux.HandleFunc("/readyz", s.ready)
|
|
mux.HandleFunc("/bot/", s.botWebhook)
|
|
return chain(mux, recoverMW, accessLogMW)
|
|
}
|
|
|
|
func (s *Server) health(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = fmt.Fprint(w, "OK")
|
|
}
|
|
|
|
func (s *Server) ready(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = fmt.Fprint(w, "OK")
|
|
}
|
|
|
|
func (s *Server) botWebhook(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
slug := strings.TrimPrefix(r.URL.Path, "/bot/")
|
|
slug = strings.Trim(slug, "/")
|
|
if slug == "" || strings.Contains(slug, "/") {
|
|
http.Error(w, "bot slug missing or malformed", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
bot, ok := s.registry.Get(slug)
|
|
if !ok {
|
|
http.Error(w, "unknown bot", http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
if !verifyTelegramSecret(r.Header.Get("X-Telegram-Bot-Api-Secret-Token"), bot.Secret) {
|
|
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
var update Update
|
|
// NOTE: pas de DisallowUnknownFields — Telegram ajoute des champs
|
|
// (entities, sticker, photo, forum_topic…) au fil du temps. On reste
|
|
// tolérant et on n'extrait que ce dont on a besoin.
|
|
if err := json.NewDecoder(r.Body).Decode(&update); err != nil {
|
|
http.Error(w, "bad update payload", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := bot.Handler.Handle(r.Context(), update, bot); err != nil {
|
|
log.Printf("bot=%s update=%d handler error: %v", slug, update.UpdateID, err)
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = fmt.Fprint(w, "{}")
|
|
}
|