package toc import ( "log" "strings" "sync" "time" "tgchanbot/internal/storage" tele "gopkg.in/telebot.v3" ) type Manager struct { storage *storage.Storage bot *tele.Bot chanID int64 debounce time.Duration coverImage string mu sync.Mutex pending bool timer *time.Timer stopCh chan struct{} } func NewManager(store *storage.Storage, bot *tele.Bot, chanID int64, debounce time.Duration, coverImage string) *Manager { return &Manager{ storage: store, bot: bot, chanID: chanID, debounce: debounce, coverImage: coverImage, stopCh: make(chan struct{}), } } func (m *Manager) TriggerUpdate() { m.mu.Lock() defer m.mu.Unlock() m.pending = true if m.timer != nil { m.timer.Stop() } m.timer = time.AfterFunc(m.debounce, func() { m.doUpdate() }) } func (m *Manager) doUpdate() { m.mu.Lock() 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 { log.Printf("TOC update error: %v", err) } } func (m *Manager) updateMessage(content string) error { chat := &tele.Chat{ID: m.chanID} msgID, err := m.storage.GetTocMsgID() if err != nil { return err } // 带封面图片模式 if m.coverImage != "" { return m.updateWithPhoto(chat, msgID, content) } // 纯文本模式 if msgID == 0 { msg, err := m.bot.Send(chat, content, tele.ModeMarkdown, tele.NoPreview) if err != nil { return err } return m.storage.SetTocMsgID(msg.ID) } existingMsg := &tele.Message{ ID: msgID, Chat: chat, } _, err = m.bot.Edit(existingMsg, content, tele.ModeMarkdown, tele.NoPreview) 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.ModeMarkdown, tele.NoPreview) if err != nil { return err } return m.storage.SetTocMsgID(msg.ID) } return err } return nil } func (m *Manager) updateWithPhoto(chat *tele.Chat, msgID int, content string) error { photo := &tele.Photo{ File: tele.FromDisk(m.coverImage), Caption: content, } if msgID == 0 { msg, err := m.bot.Send(chat, photo, tele.ModeMarkdown) if err != nil { return err } return m.storage.SetTocMsgID(msg.ID) } existingMsg := &tele.Message{ ID: msgID, Chat: chat, } // 编辑图片消息的 caption _, err := m.bot.EditCaption(existingMsg, content, tele.ModeMarkdown) 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.ModeMarkdown) if err != nil { return err } return m.storage.SetTocMsgID(msg.ID) } return err } return nil } func (m *Manager) Stop() { m.mu.Lock() defer m.mu.Unlock() if m.timer != nil { m.timer.Stop() } close(m.stopCh) }