feat(server/reward): TG getChatMember 真校验 + ClaimTelegram + POST /tg/webhook

This commit is contained in:
wangjia
2026-07-13 07:54:46 +08:00
parent e23cd5e1bd
commit e26284d072
4 changed files with 214 additions and 5 deletions
+43
View File
@@ -3,6 +3,8 @@ package reward
import (
"encoding/json"
"net/http"
"strconv"
"strings"
"github.com/wangjia/pangolin/server/internal/apierr"
"github.com/wangjia/pangolin/server/internal/auth"
@@ -73,3 +75,44 @@ func (h *Handler) TelegramStart(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
_ = json.NewEncoder(w).Encode(map[string]any{"deep_link": h.svc.TelegramDeepLink(tok)})
}
// TelegramWebhook 处理 Telegram Bot API 回调:校验 secret 头 → 解析 `/start <token>`
// → ConsumeTelegramToken → ClaimTelegram → sendMessage 回执。Telegram 只要求 200 即不重投,
// 业务结果通过 sendMessage 异步告知用户。
func (h *Handler) TelegramWebhook(w http.ResponseWriter, r *http.Request) {
if !h.tgEnabled || r.Header.Get("X-Telegram-Bot-Api-Secret-Token") != h.svc.WebhookSecret() {
w.WriteHeader(http.StatusNotFound)
return
}
var upd struct {
Message struct {
Text string `json:"text"`
From struct {
ID int64 `json:"id"`
} `json:"from"`
} `json:"message"`
}
_ = json.NewDecoder(http.MaxBytesReader(w, r.Body, 16<<10)).Decode(&upd)
w.WriteHeader(http.StatusOK) // Telegram 只要 200;业务异步在下方
text, fromID := upd.Message.Text, upd.Message.From.ID
if !strings.HasPrefix(text, "/start ") || fromID == 0 {
return
}
token := strings.TrimSpace(strings.TrimPrefix(text, "/start "))
ctx := r.Context()
uid, ok, _ := h.svc.ConsumeTelegramToken(ctx, token)
if !ok {
h.svc.SendTelegram(ctx, fromID, "链接已失效,请回 App 重新点「验证领取」。")
return
}
granted, err := h.svc.ClaimTelegram(ctx, uid, strconv.FormatInt(fromID, 10))
switch {
case err != nil:
h.svc.SendTelegram(ctx, fromID, "验证出错,请稍后重试。")
case granted:
h.svc.SendTelegram(ctx, fromID, "已到账 +3 天 Pro,感谢加入!")
default:
h.svc.SendTelegram(ctx, fromID, "请先加入频道 "+h.svc.Channel()+" 再点验证;若已加入且领取过则无需重复。")
}
}