removed all "paid" features - no more stars, gifts, or grams

This commit is contained in:
onysd 2026-08-07 01:50:10 +03:00
parent d4451d753c
commit 21d8e91756
165 changed files with 318 additions and 40948 deletions

View file

@ -5,7 +5,6 @@ import (
"strings"
"github.com/iamxvbaba/td/tg"
"go.uber.org/zap"
"telesrv/internal/domain"
)
@ -60,179 +59,3 @@ func (r *Router) validateDefaultReaction(ctx context.Context, reaction domain.Me
}
return reactionInvalidErr()
}
func (r *Router) onMessagesGetPaidReactionPrivacy(ctx context.Context) (tg.UpdatesClass, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {
return nil, internalErr()
}
settings := domain.DefaultAccountReactionSettings()
if svc, ok := r.deps.Account.(accountPaidReactionPrivacyService); ok {
next, err := svc.GetReactionSettings(ctx, userID)
if err != nil {
return nil, internalErr()
}
settings = next
}
return &tg.Updates{
Updates: []tg.UpdateClass{&tg.UpdatePaidReactionPrivacy{
Private: r.tgPaidReactionPrivacy(ctx, userID, settings.PaidPrivacy),
}},
Users: []tg.UserClass{},
Chats: []tg.ChatClass{},
Date: int(r.clock.Now().Unix()),
Seq: 0,
}, nil
}
func (r *Router) onMessagesTogglePaidReactionPrivacy(ctx context.Context, req *tg.MessagesTogglePaidReactionPrivacyRequest) (bool, error) {
if req.MsgID <= 0 || req.MsgID > domain.MaxMessageBoxID {
return false, messageIDInvalidErr()
}
userID, _, err := r.currentUserID(ctx)
if err != nil {
return false, internalErr()
}
if _, err := r.checkedDomainPeerFromInputPeer(ctx, userID, req.Peer); err != nil {
return false, err
}
privacy, err := r.domainPaidReactionPrivacy(ctx, userID, req.Private)
if err != nil {
return false, err
}
if svc, ok := r.deps.Account.(accountPaidReactionPrivacyService); ok {
next, err := svc.SetPaidReactionPrivacy(ctx, userID, privacy)
if err != nil {
return false, internalErr()
}
privacy = next.PaidPrivacy
}
r.pushUserUpdates(ctx, userID, &tg.Updates{
Updates: []tg.UpdateClass{&tg.UpdatePaidReactionPrivacy{Private: r.tgPaidReactionPrivacy(ctx, userID, privacy)}},
Users: []tg.UserClass{},
Chats: []tg.ChatClass{},
Date: int(r.clock.Now().Unix()),
Seq: 0,
})
return true, nil
}
// onMessagesSendPaidReaction 为一条广播频道消息发送付费 reaction:从 Stars 账本 Debit
// req.Count 星,累计到消息上,返回带 updateMessageReactions(含 ReactionPaid 总星数 +
// top reactors)与 updateStarsBalance 的 Updates。崩溃约束:必须返回合法 Updates——
// DrKLO StarsController 对响应无 instanceof 强转 (TLRPC.Updates)。
func (r *Router) onMessagesSendPaidReaction(ctx context.Context, req *tg.MessagesSendPaidReactionRequest) (tg.UpdatesClass, error) {
if req.MsgID <= 0 || req.MsgID > domain.MaxMessageBoxID {
return nil, messageIDInvalidErr()
}
if req.Count <= 0 || req.Count > domain.MaxPaidReactionStarsPerRequest {
return nil, starsAmountInvalidErr()
}
userID, _, err := r.currentUserID(ctx)
if err != nil {
return nil, internalErr()
}
peer, err := r.checkedDomainPeerFromInputPeer(ctx, userID, req.Peer)
if err != nil {
return nil, err
}
// 付费 reaction 仅用于(广播)频道帖子。
if peer.Type != domain.PeerTypeChannel {
return nil, peerIDInvalidErr()
}
anonymous, err := r.resolvePaidReactionAnonymous(ctx, userID, req)
if err != nil {
return nil, err
}
if r.deps.Stars == nil {
return nil, balanceTooLowErr()
}
paidSvc, ok := r.deps.Channels.(channelPaidReactionService)
if !ok {
return nil, notImplementedErr()
}
channelPeer := domain.Peer{Type: domain.PeerTypeChannel, ID: peer.ID}
// 1. 先从账本 Debit(余额不足→真实 BALANCE_TOO_LOW)。
balance, err := r.deps.Stars.Debit(ctx, userID, int64(req.Count), domain.StarsReasonReaction, channelPeer, "Paid reaction", "")
if err != nil {
return nil, starsErr(err)
}
// 2. 累计付费 reaction;失败则补偿退款。
res, err := paidSvc.SendPaidReaction(ctx, userID, domain.SendChannelPaidReactionRequest{
UserID: userID,
ChannelID: peer.ID,
MessageID: req.MsgID,
Stars: int64(req.Count),
Anonymous: anonymous,
Date: int(r.clock.Now().Unix()),
})
if err != nil {
if _, refundErr := r.deps.Stars.Credit(ctx, userID, int64(req.Count), domain.StarsReasonReaction, channelPeer, "Paid reaction refund", ""); refundErr != nil {
r.log.Error("paid reaction refund failed after record error",
zap.Int64("user_id", userID), zap.Int64("channel_id", peer.ID), zap.Int("msg_id", req.MsgID), zap.Error(refundErr))
}
return nil, channelReactionErr(err)
}
// 3. 构建并扇出 updateMessageReactions;请求者额外带 updateStarsBalance。
ids := []int{res.Message.ID}
build := func(viewerUserID int64) *tg.Updates {
updates := r.channelPaidReactionUpdates(ctx, userID, viewerUserID, res, ids)
if updates != nil && viewerUserID == userID {
updates.Updates = append(updates.Updates, &tg.UpdateStarsBalance{Balance: &tg.StarsAmount{Amount: balance.Balance}})
}
return updates
}
recipients := append([]int64{res.Message.SenderUserID}, res.Recipients...)
r.pushChannelViewerUpdates(ctx, userID, res.Channel.ID, recipients, build)
return build(userID), nil
}
// resolvePaidReactionAnonymous 计算本次付费 reaction 是否匿名:显式 private 优先,
// 缺省回退用户保存的默认付费 reaction 隐私。
func (r *Router) resolvePaidReactionAnonymous(ctx context.Context, userID int64, req *tg.MessagesSendPaidReactionRequest) (bool, error) {
if private, ok := req.GetPrivate(); ok {
privacy, err := r.domainPaidReactionPrivacy(ctx, userID, private)
if err != nil {
return false, err
}
return privacy.Kind == domain.PaidReactionPrivacyAnonymous, nil
}
if svc, ok := r.deps.Account.(accountPaidReactionPrivacyService); ok {
if settings, err := svc.GetReactionSettings(ctx, userID); err == nil {
return settings.PaidPrivacy.Kind == domain.PaidReactionPrivacyAnonymous, nil
}
}
return false, nil
}
func (r *Router) domainPaidReactionPrivacy(ctx context.Context, userID int64, in tg.PaidReactionPrivacyClass) (domain.PaidReactionPrivacy, error) {
switch typed := in.(type) {
case nil, *tg.PaidReactionPrivacyDefault:
return domain.PaidReactionPrivacy{Kind: domain.PaidReactionPrivacyDefault}, nil
case *tg.PaidReactionPrivacyAnonymous:
return domain.PaidReactionPrivacy{Kind: domain.PaidReactionPrivacyAnonymous}, nil
case *tg.PaidReactionPrivacyPeer:
peer, err := r.checkedDomainPeerFromInputPeer(ctx, userID, typed.Peer)
if err != nil {
return domain.PaidReactionPrivacy{}, err
}
return domain.PaidReactionPrivacy{Kind: domain.PaidReactionPrivacyPeer, Peer: &peer}, nil
default:
return domain.PaidReactionPrivacy{}, inputConstructorInvalidErr()
}
}
func (r *Router) tgPaidReactionPrivacy(ctx context.Context, userID int64, in domain.PaidReactionPrivacy) tg.PaidReactionPrivacyClass {
switch in.Kind {
case domain.PaidReactionPrivacyAnonymous:
return &tg.PaidReactionPrivacyAnonymous{}
case domain.PaidReactionPrivacyPeer:
if in.Peer == nil {
return &tg.PaidReactionPrivacyDefault{}
}
if peer := r.inputPeerForDomainPeer(ctx, userID, *in.Peer); peer != nil {
return &tg.PaidReactionPrivacyPeer{Peer: peer}
}
}
return &tg.PaidReactionPrivacyDefault{}
}