增加功能:删除选择;相册转发

This commit is contained in:
dela
2026-02-05 11:07:37 +08:00
parent ee9418b7cf
commit 6691dbaff2
4 changed files with 264 additions and 32 deletions

View File

@@ -3,6 +3,8 @@ package telegram
import (
"fmt"
"strings"
"sync"
"time"
"tgchanbot/internal/storage"
@@ -14,6 +16,14 @@ const (
cbPrefixConfirm = "confirm:"
cbPrefixTitle = "title:"
cbCancel = "cancel"
albumCollectDelay = 500 * time.Millisecond // 等待相册其他消息的时间
)
// 相册收集定时器
var (
albumTimers = make(map[int64]*time.Timer)
albumTimersMu sync.Mutex
)
func (b *Bot) handlePost(c tele.Context) error {
@@ -107,8 +117,10 @@ func (b *Bot) handleTextInput(c tele.Context) error {
// 私聊收到转发消息,直接启动投稿流程(无需先 /post
if isForwarded && b.cfg.IsAdmin(c.Sender().ID) {
if state == nil || state.Step == StepAwaitForward {
if state == nil {
b.states.StartPost(c.Sender().ID)
}
if state == nil || state.Step == StepAwaitForward {
return b.handleForwarded(c)
}
}
@@ -141,8 +153,43 @@ func (b *Bot) handleForwarded(c tele.Context) error {
return c.Reply("❌ 这不是一条转发消息,请转发一条消息给我")
}
b.states.SetForwarded(c.Sender().ID, msg)
userID := c.Sender().ID
// 相册消息处理
if msg.AlbumID != "" {
b.states.AddAlbumMessage(userID, msg)
b.scheduleAlbumFinalize(c, userID)
return nil // 等待收集完成
}
// 单条消息
b.states.SetForwarded(userID, msg)
return b.showCategorySelection(c)
}
// scheduleAlbumFinalize 延迟完成相册收集
func (b *Bot) scheduleAlbumFinalize(c tele.Context, userID int64) {
albumTimersMu.Lock()
defer albumTimersMu.Unlock()
// 取消之前的定时器
if timer, exists := albumTimers[userID]; exists {
timer.Stop()
}
// 设置新定时器
albumTimers[userID] = time.AfterFunc(albumCollectDelay, func() {
albumTimersMu.Lock()
delete(albumTimers, userID)
albumTimersMu.Unlock()
b.states.FinalizeAlbum(userID)
b.showCategorySelectionAsync(c, userID)
})
}
// showCategorySelection 显示分类选择
func (b *Bot) showCategorySelection(c tele.Context) error {
categories, err := b.storage.ListCategories()
if err != nil {
b.states.Delete(c.Sender().ID)
@@ -154,10 +201,48 @@ func (b *Bot) handleForwarded(c tele.Context) error {
return c.Reply("❌ 暂无分类,请先使用 /cat_add 创建分类")
}
state := b.states.Get(c.Sender().ID)
msgCount := 1
if state != nil && len(state.ForwardedMsgs) > 0 {
msgCount = len(state.ForwardedMsgs)
}
keyboard := b.buildCategoryKeyboard(categories)
if msgCount > 1 {
return c.Reply(fmt.Sprintf("📁 已收到 %d 条消息(相册),请选择分类:", msgCount), keyboard)
}
return c.Reply("📁 请选择分类:", keyboard)
}
// showCategorySelectionAsync 异步显示分类选择(用于定时器回调)
func (b *Bot) showCategorySelectionAsync(c tele.Context, userID int64) {
categories, err := b.storage.ListCategories()
if err != nil {
b.states.Delete(userID)
c.Bot().Send(c.Chat(), fmt.Sprintf("❌ 获取分类失败: %v", err))
return
}
if len(categories) == 0 {
b.states.Delete(userID)
c.Bot().Send(c.Chat(), "❌ 暂无分类,请先使用 /cat_add 创建分类")
return
}
state := b.states.Get(userID)
msgCount := 1
if state != nil && len(state.ForwardedMsgs) > 0 {
msgCount = len(state.ForwardedMsgs)
}
keyboard := b.buildCategoryKeyboard(categories)
if msgCount > 1 {
c.Bot().Send(c.Chat(), fmt.Sprintf("📁 已收到 %d 条消息(相册),请选择分类:", msgCount), keyboard)
} else {
c.Bot().Send(c.Chat(), "📁 请选择分类:", keyboard)
}
}
func (b *Bot) buildCategoryKeyboard(categories []storage.Category) *tele.ReplyMarkup {
menu := &tele.ReplyMarkup{}
var rows []tele.Row
@@ -208,6 +293,14 @@ func (b *Bot) handleCallback(c tele.Context) error {
case strings.HasPrefix(data, cbPrefixConfirm):
action := strings.TrimPrefix(data, cbPrefixConfirm)
return b.handleConfirmCallback(c, userID, action)
case strings.HasPrefix(data, cbPrefixDelWithMsg):
entryID := strings.TrimPrefix(data, cbPrefixDelWithMsg)
return b.handleDeleteEntryCallback(c, entryID, true)
case strings.HasPrefix(data, cbPrefixDelOnlyToc):
entryID := strings.TrimPrefix(data, cbPrefixDelOnlyToc)
return b.handleDeleteEntryCallback(c, entryID, false)
}
return c.Respond()
@@ -325,12 +418,20 @@ func (b *Bot) handleConfirmCallback(c tele.Context, userID int64, action string)
defer b.states.Delete(userID)
msg := state.ForwardedMsg
title := state.Title
// 复制消息内容发送到频道(非转发,可编辑)
channel := &tele.Chat{ID: b.cfg.Channel.ID}
channelMsg, err := b.sendMessageCopy(channel, msg)
var channelMsg *tele.Message
var err error
// 相册消息
if len(state.ForwardedMsgs) > 1 {
channelMsg, err = b.sendAlbumCopy(channel, state.ForwardedMsgs)
} else {
// 单条消息
channelMsg, err = b.sendMessageCopy(channel, state.ForwardedMsg)
}
if err != nil {
c.Edit(fmt.Sprintf("❌ 发送到频道失败: %v", err))
return c.Respond(&tele.CallbackResponse{Text: "发送失败"})
@@ -404,6 +505,47 @@ func (b *Bot) sendMessageCopy(to *tele.Chat, msg *tele.Message) (*tele.Message,
return nil, fmt.Errorf("不支持的消息类型")
}
// sendAlbumCopy 复制相册发送到频道,返回第一条消息
func (b *Bot) sendAlbumCopy(to *tele.Chat, msgs []*tele.Message) (*tele.Message, error) {
if len(msgs) == 0 {
return nil, fmt.Errorf("相册为空")
}
var album tele.Album
for i, msg := range msgs {
var caption string
if i == 0 {
// 只有第一条消息带 caption
caption = msg.Caption
}
if msg.Photo != nil {
album = append(album, &tele.Photo{File: msg.Photo.File, Caption: caption})
} else if msg.Video != nil {
album = append(album, &tele.Video{File: msg.Video.File, Caption: caption})
} else if msg.Document != nil {
album = append(album, &tele.Document{File: msg.Document.File, Caption: caption})
} else if msg.Audio != nil {
album = append(album, &tele.Audio{File: msg.Audio.File, Caption: caption})
}
}
if len(album) == 0 {
return nil, fmt.Errorf("相册中没有支持的媒体类型")
}
sentMsgs, err := b.bot.SendAlbum(to, album, tele.ModeHTML)
if err != nil {
return nil, err
}
if len(sentMsgs) == 0 {
return nil, fmt.Errorf("发送相册失败")
}
return &sentMsgs[0], nil
}
func buildChannelLink(channelID int64, msgID int) string {
chatID := channelID
if chatID < 0 {