11package handlers
22
33import (
4+ "fmt"
5+ "sync"
6+ "time"
7+
48 "github.com/celestix/gotgproto/dispatcher"
59 "github.com/celestix/gotgproto/ext"
610 "github.com/charmbracelet/log"
11+ "github.com/gotd/td/tg"
12+ "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/mediautil"
713 "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem"
814 "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/shortcut"
15+ "github.com/krau/SaveAny-Bot/common/utils/tgutil"
16+ "github.com/krau/SaveAny-Bot/pkg/tcbdata"
17+ "github.com/krau/SaveAny-Bot/pkg/tfile"
918 "github.com/krau/SaveAny-Bot/storage"
1019)
1120
1221func handleMediaMessage (ctx * ext.Context , update * ext.Update ) error {
1322 logger := log .FromContext (ctx )
1423 message := update .EffectiveMessage .Message
24+ groupID , isGroup := message .GetGroupedID ()
25+ if isGroup && groupID != 0 {
26+ return handleGroupMediaMessage (ctx , update , message , groupID )
27+ }
1528 logger .Debugf ("Got media: %s" , message .Media .TypeName ())
29+
1630 msg , file , err := shortcut .GetFileFromMessageWithReply (ctx , update , message )
1731 if err != nil {
1832 return err
@@ -38,6 +52,10 @@ func handleSilentSaveMedia(ctx *ext.Context, update *ext.Update) error {
3852 return dispatcher .EndGroups
3953 }
4054 message := update .EffectiveMessage .Message
55+ groupID , isGroup := message .GetGroupedID ()
56+ if isGroup && groupID != 0 {
57+ return handleGroupMediaMessage (ctx , update , message , groupID )
58+ }
4159 logger .Debugf ("Got media: %s" , message .Media .TypeName ())
4260 userID := update .GetUserChat ().GetID ()
4361 msg , file , err := shortcut .GetFileFromMessageWithReply (ctx , update , message )
@@ -46,3 +64,96 @@ func handleSilentSaveMedia(ctx *ext.Context, update *ext.Update) error {
4664 }
4765 return shortcut .CreateAndAddTGFileTaskWithEdit (ctx , userID , stor , "" , file , msg .ID )
4866}
67+
68+ type MediaGroupHandler struct {
69+ groups map [int64 ][]tfile.TGFileMessage
70+ timers map [int64 ]* time.Timer
71+ mu sync.Mutex
72+ timeout time.Duration
73+ }
74+
75+ var mediaGroupHandler = & MediaGroupHandler {
76+ groups : make (map [int64 ][]tfile.TGFileMessage ),
77+ timers : make (map [int64 ]* time.Timer ),
78+ timeout : 1 * time .Second ,
79+ }
80+
81+ func handleGroupMediaMessage (ctx * ext.Context , update * ext.Update , message * tg.Message , groupID int64 ) error {
82+ logger := log .FromContext (ctx )
83+ media := message .Media
84+ supported := mediautil .IsSupported (media )
85+ if ! supported {
86+ return dispatcher .EndGroups
87+ }
88+ file , err := tfile .FromMediaMessage (media , ctx .Raw , message , tfile .WithNameIfEmpty (
89+ tgutil .GenFileNameFromMessage (* message ),
90+ ))
91+ if err != nil {
92+ logger .Errorf ("Failed to get file from media: %s" , err )
93+ return dispatcher .EndGroups
94+ }
95+ mediaGroupHandler .mu .Lock ()
96+ defer mediaGroupHandler .mu .Unlock ()
97+ if mediaGroupHandler .groups [groupID ] == nil {
98+ mediaGroupHandler .groups [groupID ] = make ([]tfile.TGFileMessage , 0 )
99+ }
100+ mediaGroupHandler .groups [groupID ] = append (mediaGroupHandler .groups [groupID ], file )
101+
102+ if timer , exists := mediaGroupHandler .timers [groupID ]; exists {
103+ timer .Stop ()
104+ }
105+ mediaGroupHandler .timers [groupID ] = time .AfterFunc (mediaGroupHandler .timeout , func () {
106+ processMediaGroup (ctx , update , groupID )
107+ })
108+ return dispatcher .EndGroups
109+ }
110+
111+ func processMediaGroup (ctx * ext.Context , update * ext.Update , groupID int64 ) {
112+ logger := log .FromContext (ctx )
113+ mediaGroupHandler .mu .Lock ()
114+ items := mediaGroupHandler .groups [groupID ]
115+ delete (mediaGroupHandler .groups , groupID )
116+ delete (mediaGroupHandler .timers , groupID )
117+ mediaGroupHandler .mu .Unlock ()
118+ if len (items ) == 0 {
119+ logger .Warn ("No media items to process for group" , "groupID" , groupID )
120+ return
121+ }
122+ logger .Debugf ("Processing media group %d with %d items" , groupID , len (items ))
123+
124+ userId := update .GetUserChat ().GetID ()
125+ msg , err := ctx .Reply (update , ext .ReplyTextString ("正在保存文件..." ), nil )
126+ if err != nil {
127+ logger .Errorf ("Failed to reply: %s" , err )
128+ return
129+ }
130+ stor := storage .FromContext (ctx )
131+ if stor != nil {
132+ // In silent mode
133+ if len (items ) == 1 {
134+ shortcut .CreateAndAddTGFileTaskWithEdit (ctx , userId , stor , "" , items [0 ], msg .ID )
135+ return
136+ }
137+ shortcut .CreateAndAddBatchTGFileTaskWithEdit (ctx , userId , stor , "" , items , msg .ID )
138+ return
139+ }
140+
141+ stors := storage .GetUserStorages (ctx , userId )
142+ markup , err := msgelem .BuildAddSelectStorageKeyboard (stors , tcbdata.Add {
143+ Files : items ,
144+ AsBatch : len (items ) > 1 ,
145+ })
146+ if err != nil {
147+ logger .Errorf ("构建存储选择键盘失败: %s" , err )
148+ ctx .EditMessage (userId , & tg.MessagesEditMessageRequest {
149+ ID : msg .ID ,
150+ Message : "构建存储选择键盘失败: " + err .Error (),
151+ })
152+ return
153+ }
154+ ctx .EditMessage (userId , & tg.MessagesEditMessageRequest {
155+ ID : msg .ID ,
156+ Message : fmt .Sprintf ("共 %d 个文件, 请选择存储位置" , len (items )),
157+ ReplyMarkup : markup ,
158+ })
159+ }
0 commit comments