chore: refresh gramsrv public release

This commit is contained in:
A 2026-06-30 14:37:43 +08:00
parent 75cebe8dbf
commit 70b6820474
1274 changed files with 378751 additions and 59919 deletions

View file

@ -0,0 +1,125 @@
package rpc
import (
"errors"
"github.com/gotd/td/tg"
"github.com/gotd/td/tgerr"
"telesrv/internal/domain"
)
// P3 reply_markup 错误码(对齐官方)。
func buttonDataInvalidErr() error { return tgerr.New(400, "BUTTON_DATA_INVALID") }
func buttonInvalidErr() error { return tgerr.New(400, "BUTTON_INVALID") }
func buttonURLInvalidErr() error { return tgerr.New(400, "BUTTON_URL_INVALID") }
// replyMarkupErr 把 domain 校验错误映射为客户端错误码。
func replyMarkupErr(err error) error {
switch {
case errors.Is(err, domain.ErrButtonDataInvalid):
return buttonDataInvalidErr()
case errors.Is(err, domain.ErrButtonURLInvalid):
return buttonURLInvalidErr()
case errors.Is(err, domain.ErrButtonInvalid), errors.Is(err, domain.ErrButtonTypeInvalid):
return buttonInvalidErr()
default:
return replyMarkupInvalidErr()
}
}
// domainReplyMarkupForSender 解析入站 reply_markup。P3 语义:
// - 仅 bot 账号下发的 markup 被接受;非 bot 一律丢弃(返回 nil,不报错——对齐
// 官方「普通用户 markup 无效」,I1)。
// - 仅 ReplyInlineMarkup 被处理;reply keyboard 家族(自定义键盘/隐藏/强制回复)
// P3 不支持,静默丢弃(不报错,避免破坏 bot 发送;记 P4)。
// - inline 行内按钮仅 callback / url;其它按钮类型(webview/game/url_auth/
// request_* 等)→ ErrButtonTypeInvalid(拒绝整条发送,绝不半实现下发)。
// - data≤64B、行/按钮上限、url https 由 domain.ValidateReplyMarkup 校验。
func domainReplyMarkupForSender(markup tg.ReplyMarkupClass, senderIsBot bool) (*domain.MessageReplyMarkup, error) {
if markup == nil || !senderIsBot {
return nil, nil
}
inline, ok := markup.(*tg.ReplyInlineMarkup)
if !ok {
// reply keyboard / hide / force-reply:P3 不支持,丢弃。
return nil, nil
}
parsed, err := domainInlineMarkup(inline)
if err != nil {
return nil, err
}
if parsed.IsZero() {
return nil, nil
}
if err := domain.ValidateReplyMarkup(parsed); err != nil {
return nil, err
}
return parsed, nil
}
func domainInlineMarkup(inline *tg.ReplyInlineMarkup) (*domain.MessageReplyMarkup, error) {
out := &domain.MessageReplyMarkup{Inline: make([][]domain.MarkupButton, 0, len(inline.Rows))}
for _, row := range inline.Rows {
domainRow := make([]domain.MarkupButton, 0, len(row.Buttons))
for _, btn := range row.Buttons {
db, err := domainMarkupButton(btn)
if err != nil {
return nil, err
}
domainRow = append(domainRow, db)
}
out.Inline = append(out.Inline, domainRow)
}
return out, nil
}
func domainMarkupButton(btn tg.KeyboardButtonClass) (domain.MarkupButton, error) {
switch b := btn.(type) {
case *tg.KeyboardButtonCallback:
return domain.MarkupButton{
Type: domain.MarkupButtonCallback,
Text: b.Text,
Data: append([]byte(nil), b.Data...),
RequiresPassword: b.RequiresPassword,
}, nil
case *tg.KeyboardButtonURL:
return domain.MarkupButton{
Type: domain.MarkupButtonURL,
Text: b.Text,
URL: b.URL,
}, nil
default:
// webview/game/url_auth/request_*/switch_inline/buy 等 P3 未实现按钮类型。
return domain.MarkupButton{}, domain.ErrButtonTypeInvalid
}
}
// tgReplyMarkup 把存储的 inline keyboard 快照还原为 tg.ReplyInlineMarkup。
func tgReplyMarkup(m *domain.MessageReplyMarkup) tg.ReplyMarkupClass {
if m.IsZero() {
return nil
}
rows := make([]tg.KeyboardButtonRow, 0, len(m.Inline))
for _, row := range m.Inline {
buttons := make([]tg.KeyboardButtonClass, 0, len(row))
for _, btn := range row {
buttons = append(buttons, tgMarkupButton(btn))
}
rows = append(rows, tg.KeyboardButtonRow{Buttons: buttons})
}
return &tg.ReplyInlineMarkup{Rows: rows}
}
func tgMarkupButton(btn domain.MarkupButton) tg.KeyboardButtonClass {
switch btn.Type {
case domain.MarkupButtonURL:
return &tg.KeyboardButtonURL{Text: btn.Text, URL: btn.URL}
default: // callback
out := &tg.KeyboardButtonCallback{Text: btn.Text, Data: btn.Data}
if btn.RequiresPassword {
out.SetRequiresPassword(true)
}
return out
}
}