Skip to content

Commit abc6525

Browse files
committed
feat: refactor getArtworkFiles to improve media handling and batch sending
1 parent 2ac26a0 commit abc6525

File tree

1 file changed

+139
-156
lines changed

1 file changed

+139
-156
lines changed

internal/interface/telegram/handlers/get_file.go

Lines changed: 139 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)