chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
123
internal/rpc/bots_callback.go
Normal file
123
internal/rpc/bots_callback.go
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/gotd/td/tg"
|
||||
"github.com/gotd/td/tgerr"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
// botCallbackTimeout 是 getBotCallbackAnswer 的挂起上限:bot 未在窗口内
|
||||
// setBotCallbackAnswer 即回 BOT_RESPONSE_TIMEOUT(不快速失败,给 bot 上线追答的窗口)。
|
||||
const botCallbackTimeout = 25 * time.Second
|
||||
|
||||
func botResponseTimeoutErr() error { return tgerr.New(502, "BOT_RESPONSE_TIMEOUT") }
|
||||
func dataInvalidErr() error { return tgerr.New(400, "DATA_INVALID") }
|
||||
|
||||
// onMessagesGetBotCallbackAnswer 处理 inline callback 按钮点击:把 updateBotCallbackQuery
|
||||
// 推给 bot,挂起等待 bot 的 setBotCallbackAnswer,或超时回 BOT_RESPONSE_TIMEOUT。
|
||||
func (r *Router) onMessagesGetBotCallbackAnswer(ctx context.Context, req *tg.MessagesGetBotCallbackAnswerRequest) (*tg.MessagesBotCallbackAnswer, error) {
|
||||
userID, _, err := r.currentUserID(ctx)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
if userID == 0 {
|
||||
return nil, peerIDInvalidErr()
|
||||
}
|
||||
peer, err := r.checkedDomainPeerFromInputPeer(ctx, userID, req.Peer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// callback 按钮只存在于 bot 的私聊消息。peer 必须是 bot 用户。
|
||||
if peer.Type != domain.PeerTypeUser || !r.userIsBot(ctx, peer.ID) {
|
||||
return nil, dataInvalidErr()
|
||||
}
|
||||
botUserID := peer.ID
|
||||
// game 按钮(getBotCallbackAnswer.game)P3 不支持:返回空答案(客户端不弹任何东西),
|
||||
// 不挂起、不推送(避免给 bot 投递无法处理的 game query)。
|
||||
if req.Game {
|
||||
return &tg.MessagesBotCallbackAnswer{}, nil
|
||||
}
|
||||
data, hasData := req.GetData()
|
||||
if !hasData {
|
||||
return nil, dataInvalidErr()
|
||||
}
|
||||
// 校验目标消息存在于请求者自己的盒、且对端正是该 bot。
|
||||
if req.MsgID <= 0 || req.MsgID > domain.MaxMessageBoxID {
|
||||
return nil, messageIDInvalidErr()
|
||||
}
|
||||
msg, ok, err := r.lookupOwnerMessage(ctx, userID, req.MsgID)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
if !ok || msg.Peer != peer {
|
||||
return nil, messageIDInvalidErr()
|
||||
}
|
||||
|
||||
queryID, pending := r.callbacks.register(botUserID, userID)
|
||||
defer r.callbacks.deregister(queryID)
|
||||
|
||||
// updateBotCallbackQuery 是 ephemeral(无 pts/qts,不进 getDifference):仅在线推给
|
||||
// bot;bot 离线则投递 0,但仍走超时窗口(I5,给 bot 上线追答机会)。
|
||||
// MsgID 透传请求者侧的 box id(P3 不做 bot 侧 box id 翻译——bot 侧消息编辑后移,记 todo)。
|
||||
update := &tg.UpdateBotCallbackQuery{
|
||||
QueryID: queryID,
|
||||
UserID: userID,
|
||||
Peer: &tg.PeerUser{UserID: userID},
|
||||
MsgID: req.MsgID,
|
||||
ChatInstance: chatInstanceFor(botUserID, userID),
|
||||
}
|
||||
update.SetData(data)
|
||||
r.pushUserMessage(ctx, botUserID, "push bot callback query", &tg.Updates{
|
||||
Updates: []tg.UpdateClass{update},
|
||||
Date: int(r.clock.Now().Unix()),
|
||||
})
|
||||
|
||||
waitCtx, cancel := context.WithTimeout(ctx, botCallbackTimeout)
|
||||
defer cancel()
|
||||
select {
|
||||
case ans := <-pending.ch:
|
||||
return tgBotCallbackAnswer(ans), nil
|
||||
case <-waitCtx.Done():
|
||||
return nil, botResponseTimeoutErr()
|
||||
}
|
||||
}
|
||||
|
||||
// onMessagesSetBotCallbackAnswer 是 bot 对一次 callback query 的应答:解挂等待中的
|
||||
// getBotCallbackAnswer。仅属主 bot 可解挂(callerBotID==pending.botUserID,I6)。
|
||||
func (r *Router) onMessagesSetBotCallbackAnswer(ctx context.Context, req *tg.MessagesSetBotCallbackAnswerRequest) (bool, error) {
|
||||
botID, err := r.callerBotID(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
ans := domain.BotCallbackAnswer{Alert: req.Alert, CacheTime: req.CacheTime}
|
||||
if msg, ok := req.GetMessage(); ok {
|
||||
if utf8.RuneCountInString(msg) > domain.MaxBotCallbackAnswerLen {
|
||||
return false, messageTooLongErr()
|
||||
}
|
||||
ans.Message = msg
|
||||
}
|
||||
if url, ok := req.GetURL(); ok {
|
||||
ans.URL = url
|
||||
}
|
||||
// resolve 返回是否投递成功;未注册/超时/非属主一律 false。对 bot 而言答案是否
|
||||
// 被等待者接收无关紧要(官方恒返回 true),但非属主必须拒绝投递(防钓鱼弹窗)。
|
||||
r.callbacks.resolve(botID, req.QueryID, ans)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func tgBotCallbackAnswer(ans domain.BotCallbackAnswer) *tg.MessagesBotCallbackAnswer {
|
||||
out := &tg.MessagesBotCallbackAnswer{Alert: ans.Alert, CacheTime: ans.CacheTime}
|
||||
if ans.Message != "" {
|
||||
out.SetMessage(ans.Message)
|
||||
}
|
||||
if ans.URL != "" {
|
||||
out.SetURL(ans.URL)
|
||||
out.HasURL = true
|
||||
}
|
||||
return out
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue