Files
pangolin/server/internal/reward/handler.go
T

119 lines
3.6 KiB
Go

package reward
import (
"encoding/json"
"net/http"
"strconv"
"strings"
"github.com/wangjia/pangolin/server/internal/apierr"
"github.com/wangjia/pangolin/server/internal/auth"
)
const inviteLinkBase = "https://pangolin.yanmeiai.com/i/"
type Handler struct {
svc *Service
st *Store
tgEnabled bool
channel string
}
func NewHandler(s *Service, st *Store, tgEnabled bool, channel string) *Handler {
return &Handler{svc: s, st: st, tgEnabled: tgEnabled, channel: channel}
}
func (h *Handler) GetInvite(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
uid, ok := auth.UserIDFromContext(ctx)
if !ok {
apierr.WriteJSON(w, http.StatusUnauthorized, apierr.ErrUnauthorized)
return
}
code, err := h.svc.EnsureCode(ctx, uid)
if err != nil {
apierr.WriteJSON(w, http.StatusInternalServerError, apierr.ErrInternal)
return
}
invited, converted, earned, err := h.st.Summary(ctx, uid)
if err != nil {
apierr.WriteJSON(w, http.StatusInternalServerError, apierr.ErrInternal)
return
}
joined, _ := h.st.TelegramClaimed(ctx, uid)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
_ = json.NewEncoder(w).Encode(map[string]any{
"invite_code": code,
"invite_link": inviteLinkBase + code,
"invited": invited,
"converted": converted,
"earned_days": earned,
"telegram": map[string]any{
"enabled": h.tgEnabled,
"joined": joined,
"channel": h.channel,
},
})
}
func (h *Handler) TelegramStart(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
uid, ok := auth.UserIDFromContext(ctx)
if !ok {
apierr.WriteJSON(w, http.StatusUnauthorized, apierr.ErrUnauthorized)
return
}
if !h.tgEnabled {
apierr.WriteJSON(w, http.StatusNotFound, apierr.ErrNotFound)
return
}
tok, err := h.svc.IssueTelegramToken(ctx, uid)
if err != nil {
apierr.WriteJSON(w, http.StatusInternalServerError, apierr.ErrInternal)
return
}
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()+" 再点验证;若已加入且领取过则无需重复。")
}
}