+// SendImage uploads an image to the owner as a photo, or as a document when the
+// Bot API refuses the photo. Network failures do not trigger a second send.
+func (b *TelegramAPI) SendImage(ctx context.Context, chat int64, name string, data []byte, caption string) error {
+ err := b.sendImage(ctx, "sendPhoto", "photo", chat, name, data, caption)
+ if telegramRejected(err) {
+ err = b.sendImage(ctx, "sendDocument", "document", chat, name, data, caption)
+ }
+
+ return err
+}
+
+func (b *TelegramAPI) sendImage(ctx context.Context, method, field string, chat int64, name string, data []byte, caption string) error {
+ ctx, cancel := context.WithTimeout(ctx, telegramSendTimeout)
+ defer cancel()
+
+ var body bytes.Buffer
+
+ writer := multipart.NewWriter(&body)
+ err := writer.WriteField("chat_id", strconv.FormatInt(chat, 10))
+ if err != nil {
+ return fmt.Errorf("write image chat: %w", err)
+ }
+
+ err = writer.WriteField("caption", caption)
+ if err != nil {
+ return fmt.Errorf("write image caption: %w", err)
+ }
+
+ part, err := writer.CreateFormFile(field, name)
+ if err != nil {
+ return fmt.Errorf("make image part: %w", err)
+ }
+
+ _, err = part.Write(data)
+ if err != nil {
+ return fmt.Errorf("write image part: %w", err)
+ }
+
+ err = writer.Close()
+ if err != nil {
+ return fmt.Errorf("close image form: %w", err)
+ }
+
+ request, err := http.NewRequestWithContext(ctx, http.MethodPost, b.base+"/bot"+b.token+"/"+method, &body)
+ if err != nil {
+ return fmt.Errorf("make image upload: %s", b.redact(err))
+ }
+
+ request.Header.Set("Content-Type", writer.FormDataContentType())
+
+ err = b.answer(request, telegramSendTimeout, nil)
+ if err != nil {
+ return fmt.Errorf("upload image: %w", err)
+ }
+
+ return nil
+}
+