feat: sync rich text message support

This commit is contained in:
A 2026-07-04 21:15:35 +08:00
parent 4d3bbeabd8
commit 7c9d8dda16
41 changed files with 1164 additions and 94 deletions

View file

@ -2,6 +2,7 @@ package rpc
import (
"context"
"strconv"
"github.com/gotd/td/bin"
"github.com/gotd/td/tg"
@ -50,6 +51,81 @@ func decodeRichBlocks(data []byte) ([]tg.PageBlockClass, error) {
return out, nil
}
func normalizeRichBlocksForClients(blocks []tg.PageBlockClass) {
for _, block := range blocks {
normalizeRichBlockForClients(block)
}
}
func normalizeRichBlockForClients(block tg.PageBlockClass) {
switch b := block.(type) {
case *tg.PageBlockList:
for _, item := range b.Items {
if item, ok := item.(*tg.PageListItemBlocks); ok {
normalizeRichBlocksForClients(item.Blocks)
}
}
case *tg.PageBlockCover:
normalizeRichBlockForClients(b.Cover)
case *tg.PageBlockEmbedPost:
normalizeRichBlocksForClients(b.Blocks)
case *tg.PageBlockCollage:
normalizeRichBlocksForClients(b.Items)
case *tg.PageBlockSlideshow:
normalizeRichBlocksForClients(b.Items)
case *tg.PageBlockOrderedList:
normalizeOrderedListForClients(b)
case *tg.PageBlockDetails:
normalizeRichBlocksForClients(b.Blocks)
case *tg.PageBlockBlockquoteBlocks:
normalizeRichBlocksForClients(b.Blocks)
}
}
func normalizeOrderedListForClients(list *tg.PageBlockOrderedList) {
if list == nil {
return
}
reversed := list.Reversed || list.Flags.Has(2)
current := 1
if list.Flags.Has(0) || list.Start != 0 {
current = list.Start
} else if reversed {
current = len(list.Items)
}
step := 1
if reversed {
step = -1
}
for _, item := range list.Items {
value := current
switch i := item.(type) {
case *tg.PageListOrderedItemText:
if v, ok := i.GetValue(); ok || i.Value != 0 {
value = v
if !ok {
value = i.Value
}
}
if num, ok := i.GetNum(); !ok || num == "" {
i.SetNum(strconv.Itoa(value))
}
case *tg.PageListOrderedItemBlocks:
if v, ok := i.GetValue(); ok || i.Value != 0 {
value = v
if !ok {
value = i.Value
}
}
if num, ok := i.GetNum(); !ok || num == "" {
i.SetNum(strconv.Itoa(value))
}
normalizeRichBlocksForClients(i.Blocks)
}
current = value + step
}
}
// domainRichMessageFromInput 把入站 tg.InputRichMessageClass 解析为 domain 快照:
// 序列化 blocks + 按 id 解析内嵌 photos/documents(复用 sendMedia 同款媒体解析)。
// 返回 nil 表示无富文本载荷。Phase 1 仅认 *tg.InputRichMessage。
@ -62,9 +138,16 @@ func (r *Router) domainRichMessageFromInput(ctx context.Context, input tg.InputR
// Phase 1:HTML/Markdown 变体需服务端解析为 PageBlock,尚未支持。
return nil, mediaInvalidErr()
}
if r.deps.Files == nil {
if len(in.Blocks) == 0 {
if len(in.Photos) == 0 && len(in.Documents) == 0 {
return nil, nil
}
return nil, mediaInvalidErr()
}
if (len(in.Photos) > 0 || len(in.Documents) > 0) && r.deps.Files == nil {
return nil, notImplementedErr()
}
normalizeRichBlocksForClients(in.Blocks)
blocks, err := encodeRichBlocks(in.Blocks)
if err != nil {
return nil, err
@ -132,3 +215,11 @@ func tgRichMessage(m *domain.MessageRichMessage) (*tg.RichMessage, error) {
}
return out, nil
}
func mustTGRichMessage(m *domain.MessageRichMessage) *tg.RichMessage {
out, err := tgRichMessage(m)
if err != nil {
panic("invalid stored rich_message: " + err.Error())
}
return out
}