TOC分页:解决内容超长导致MEDIA_CAPTION_TOO_LONG错误
当目录条目增多超过Telegram caption字符限制时,自动按字符上限分页渲染, 附带翻页inline keyboard(⏮首页/⬅️上一页/➡️下一页),支持photo和text两种模式。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package toc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -11,6 +12,14 @@ import (
|
||||
tele "gopkg.in/telebot.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
captionCharLimit = 950 // photo caption 字符上限(预留安全余量)
|
||||
textCharLimit = 3900 // 纯文本消息字符上限
|
||||
|
||||
// CbPrefixTocPage 翻页回调前缀,导出供 telegram 包使用
|
||||
CbPrefixTocPage = "tocpage:"
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
storage *storage.Storage
|
||||
bot *tele.Bot
|
||||
@@ -55,18 +64,64 @@ func (m *Manager) doUpdate() {
|
||||
m.pending = false
|
||||
m.mu.Unlock()
|
||||
|
||||
content, err := m.Render()
|
||||
if err != nil {
|
||||
log.Printf("TOC render error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := m.updateMessage(content); err != nil {
|
||||
if err := m.DoUpdatePage(1); err != nil {
|
||||
log.Printf("TOC update error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) updateMessage(content string) error {
|
||||
// DoUpdatePage 渲染指定页并更新频道 TOC 消息
|
||||
func (m *Manager) DoUpdatePage(page int) error {
|
||||
charLimit := captionCharLimit
|
||||
if m.coverImage == "" {
|
||||
charLimit = textCharLimit
|
||||
}
|
||||
|
||||
content, totalPages, err := m.RenderPage(page, charLimit)
|
||||
if err != nil {
|
||||
return fmt.Errorf("TOC render error: %w", err)
|
||||
}
|
||||
|
||||
var markup *tele.ReplyMarkup
|
||||
if totalPages > 1 {
|
||||
markup = m.buildPageKeyboard(page, totalPages)
|
||||
}
|
||||
|
||||
return m.updateMessage(content, markup)
|
||||
}
|
||||
|
||||
// HandlePageCallback 处理翻页回调,供 telegram 包调用
|
||||
func (m *Manager) HandlePageCallback(page int) error {
|
||||
return m.DoUpdatePage(page)
|
||||
}
|
||||
|
||||
// buildPageKeyboard 构建翻页 inline keyboard
|
||||
func (m *Manager) buildPageKeyboard(currentPage, totalPages int) *tele.ReplyMarkup {
|
||||
menu := &tele.ReplyMarkup{}
|
||||
var btns []tele.Btn
|
||||
|
||||
// 回到第一页(非首页时显示)
|
||||
if currentPage > 1 {
|
||||
btns = append(btns, menu.Data("⏮", CbPrefixTocPage+"1"))
|
||||
}
|
||||
|
||||
// 上一页(非首页时显示)
|
||||
if currentPage > 1 {
|
||||
btns = append(btns, menu.Data("⬅️", fmt.Sprintf("%s%d", CbPrefixTocPage, currentPage-1)))
|
||||
}
|
||||
|
||||
// 下一页(非末页时显示)
|
||||
if currentPage < totalPages {
|
||||
btns = append(btns, menu.Data("➡️", fmt.Sprintf("%s%d", CbPrefixTocPage, currentPage+1)))
|
||||
}
|
||||
|
||||
if len(btns) > 0 {
|
||||
menu.Inline(menu.Row(btns...))
|
||||
}
|
||||
|
||||
return menu
|
||||
}
|
||||
|
||||
func (m *Manager) updateMessage(content string, markup *tele.ReplyMarkup) error {
|
||||
chat := &tele.Chat{ID: m.chanID}
|
||||
|
||||
msgID, err := m.storage.GetTocMsgID()
|
||||
@@ -76,16 +131,12 @@ func (m *Manager) updateMessage(content string) error {
|
||||
|
||||
// 带封面图片模式
|
||||
if m.coverImage != "" {
|
||||
return m.updateWithPhoto(chat, msgID, content)
|
||||
return m.updateWithPhoto(chat, msgID, content, markup)
|
||||
}
|
||||
|
||||
// 纯文本模式
|
||||
if msgID == 0 {
|
||||
msg, err := m.bot.Send(chat, content, tele.ModeHTML, tele.NoPreview)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.storage.SetTocMsgID(msg.ID)
|
||||
return m.sendNewText(chat, content, markup)
|
||||
}
|
||||
|
||||
existingMsg := &tele.Message{
|
||||
@@ -93,19 +144,19 @@ func (m *Manager) updateMessage(content string) error {
|
||||
Chat: chat,
|
||||
}
|
||||
|
||||
_, err = m.bot.Edit(existingMsg, content, tele.ModeHTML, tele.NoPreview)
|
||||
opts := []interface{}{tele.ModeHTML, tele.NoPreview}
|
||||
if markup != nil {
|
||||
opts = append(opts, markup)
|
||||
}
|
||||
|
||||
_, err = m.bot.Edit(existingMsg, content, opts...)
|
||||
if err != nil {
|
||||
errMsg := err.Error()
|
||||
// 内容未变化,忽略
|
||||
if err == tele.ErrMessageNotModified || strings.Contains(errMsg, "message is not modified") {
|
||||
return nil
|
||||
}
|
||||
if strings.Contains(errMsg, "message to edit not found") {
|
||||
msg, err := m.bot.Send(chat, content, tele.ModeHTML, tele.NoPreview)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.storage.SetTocMsgID(msg.ID)
|
||||
return m.sendNewText(chat, content, markup)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -113,14 +164,31 @@ func (m *Manager) updateMessage(content string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) updateWithPhoto(chat *tele.Chat, msgID int, content string) error {
|
||||
func (m *Manager) sendNewText(chat *tele.Chat, content string, markup *tele.ReplyMarkup) error {
|
||||
opts := []interface{}{tele.ModeHTML, tele.NoPreview}
|
||||
if markup != nil {
|
||||
opts = append(opts, markup)
|
||||
}
|
||||
|
||||
msg, err := m.bot.Send(chat, content, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.storage.SetTocMsgID(msg.ID)
|
||||
}
|
||||
|
||||
func (m *Manager) updateWithPhoto(chat *tele.Chat, msgID int, content string, markup *tele.ReplyMarkup) error {
|
||||
photo := &tele.Photo{
|
||||
File: tele.FromDisk(m.coverImage),
|
||||
Caption: content,
|
||||
}
|
||||
|
||||
if msgID == 0 {
|
||||
msg, err := m.bot.Send(chat, photo, tele.ModeHTML)
|
||||
opts := []interface{}{tele.ModeHTML}
|
||||
if markup != nil {
|
||||
opts = append(opts, markup)
|
||||
}
|
||||
msg, err := m.bot.Send(chat, photo, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -133,16 +201,24 @@ func (m *Manager) updateWithPhoto(chat *tele.Chat, msgID int, content string) er
|
||||
}
|
||||
|
||||
// 编辑图片消息的 caption
|
||||
_, err := m.bot.EditCaption(existingMsg, content, tele.ModeHTML)
|
||||
opts := []interface{}{tele.ModeHTML}
|
||||
if markup != nil {
|
||||
opts = append(opts, markup)
|
||||
}
|
||||
|
||||
_, err := m.bot.EditCaption(existingMsg, content, opts...)
|
||||
if err != nil {
|
||||
errMsg := err.Error()
|
||||
if err == tele.ErrMessageNotModified || strings.Contains(errMsg, "message is not modified") {
|
||||
return nil
|
||||
}
|
||||
// 旧消息不是图片或找不到,重新发送
|
||||
if strings.Contains(errMsg, "message to edit not found") ||
|
||||
strings.Contains(errMsg, "no caption") {
|
||||
msg, err := m.bot.Send(chat, photo, tele.ModeHTML)
|
||||
opts := []interface{}{tele.ModeHTML}
|
||||
if markup != nil {
|
||||
opts = append(opts, markup)
|
||||
}
|
||||
msg, err := m.bot.Send(chat, photo, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user