@@ -14,6 +14,7 @@ import (
1414 "github.com/krau/ManyACG/internal/service"
1515 "github.com/krau/ManyACG/internal/shared"
1616 "github.com/krau/ManyACG/internal/shared/errs"
17+ "github.com/krau/ManyACG/pkg/ioutil"
1718 "github.com/krau/ManyACG/pkg/log"
1819 "github.com/mymmrac/telego"
1920 "github.com/mymmrac/telego/telegohandler"
@@ -95,211 +96,193 @@ func getArtworkFiles(ctx *telegohandler.Context,
9596 }()
9697 }
9798 var errs []error
99+
100+ type fileMediaItem struct {
101+ picture shared.PictureLike
102+ ugoira shared.UgoiraMetaLike
103+ video shared.VideoLike
104+ index int
105+ kind string
106+ }
107+ const (
108+ fileMediaPicture = "picture"
109+ fileMediaUgoira = "ugoira"
110+ fileMediaVideo = "video"
111+ )
112+
113+ items := make ([]fileMediaItem , 0 )
98114 for i , picture := range artwork .GetPictures () {
99- err := func () error {
100- buildDocument := func () (* telego.SendDocumentParams , func () error , error ) {
101- file , err := utils .GetPictureDocumentInputFile (ctx , serv , meta , artwork , picture )
102- if err != nil {
103- return nil , nil , oops .Wrapf (err , "failed to get picture document input file" )
104- }
105- document := telegoutil .Document (message .Chat .ChatID (), file .Value ).
106- WithReplyParameters (& telego.ReplyParameters {
107- MessageID : message .MessageID ,
108- }).WithCaption (artwork .GetTitle () + "_" + strconv .Itoa (i + 1 )).WithDisableContentTypeDetection ()
109- if meta .ChannelAvailable () && picture .GetTelegramInfo ().MessageID (meta .ChannelChatID ().ID ) != 0 {
110- document .WithReplyMarkup (telegoutil .InlineKeyboard ([]telego.InlineKeyboardButton {
111- telegoutil .InlineKeyboardButton ("详情" ).WithURL (meta .ChannelMessageURL (picture .GetTelegramInfo ().MessageID (meta .ChannelChatID ().ID ))),
112- }))
113- } else {
114- document .WithReplyMarkup (telegoutil .InlineKeyboard ([]telego.InlineKeyboardButton {
115- telegoutil .InlineKeyboardButton ("详情" ).WithURL (artwork .GetSourceURL ()),
116- }))
117- }
118- return document , file .Close , nil
119- }
115+ items = append (items , fileMediaItem {picture : picture , index : i , kind : fileMediaPicture })
116+ }
117+ for i , ugoira := range artwork .GetUgoiraMetas () {
118+ items = append (items , fileMediaItem {ugoira : ugoira , index : i , kind : fileMediaUgoira })
119+ }
120+ for i , video := range artwork .GetVideos () {
121+ items = append (items , fileMediaItem {video : video , index : i , kind : fileMediaVideo })
122+ }
123+ if len (items ) == 0 {
124+ return nil
125+ }
120126
121- document , close , err := buildDocument ()
122- if err != nil {
123- return oops .Wrapf (err , "failed to build document" )
127+ var cachedData * entity.CachedArtworkData
128+ var cachedUpdated bool
129+ getCachedData := func () (* entity.CachedArtworkData , error ) {
130+ if cachedData != nil {
131+ return cachedData , nil
132+ }
133+ cached , err := serv .GetCachedArtworkByURL (ctx , artwork .GetSourceURL ())
134+ if err != nil {
135+ return nil , oops .Wrapf (err , "failed to get cached artwork by url: %s" , artwork .GetSourceURL ())
136+ }
137+ cachedData = cached .Artwork .Data ()
138+ return cachedData , nil
139+ }
140+
141+ sendBatch := func (start , end int ) error {
142+ inputs := make ([]telego.InputMedia , 0 , end - start )
143+ closers := make ([]func () error , 0 , end - start )
144+ for i := start ; i < end ; i ++ {
145+ item := items [i ]
146+ var file * ioutil.Closer [telego.InputFile ]
147+ var err error
148+ switch item .kind {
149+ case fileMediaPicture :
150+ file , err = utils .GetPictureDocumentInputFile (ctx , serv , meta , artwork , item .picture )
151+ case fileMediaUgoira :
152+ file , err = utils .GetUgoiraVideoDocumentInputFile (ctx , serv , meta , artwork , item .ugoira )
153+ case fileMediaVideo :
154+ file , err = utils .GetVideoDocumentInputFile (ctx , serv , meta , artwork , item .video )
155+ default :
156+ err = oops .Errorf ("unknown media kind: %s" , item .kind )
124157 }
125- defer close ()
126- documentMessage , err := ctx .Bot ().SendDocument (ctx , document )
127158 if err != nil {
128- ctx .Bot ().SendMessage (ctx , telegoutil .Messagef (
129- message .Chat .ChatID (),
130- "发送第 %d 张图片时失败" ,
131- i + 1 ,
132- ).WithReplyParameters (& telego.ReplyParameters {
133- MessageID : message .MessageID ,
134- }))
135- return oops .Wrapf (err , "failed to send document" )
159+ for _ , close := range closers {
160+ close ()
161+ }
162+ return err
136163 }
137- if documentMessage != nil && documentMessage .Document != nil {
138- switch pic := picture .(type ) {
164+ closers = append (closers , file .Close )
165+ caption := artwork .GetTitle () + "_" + strconv .Itoa (item .index + 1 )
166+ doc := telegoutil .MediaDocument (file .Value ).
167+ WithCaption (caption ).
168+ WithDisableContentTypeDetection ()
169+ inputs = append (inputs , telego .InputMedia (doc ))
170+ }
171+ defer func () {
172+ for _ , close := range closers {
173+ close ()
174+ }
175+ }()
176+
177+ group := telegoutil .MediaGroup (message .Chat .ChatID (), inputs ... ).WithReplyParameters (& telego.ReplyParameters {
178+ MessageID : message .MessageID ,
179+ })
180+ msgs , err := ctx .Bot ().SendMediaGroup (ctx , group )
181+ if err != nil {
182+ return oops .Wrapf (err , "failed to send media group" )
183+ }
184+
185+ for i , msg := range msgs {
186+ if msg .Document == nil {
187+ continue
188+ }
189+ item := items [start + i ]
190+ switch item .kind {
191+ case fileMediaPicture :
192+ switch pic := item .picture .(type ) {
139193 case * entity.Picture :
140194 tginfo := pic .GetTelegramInfo ()
141- tginfo .SetFileID (meta .BotID (), shared .TelegramMediaTypeDocument , documentMessage .Document .FileID )
142- return serv .UpdatePictureTelegramInfo (ctx , pic .ID , & tginfo )
195+ tginfo .SetFileID (meta .BotID (), shared .TelegramMediaTypeDocument , msg .Document .FileID )
196+ if err := serv .UpdatePictureTelegramInfo (ctx , pic .ID , & tginfo ); err != nil {
197+ return err
198+ }
143199 case * entity.CachedPicture :
144- cached , err := serv . GetCachedArtworkByURL ( ctx , artwork . GetSourceURL () )
200+ data , err := getCachedData ( )
145201 if err != nil {
146- return oops . Wrapf ( err , "failed to get cached artwork by url: %s" , artwork . GetSourceURL ())
202+ return err
147203 }
148- data := cached .Artwork .Data ()
149204 for _ , p := range data .Pictures {
150205 if p .Original == pic .GetOriginal () {
151206 tginfo := pic .GetTelegramInfo ()
152- tginfo .SetFileID (meta .BotID (), shared .TelegramMediaTypeDocument , documentMessage .Document .FileID )
207+ tginfo .SetFileID (meta .BotID (), shared .TelegramMediaTypeDocument , msg .Document .FileID )
153208 p .TelegramInfo = tginfo
154- return serv .UpdateCachedArtwork (ctx , data )
209+ cachedUpdated = true
210+ break
155211 }
156212 }
157213 default :
158214 log .Warnf ("unknown picture type: %T" , pic )
159215 }
160- }
161- return nil
162- }()
163- if err != nil {
164- errs = append (errs , oops .Wrapf (err , "failed to send picture %d file" , i + 1 ))
165- }
166- }
167- for i , ugoira := range artwork .GetUgoiraMetas () {
168- err := func () error {
169- buildDocument := func () (* telego.SendDocumentParams , func () error , error ) {
170- file , err := utils .GetUgoiraVideoDocumentInputFile (ctx , serv , meta , artwork , ugoira )
171- if err != nil {
172- return nil , nil , oops .Wrapf (err , "failed to get ugoira video document input file" )
173- }
174- document := telegoutil .Document (message .Chat .ChatID (), file .Value ).
175- WithReplyParameters (& telego.ReplyParameters {
176- MessageID : message .MessageID ,
177- }).WithCaption (artwork .GetTitle () + "_" + strconv .Itoa (i + 1 )).WithDisableContentTypeDetection ()
178- if meta .ChannelAvailable () && ugoira .GetTelegramInfo ().MessageID (meta .ChannelChatID ().ID ) != 0 {
179- document .WithReplyMarkup (telegoutil .InlineKeyboard ([]telego.InlineKeyboardButton {
180- telegoutil .InlineKeyboardButton ("详情" ).WithURL (meta .ChannelMessageURL (ugoira .GetTelegramInfo ().MessageID (meta .ChannelChatID ().ID ))),
181- }))
182- } else {
183- document .WithReplyMarkup (telegoutil .InlineKeyboard ([]telego.InlineKeyboardButton {
184- telegoutil .InlineKeyboardButton ("详情" ).WithURL (artwork .GetSourceURL ()),
185- }))
186- }
187- return document , file .Close , nil
188- }
189- document , close , err := buildDocument ()
190- if err != nil {
191- return oops .Wrapf (err , "failed to build document" )
192- }
193- defer close ()
194- documentMessage , err := ctx .Bot ().SendDocument (ctx , document )
195- if err != nil {
196- ctx .Bot ().SendMessage (ctx , telegoutil .Messagef (
197- message .Chat .ChatID (),
198- "发送第 %d 个动图时失败" ,
199- i + 1 ,
200- ).WithReplyParameters (& telego.ReplyParameters {
201- MessageID : message .MessageID ,
202- }))
203- return oops .Wrapf (err , "failed to send document" )
204- }
205- if documentMessage != nil && documentMessage .Document != nil {
206- switch ugo := ugoira .(type ) {
216+ case fileMediaUgoira :
217+ switch ugo := item .ugoira .(type ) {
207218 case * entity.UgoiraMeta :
208219 tginfo := ugo .GetTelegramInfo ()
209- tginfo .SetFileID (meta .BotID (), shared .TelegramMediaTypeDocument , documentMessage .Document .FileID )
210- return serv .UpdateUgoiraTelegramInfo (ctx , ugo .ID , & tginfo )
220+ tginfo .SetFileID (meta .BotID (), shared .TelegramMediaTypeDocument , msg .Document .FileID )
221+ if err := serv .UpdateUgoiraTelegramInfo (ctx , ugo .ID , & tginfo ); err != nil {
222+ return err
223+ }
211224 case * entity.CachedUgoiraMeta :
212- cached , err := serv . GetCachedArtworkByURL ( ctx , artwork . GetSourceURL () )
225+ data , err := getCachedData ( )
213226 if err != nil {
214- return oops . Wrapf ( err , "failed to get cached artwork by url: %s" , artwork . GetSourceURL ())
227+ return err
215228 }
216- data := cached .Artwork .Data ()
217229 for _ , u := range data .UgoiraMetas {
218230 if u .MetaData .OriginalZip == ugo .MetaData .OriginalZip {
219231 tginfo := ugo .GetTelegramInfo ()
220- tginfo .SetFileID (meta .BotID (), shared .TelegramMediaTypeDocument , documentMessage .Document .FileID )
232+ tginfo .SetFileID (meta .BotID (), shared .TelegramMediaTypeDocument , msg .Document .FileID )
221233 u .TelegramInfo = tginfo
222- return serv .UpdateCachedArtwork (ctx , data )
234+ cachedUpdated = true
235+ break
223236 }
224237 }
225238 default :
226239 log .Warnf ("unknown ugoira type: %T" , ugo )
227240 }
228- }
229- return nil
230- }()
231- if err != nil {
232- errs = append (errs , oops .Wrapf (err , "failed to send ugoira %d file" , i + 1 ))
233- }
234- }
235- for i , video := range artwork .GetVideos () {
236- err := func () error {
237- buildDocument := func () (* telego.SendDocumentParams , func () error , error ) {
238- file , err := utils .GetVideoDocumentInputFile (ctx , serv , meta , artwork , video )
239- if err != nil {
240- return nil , nil , oops .Wrapf (err , "failed to get video document input file" )
241- }
242- document := telegoutil .Document (message .Chat .ChatID (), file .Value ).
243- WithReplyParameters (& telego.ReplyParameters {
244- MessageID : message .MessageID ,
245- }).WithCaption (artwork .GetTitle () + "_" + strconv .Itoa (i + 1 )).
246- WithDisableContentTypeDetection ()
247- if meta .ChannelAvailable () && video .GetTelegramInfo ().MessageID (meta .ChannelChatID ().ID ) != 0 {
248- document .WithReplyMarkup (telegoutil .InlineKeyboard ([]telego.InlineKeyboardButton {
249- telegoutil .InlineKeyboardButton ("详情" ).WithURL (meta .ChannelMessageURL (video .GetTelegramInfo ().MessageID (meta .ChannelChatID ().ID ))),
250- }))
251- } else {
252- document .WithReplyMarkup (telegoutil .InlineKeyboard ([]telego.InlineKeyboardButton {
253- telegoutil .InlineKeyboardButton ("详情" ).WithURL (artwork .GetSourceURL ()),
254- }))
255- }
256- return document , file .Close , nil
257- }
258- document , close , err := buildDocument ()
259- if err != nil {
260- return oops .Wrapf (err , "failed to build document" )
261- }
262- defer close ()
263- documentMessage , err := ctx .Bot ().SendDocument (ctx , document )
264- if err != nil {
265- ctx .Bot ().SendMessage (ctx , telegoutil .Messagef (
266- message .Chat .ChatID (),
267- "发送第 %d 个视频时失败" ,
268- i + 1 ,
269- ).WithReplyParameters (& telego.ReplyParameters {
270- MessageID : message .MessageID ,
271- }))
272- return oops .Wrapf (err , "failed to send document" )
273- }
274- if documentMessage != nil && documentMessage .Document != nil {
275- switch vid := video .(type ) {
241+ case fileMediaVideo :
242+ switch vid := item .video .(type ) {
276243 case * entity.Video :
277244 tginfo := vid .GetTelegramInfo ()
278- tginfo .SetFileID (meta .BotID (), shared .TelegramMediaTypeDocument , documentMessage .Document .FileID )
279- return serv .UpdateVideoTelegramInfo (ctx , vid .ID , & tginfo )
245+ tginfo .SetFileID (meta .BotID (), shared .TelegramMediaTypeDocument , msg .Document .FileID )
246+ if err := serv .UpdateVideoTelegramInfo (ctx , vid .ID , & tginfo ); err != nil {
247+ return err
248+ }
280249 case * entity.CachedVideo :
281- cached , err := serv . GetCachedArtworkByURL ( ctx , artwork . GetSourceURL () )
250+ data , err := getCachedData ( )
282251 if err != nil {
283- return oops . Wrapf ( err , "failed to get cached artwork by url: %s" , artwork . GetSourceURL ())
252+ return err
284253 }
285- data := cached .Artwork .Data ()
286254 for _ , v := range data .Videos {
287255 if v .URL == vid .URL {
288256 tginfo := vid .GetTelegramInfo ()
289- tginfo .SetFileID (meta .BotID (), shared .TelegramMediaTypeDocument , documentMessage .Document .FileID )
257+ tginfo .SetFileID (meta .BotID (), shared .TelegramMediaTypeDocument , msg .Document .FileID )
290258 v .TelegramInfo = tginfo
291- return serv .UpdateCachedArtwork (ctx , data )
259+ cachedUpdated = true
260+ break
292261 }
293262 }
294263 default :
295264 log .Warnf ("unknown video type: %T" , vid )
296265 }
297266 }
298- return nil
299- }()
300- if err != nil {
301- errs = append (errs , oops .Wrapf (err , "failed to send video %d file" , i + 1 ))
302267 }
268+ return nil
303269 }
270+
271+ for i := 0 ; i < len (items ); i += 10 {
272+ end := i + 10
273+ if end > len (items ) {
274+ end = len (items )
275+ }
276+ if err := sendBatch (i , end ); err != nil {
277+ errs = append (errs , oops .Wrapf (err , "failed to send files %d-%d" , i + 1 , end ))
278+ }
279+ }
280+
281+ if cachedUpdated && cachedData != nil {
282+ if err := serv .UpdateCachedArtwork (ctx , cachedData ); err != nil {
283+ errs = append (errs , oops .Wrapf (err , "failed to update cached artwork" ))
284+ }
285+ }
286+
304287 return oops .Join (errs ... )
305288}
0 commit comments