|
| 1 | +package botapi |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + |
| 6 | + "github.com/gotd/td/telegram/message" |
| 7 | + "github.com/gotd/td/telegram/message/styling" |
| 8 | + "github.com/gotd/td/tg" |
| 9 | +) |
| 10 | + |
| 11 | +// errNonUploadInAlbum is returned for album items that reference a file_id or |
| 12 | +// URL. The high-level send API can only place freshly uploaded files in a group. |
| 13 | +var errNonUploadInAlbum = &Error{ |
| 14 | + Code: 400, |
| 15 | + Description: "Bad Request: media group items must be uploaded files (file_id and URL are not supported yet)", |
| 16 | +} |
| 17 | + |
| 18 | +// photoMulti resolves a photo InputFile into an album item. Only uploads are |
| 19 | +// supported in groups. |
| 20 | +func (b *Bot) photoMulti(ctx context.Context, file InputFile, caption []styling.StyledTextOption) (message.MultiMediaOption, error) { |
| 21 | + up, ok := file.(*InputFileUpload) |
| 22 | + if !ok { |
| 23 | + return nil, errNonUploadInAlbum |
| 24 | + } |
| 25 | + upFile, err := b.uploadInputFile(ctx, up) |
| 26 | + if err != nil { |
| 27 | + return nil, err |
| 28 | + } |
| 29 | + return message.UploadedPhoto(upFile, caption...), nil |
| 30 | +} |
| 31 | + |
| 32 | +// docMulti resolves a document-family InputFile into an album item, applying the |
| 33 | +// typed attribute builder. Only uploads are supported in groups. |
| 34 | +func (b *Bot) docMulti( |
| 35 | + ctx context.Context, |
| 36 | + file InputFile, |
| 37 | + caption []styling.StyledTextOption, |
| 38 | + typed func(*message.UploadedDocumentBuilder) message.MultiMediaOption, |
| 39 | +) (message.MultiMediaOption, error) { |
| 40 | + up, ok := file.(*InputFileUpload) |
| 41 | + if !ok { |
| 42 | + return nil, errNonUploadInAlbum |
| 43 | + } |
| 44 | + upFile, err := b.uploadInputFile(ctx, up) |
| 45 | + if err != nil { |
| 46 | + return nil, err |
| 47 | + } |
| 48 | + doc := message.UploadedDocument(upFile, caption...) |
| 49 | + if up.Name != "" { |
| 50 | + doc = doc.Filename(up.Name) |
| 51 | + } |
| 52 | + return typed(doc), nil |
| 53 | +} |
| 54 | + |
| 55 | +// inputMediaToMulti resolves a single InputMedia into an album item. |
| 56 | +// |
| 57 | +// The switch over the sealed InputMedia union is exhaustive. |
| 58 | +func (b *Bot) inputMediaToMulti(ctx context.Context, m InputMedia) (message.MultiMediaOption, error) { |
| 59 | + resolver := b.peers.UserResolveHook(ctx) |
| 60 | + plain := func(d *message.UploadedDocumentBuilder) message.MultiMediaOption { return d } |
| 61 | + switch m := m.(type) { |
| 62 | + case *InputMediaPhoto: |
| 63 | + caption, err := styledText(m.Caption, m.ParseMode, resolver) |
| 64 | + if err != nil { |
| 65 | + return nil, err |
| 66 | + } |
| 67 | + return b.photoMulti(ctx, m.Media, caption) |
| 68 | + case *InputMediaVideo: |
| 69 | + caption, err := styledText(m.Caption, m.ParseMode, resolver) |
| 70 | + if err != nil { |
| 71 | + return nil, err |
| 72 | + } |
| 73 | + return b.docMulti(ctx, m.Media, caption, func(d *message.UploadedDocumentBuilder) message.MultiMediaOption { return d.Video() }) |
| 74 | + case *InputMediaAnimation: |
| 75 | + caption, err := styledText(m.Caption, m.ParseMode, resolver) |
| 76 | + if err != nil { |
| 77 | + return nil, err |
| 78 | + } |
| 79 | + return b.docMulti(ctx, m.Media, caption, func(d *message.UploadedDocumentBuilder) message.MultiMediaOption { return d.GIF() }) |
| 80 | + case *InputMediaAudio: |
| 81 | + caption, err := styledText(m.Caption, m.ParseMode, resolver) |
| 82 | + if err != nil { |
| 83 | + return nil, err |
| 84 | + } |
| 85 | + return b.docMulti(ctx, m.Media, caption, func(d *message.UploadedDocumentBuilder) message.MultiMediaOption { return d.Audio() }) |
| 86 | + case *InputMediaDocument: |
| 87 | + caption, err := styledText(m.Caption, m.ParseMode, resolver) |
| 88 | + if err != nil { |
| 89 | + return nil, err |
| 90 | + } |
| 91 | + return b.docMulti(ctx, m.Media, caption, plain) |
| 92 | + default: |
| 93 | + return nil, &Error{Code: 400, Description: "Bad Request: invalid media"} |
| 94 | + } |
| 95 | +} |
| 96 | + |
| 97 | +// sentMessages extracts every message produced by a send into Bot API messages. |
| 98 | +func (b *Bot) sentMessages(ctx context.Context, resp tg.UpdatesClass, sendErr error) ([]*Message, error) { |
| 99 | + if sendErr != nil { |
| 100 | + return nil, asAPIError(sendErr) |
| 101 | + } |
| 102 | + |
| 103 | + var updates []tg.UpdateClass |
| 104 | + switch u := resp.(type) { |
| 105 | + case *tg.Updates: |
| 106 | + updates = u.Updates |
| 107 | + case *tg.UpdatesCombined: |
| 108 | + updates = u.Updates |
| 109 | + default: |
| 110 | + return nil, nil |
| 111 | + } |
| 112 | + |
| 113 | + var out []*Message |
| 114 | + for _, upd := range updates { |
| 115 | + var msg tg.MessageClass |
| 116 | + switch u := upd.(type) { |
| 117 | + case *tg.UpdateNewMessage: |
| 118 | + msg = u.Message |
| 119 | + case *tg.UpdateNewChannelMessage: |
| 120 | + msg = u.Message |
| 121 | + default: |
| 122 | + continue |
| 123 | + } |
| 124 | + m, ok := msg.(*tg.Message) |
| 125 | + if !ok { |
| 126 | + continue |
| 127 | + } |
| 128 | + converted, err := b.convertMessage(ctx, m) |
| 129 | + if err != nil { |
| 130 | + return nil, err |
| 131 | + } |
| 132 | + out = append(out, converted) |
| 133 | + } |
| 134 | + return out, nil |
| 135 | +} |
| 136 | + |
| 137 | +// SendMediaGroup sends a group of 2-10 photos, videos, documents or audio files |
| 138 | +// as an album. Items referencing an existing file_id are not yet supported. |
| 139 | +func (b *Bot) SendMediaGroup(ctx context.Context, chat ChatID, media []InputMedia, opts ...SendOption) ([]*Message, error) { |
| 140 | + if len(media) < 2 || len(media) > 10 { |
| 141 | + return nil, &Error{Code: 400, Description: "Bad Request: media group must include 2-10 items"} |
| 142 | + } |
| 143 | + |
| 144 | + var cfg sendConfig |
| 145 | + for _, o := range opts { |
| 146 | + o(&cfg) |
| 147 | + } |
| 148 | + |
| 149 | + items := make([]message.MultiMediaOption, len(media)) |
| 150 | + for i, m := range media { |
| 151 | + mm, err := b.inputMediaToMulti(ctx, m) |
| 152 | + if err != nil { |
| 153 | + return nil, err |
| 154 | + } |
| 155 | + items[i] = mm |
| 156 | + } |
| 157 | + |
| 158 | + peer, err := b.resolveInputPeer(ctx, chat) |
| 159 | + if err != nil { |
| 160 | + return nil, err |
| 161 | + } |
| 162 | + builder := &b.sender.To(peer).Builder |
| 163 | + builder, err = b.applySendConfig(builder, cfg) |
| 164 | + if err != nil { |
| 165 | + return nil, err |
| 166 | + } |
| 167 | + |
| 168 | + resp, err := builder.Album(ctx, items[0], items[1:]...) |
| 169 | + return b.sentMessages(ctx, resp, err) |
| 170 | +} |
0 commit comments