fix: implement watch media group handler and enhance media processing logic
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -10,3 +10,5 @@ temp/
|
||||
.hugo_build.lock
|
||||
playwright/
|
||||
testplugins/
|
||||
*.exe
|
||||
tmp-*
|
||||
@@ -26,7 +26,7 @@ func BuildDirHelpStyling(dirs []database.Dir) []styling.StyledTextOption {
|
||||
styling.Blockquote(func() string {
|
||||
var sb strings.Builder
|
||||
for _, dir := range dirs {
|
||||
sb.WriteString(fmt.Sprintf("%d: ", dir.ID))
|
||||
fmt.Fprintf(&sb, "%d: ", dir.ID)
|
||||
sb.WriteString(dir.StorageName)
|
||||
sb.WriteString(" - ")
|
||||
sb.WriteString(dir.Path)
|
||||
|
||||
@@ -5,7 +5,9 @@ import (
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/celestix/gotgproto/dispatcher"
|
||||
"github.com/celestix/gotgproto/ext"
|
||||
@@ -16,10 +18,12 @@ import (
|
||||
"github.com/krau/SaveAny-Bot/common/i18n"
|
||||
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
|
||||
"github.com/krau/SaveAny-Bot/common/utils/tgutil"
|
||||
"github.com/krau/SaveAny-Bot/config"
|
||||
"github.com/krau/SaveAny-Bot/core"
|
||||
"github.com/krau/SaveAny-Bot/core/tasks/tfile"
|
||||
coretfile "github.com/krau/SaveAny-Bot/core/tasks/tfile"
|
||||
"github.com/krau/SaveAny-Bot/database"
|
||||
"github.com/krau/SaveAny-Bot/pkg/enums/fnamest"
|
||||
"github.com/krau/SaveAny-Bot/pkg/tfile"
|
||||
"github.com/krau/SaveAny-Bot/storage"
|
||||
"github.com/rs/xid"
|
||||
)
|
||||
@@ -151,6 +155,53 @@ func handleUnwatchCmd(ctx *ext.Context, update *ext.Update) error {
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
|
||||
type watchMediaGroupHandler struct {
|
||||
groups map[int64]map[uint][]tfile.TGFileMessage // chatID -> userID -> files
|
||||
timers map[int64]map[uint]*time.Timer
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var watchMediaGroupMgr = &watchMediaGroupHandler{
|
||||
groups: make(map[int64]map[uint][]tfile.TGFileMessage),
|
||||
timers: make(map[int64]map[uint]*time.Timer),
|
||||
}
|
||||
|
||||
func (w *watchMediaGroupHandler) addFile(chatID int64, userID uint, file tfile.TGFileMessage, timeout time.Duration, callback func([]tfile.TGFileMessage)) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
if w.groups[chatID] == nil {
|
||||
w.groups[chatID] = make(map[uint][]tfile.TGFileMessage)
|
||||
}
|
||||
if w.timers[chatID] == nil {
|
||||
w.timers[chatID] = make(map[uint]*time.Timer)
|
||||
}
|
||||
|
||||
if timer, exists := w.timers[chatID][userID]; exists {
|
||||
timer.Stop()
|
||||
}
|
||||
|
||||
w.groups[chatID][userID] = append(w.groups[chatID][userID], file)
|
||||
|
||||
w.timers[chatID][userID] = time.AfterFunc(timeout, func() {
|
||||
w.mu.Lock()
|
||||
files := w.groups[chatID][userID]
|
||||
delete(w.groups[chatID], userID)
|
||||
delete(w.timers[chatID], userID)
|
||||
if len(w.groups[chatID]) == 0 {
|
||||
delete(w.groups, chatID)
|
||||
}
|
||||
if len(w.timers[chatID]) == 0 {
|
||||
delete(w.timers, chatID)
|
||||
}
|
||||
w.mu.Unlock()
|
||||
|
||||
if len(files) > 0 {
|
||||
callback(files)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func listenMediaMessageEvent(ch chan userclient.MediaMessageEvent) {
|
||||
if userclient.GetCtx() == nil {
|
||||
return
|
||||
@@ -224,6 +275,24 @@ func listenMediaMessageEvent(ch chan userclient.MediaMessageEvent) {
|
||||
}
|
||||
file.SetName(sb.String())
|
||||
}
|
||||
|
||||
// Check if this is a media group and if rules specify NEW-FOR-ALBUM
|
||||
groupID, isGroup := file.Message().GetGroupedID()
|
||||
needAlbumHandling := false
|
||||
if isGroup && groupID != 0 && user.ApplyRule && user.Rules != nil {
|
||||
_, _, matchedDirPath := ruleutil.ApplyRule(ctx, user.Rules, ruleutil.NewInput(file))
|
||||
needAlbumHandling = matchedDirPath.NeedNewForAlbum()
|
||||
}
|
||||
|
||||
if needAlbumHandling {
|
||||
// For media groups with NEW-FOR-ALBUM rule, collect all files of the same group
|
||||
watchMediaGroupMgr.addFile(event.ChatID, user.ID, file, time.Duration(config.C().Telegram.MediaGroupTimeout)*time.Second, func(files []tfile.TGFileMessage) {
|
||||
processWatchMediaGroup(ctx, user, stor, "", files)
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// Process single file or media group without album folder creation
|
||||
var dirPath string
|
||||
if user.ApplyRule && user.Rules != nil {
|
||||
matched, matchedStorageName, matchedDirPath := ruleutil.ApplyRule(ctx, user.Rules, ruleutil.NewInput(file))
|
||||
@@ -243,7 +312,7 @@ func listenMediaMessageEvent(ch chan userclient.MediaMessageEvent) {
|
||||
storagePath := stor.JoinStoragePath(path.Join(dirPath, file.Name()))
|
||||
injectCtx := tgutil.ExtWithContext(ctx.Context, ctx)
|
||||
taskid := xid.New().String()
|
||||
task, err := tfile.NewTGFileTask(taskid, injectCtx, file, stor, storagePath, nil)
|
||||
task, err := coretfile.NewTGFileTask(taskid, injectCtx, file, stor, storagePath, nil)
|
||||
if err != nil {
|
||||
logger.Errorf("create task failed: %s", err)
|
||||
continue
|
||||
@@ -256,3 +325,97 @@ func listenMediaMessageEvent(ch chan userclient.MediaMessageEvent) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func processWatchMediaGroup(ctx *ext.Context, user *database.User, stor storage.Storage, dirPath string, files []tfile.TGFileMessage) {
|
||||
logger := log.FromContext(ctx)
|
||||
if len(files) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
useRule := user.ApplyRule && user.Rules != nil
|
||||
|
||||
applyRule := func(file tfile.TGFileMessage) (string, ruleutil.MatchedDirPath) {
|
||||
if !useRule {
|
||||
return stor.Name(), ruleutil.MatchedDirPath(dirPath)
|
||||
}
|
||||
matched, storName, dirP := ruleutil.ApplyRule(ctx, user.Rules, ruleutil.NewInput(file))
|
||||
if !matched {
|
||||
return stor.Name(), ruleutil.MatchedDirPath(dirPath)
|
||||
}
|
||||
storname := storName.String()
|
||||
if !storName.Usable() {
|
||||
storname = stor.Name()
|
||||
}
|
||||
return storname, dirP
|
||||
}
|
||||
|
||||
type albumFile struct {
|
||||
file tfile.TGFileMessage
|
||||
storage storage.Storage
|
||||
}
|
||||
albumFiles := make(map[int64][]albumFile)
|
||||
|
||||
// Collect files by group ID
|
||||
for _, file := range files {
|
||||
storName, ruleDirPath := applyRule(file)
|
||||
fileStor := stor
|
||||
if storName != stor.Name() && storName != "" {
|
||||
var err error
|
||||
fileStor, err = storage.GetStorageByUserIDAndName(ctx, user.ChatID, storName)
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to get storage by user ID and name: %s", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
groupId, isGroup := file.Message().GetGroupedID()
|
||||
if !isGroup || groupId == 0 {
|
||||
logger.Warnf("File %s is not in a group, skipping", file.Name())
|
||||
continue
|
||||
}
|
||||
|
||||
if !ruleDirPath.NeedNewForAlbum() {
|
||||
logger.Warnf("File %s does not need album folder, skipping", file.Name())
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := albumFiles[groupId]; !ok {
|
||||
albumFiles[groupId] = make([]albumFile, 0)
|
||||
}
|
||||
albumFiles[groupId] = append(albumFiles[groupId], albumFile{
|
||||
file: file,
|
||||
storage: fileStor,
|
||||
})
|
||||
}
|
||||
|
||||
// Process album files with folder creation
|
||||
injectCtx := tgutil.ExtWithContext(ctx.Context, ctx)
|
||||
totalTasks := 0
|
||||
for groupID, afiles := range albumFiles {
|
||||
if len(afiles) <= 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Use first file's name (without extension) as album folder name
|
||||
albumDir := strings.TrimSuffix(path.Base(afiles[0].file.Name()), path.Ext(afiles[0].file.Name()))
|
||||
albumStor := afiles[0].storage
|
||||
|
||||
logger.Infof("Creating album folder for group %d: %s with %d files", groupID, albumDir, len(afiles))
|
||||
|
||||
for _, af := range afiles {
|
||||
afstorPath := af.storage.JoinStoragePath(path.Join(dirPath, albumDir, af.file.Name()))
|
||||
taskid := xid.New().String()
|
||||
task, err := coretfile.NewTGFileTask(taskid, injectCtx, af.file, albumStor, afstorPath, nil)
|
||||
if err != nil {
|
||||
logger.Errorf("create task failed for album file: %s", err)
|
||||
continue
|
||||
}
|
||||
if err := core.AddTask(injectCtx, task); err != nil {
|
||||
logger.Errorf("add task failed: %s", err)
|
||||
continue
|
||||
}
|
||||
totalTasks++
|
||||
}
|
||||
}
|
||||
logger.Infof("Added %d watch media tasks for user %d", totalTasks, user.ChatID)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user