236 lines
7.3 KiB
Go
236 lines
7.3 KiB
Go
package bot
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/celestix/gotgproto/dispatcher"
|
|
"github.com/celestix/gotgproto/ext"
|
|
"github.com/gotd/td/tg"
|
|
"github.com/krau/SaveAny-Bot/common"
|
|
"github.com/krau/SaveAny-Bot/dao"
|
|
"github.com/krau/SaveAny-Bot/types"
|
|
"github.com/krau/SaveAny-Bot/userclient"
|
|
)
|
|
|
|
var (
|
|
linkRegexString = `https?://t\.me/(?:c/\d+|[a-zA-Z0-9_]+)/\d+(?:\?[^\s]*)?`
|
|
linkRegex = regexp.MustCompile(linkRegexString)
|
|
)
|
|
|
|
type parseResult struct {
|
|
ChatID int64
|
|
MessageID int
|
|
Files []*types.File
|
|
UserClient bool
|
|
}
|
|
|
|
func parseLink(ctx *ext.Context, link string) (*parseResult, error) {
|
|
u, err := url.Parse(link)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("无法解析链接: %s", err)
|
|
}
|
|
strSlice := strings.Split(u.Path, "/")
|
|
if len(strSlice) < 3 {
|
|
return nil, fmt.Errorf("链接格式错误: %s", link)
|
|
}
|
|
messageID, err := strconv.Atoi(strSlice[len(strSlice)-1])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("无法解析消息 ID: %s", err)
|
|
}
|
|
var chatID int64
|
|
if len(strSlice) == 3 {
|
|
chatUsername := strSlice[1]
|
|
peer := ctx.PeerStorage.GetPeerByUsername(chatUsername)
|
|
if peer != nil {
|
|
chatID = peer.ID
|
|
} else {
|
|
linkChat, err := ctx.ResolveUsername(chatUsername)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("解析用户名失败: %s", err)
|
|
}
|
|
if linkChat == nil {
|
|
return nil, fmt.Errorf("找不到该聊天: %s", chatUsername)
|
|
}
|
|
chatID = linkChat.GetID()
|
|
}
|
|
} else if len(strSlice) == 4 {
|
|
chatIDInt, err := strconv.Atoi(strSlice[2])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("无法解析 Chat ID: %s", err)
|
|
}
|
|
chatID = int64(chatIDInt)
|
|
} else {
|
|
return nil, errors.New("链接格式不正确,无法解析 Chat ID")
|
|
}
|
|
if chatID == 0 || messageID == 0 {
|
|
return nil, fmt.Errorf("链接中缺少 Chat ID 或 Message ID: %s", link)
|
|
}
|
|
msg, _, err := tryFetchMessage(ctx, chatID, messageID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("获取消息失败: %s", err)
|
|
}
|
|
mediaGroup, isGroup := msg.GetGroupedID()
|
|
if u.Query().Has("single") || !isGroup || (mediaGroup == 0) || userclient.UC == nil {
|
|
file, useUserClient, err := tryFetchFileFromMessage(ctx, chatID, messageID, "")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("获取文件失败: %s", err)
|
|
}
|
|
if file.FileName == "" {
|
|
file.FileName = GenFileNameFromMessage(*msg, file)
|
|
}
|
|
return &parseResult{
|
|
ChatID: chatID,
|
|
MessageID: messageID,
|
|
Files: []*types.File{file},
|
|
UserClient: useUserClient,
|
|
}, nil
|
|
}
|
|
groupMessages, isUserClient, err := tryGetMediaGroup(chatID, messageID, mediaGroup)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("获取媒体组消息失败: %s", err)
|
|
}
|
|
var files []*types.File
|
|
for _, groupMsg := range groupMessages {
|
|
file, err := FileFromMedia(groupMsg.Media, "")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("获取媒体文件失败: %s", err)
|
|
}
|
|
if file.FileName == "" {
|
|
file.FileName = GenFileNameFromMessage(*groupMsg, file)
|
|
}
|
|
files = append(files, file)
|
|
}
|
|
return &parseResult{
|
|
ChatID: chatID,
|
|
MessageID: messageID,
|
|
Files: files,
|
|
UserClient: isUserClient,
|
|
}, nil
|
|
}
|
|
|
|
// use passed ctx client to fetch file from message,
|
|
//
|
|
// if failed try using userclient
|
|
func tryFetchFileFromMessage(ctx *ext.Context, chatID int64, messageID int, fileName string) (*types.File, bool, error) {
|
|
file, err := FileFromMessage(ctx, chatID, messageID, fileName)
|
|
if err == nil {
|
|
return file, false, nil
|
|
}
|
|
if (strings.Contains(err.Error(), "peer not found") || strings.Contains(err.Error(), "unexpected message type")) && userclient.UC != nil {
|
|
common.Log.Warnf("无法获取文件 %d:%d, 尝试使用 userbot: %s", chatID, messageID, err)
|
|
uctx := userclient.GetCtx()
|
|
peer := uctx.PeerStorage.GetInputPeerById(chatID)
|
|
if peer == nil {
|
|
return nil, true, fmt.Errorf("failed to get peer for chat %d: %w", chatID, err)
|
|
}
|
|
msg, err := GetSingleHistoryMessage(uctx, uctx.Raw, peer, messageID)
|
|
if err != nil {
|
|
return nil, true, err
|
|
}
|
|
file, err = FileFromMedia(msg.Media, fileName)
|
|
if err != nil {
|
|
return nil, true, fmt.Errorf("failed to get file from userbot message %d:%d: %w", chatID, messageID, err)
|
|
}
|
|
return file, true, nil
|
|
}
|
|
return nil, false, err
|
|
}
|
|
|
|
func tryGetMediaGroup(chatID int64, messageID int, mediaGroupID int64) ([]*tg.Message, bool, error) {
|
|
if userclient.UC != nil {
|
|
uctx := userclient.GetCtx()
|
|
messages, err := GetMediaGroup(uctx, chatID, messageID, mediaGroupID)
|
|
if err != nil {
|
|
return nil, true, fmt.Errorf("failed to get media group from userbot: %w", err)
|
|
}
|
|
return messages, true, nil
|
|
}
|
|
return nil, false, errors.New("userclient is not available, cannot fetch media group")
|
|
}
|
|
|
|
func tryFetchMessage(ctx *ext.Context, chatID int64, messageID int) (*tg.Message, bool, error) {
|
|
msg, err := GetTGMessage(ctx, chatID, messageID)
|
|
if err == nil {
|
|
return msg, false, nil
|
|
}
|
|
if userclient.UC != nil && (strings.Contains(err.Error(), "peer not found") || strings.Contains(err.Error(), "unexpected message type")) {
|
|
common.Log.Warnf("无法获取消息 %d:%d, 尝试使用 userbot: %s", chatID, messageID, err)
|
|
uctx := userclient.GetCtx()
|
|
msg, err := GetTGMessage(uctx, chatID, messageID)
|
|
if err == nil {
|
|
return msg, true, nil
|
|
}
|
|
return nil, true, fmt.Errorf("获取消息失败: %w", err)
|
|
}
|
|
return nil, false, fmt.Errorf("获取消息失败: %s", err)
|
|
}
|
|
|
|
func handleLinkMessage(ctx *ext.Context, update *ext.Update) error {
|
|
common.Log.Trace("Got link message")
|
|
link := linkRegex.FindString(update.EffectiveMessage.Text)
|
|
if link == "" {
|
|
return dispatcher.ContinueGroups
|
|
}
|
|
result, err := parseLink(ctx, link)
|
|
if err != nil {
|
|
common.Log.Errorf("解析链接失败: %s", err)
|
|
ctx.Reply(update, ext.ReplyTextString("解析链接失败"), nil)
|
|
return dispatcher.EndGroups
|
|
}
|
|
|
|
user, err := dao.GetUserByChatID(update.GetUserChat().GetID())
|
|
if err != nil {
|
|
common.Log.Errorf("获取用户失败: %s", err)
|
|
ctx.Reply(update, ext.ReplyTextString("获取用户失败"), nil)
|
|
return dispatcher.EndGroups
|
|
}
|
|
|
|
replied, err := ctx.Reply(update, ext.ReplyTextString("正在获取文件..."), nil)
|
|
if err != nil {
|
|
common.Log.Errorf("回复失败: %s", err)
|
|
return dispatcher.EndGroups
|
|
}
|
|
|
|
// TODO: handle group files
|
|
receivedFile := &dao.ReceivedFile{
|
|
Processing: false,
|
|
FileName: result.Files[0].FileName,
|
|
ChatID: result.ChatID,
|
|
MessageID: result.MessageID,
|
|
ReplyMessageID: replied.ID,
|
|
ReplyChatID: update.GetUserChat().GetID(),
|
|
UseUserClient: result.UserClient,
|
|
}
|
|
record, err := dao.SaveReceivedFile(receivedFile)
|
|
if err != nil {
|
|
common.Log.Errorf("保存接收的文件失败: %s", err)
|
|
ctx.EditMessage(update.EffectiveChat().GetID(), &tg.MessagesEditMessageRequest{
|
|
Message: "无法保存文件: " + err.Error(),
|
|
ID: replied.ID,
|
|
})
|
|
return dispatcher.EndGroups
|
|
}
|
|
file := result.Files[0]
|
|
if !user.Silent || user.DefaultStorage == "" {
|
|
return ProvideSelectMessage(ctx, update, file.FileName, result.ChatID, result.MessageID, replied.ID)
|
|
}
|
|
return HandleSilentAddTask(ctx, update, user, &types.Task{
|
|
Ctx: ctx,
|
|
Status: types.Pending,
|
|
FileDBID: record.ID,
|
|
UseUserClient: result.UserClient,
|
|
File: file,
|
|
StorageName: user.DefaultStorage,
|
|
UserID: user.ChatID,
|
|
FileChatID: result.ChatID,
|
|
FileMessageID: result.MessageID,
|
|
ReplyMessageID: replied.ID,
|
|
ReplyChatID: update.GetUserChat().GetID(),
|
|
})
|
|
}
|