11 "go.mau.fi/whatsmeow/proto/waE2E"
14 // whatsappImageUploadTimeout limits one image upload to WhatsApp.
15 const whatsappImageUploadTimeout = 2 * time.Minute
17 // whatsappImage downloads only supported images and stores them before the
18 // event can be acknowledged. Both the declared size and the downloaded bytes
19 // must fit the image store. Decoded bytes determine the stored format.
20 func whatsappImage(ctx context.Context, client WhatsAppClient, store *ImageStore, message *waE2E.ImageMessage) (string, error) {
21 if message.GetFileLength() > imageMaxBytes {
22 return "", errImageSize
25 switch message.GetMimetype() {
26 case "image/jpeg", "image/png", "image/webp":
28 return "", errImageFormat
32 return "", errors.New("WhatsApp client is not available")
35 data, err := client.Download(ctx, message)
37 return "", fmt.Errorf("download image: %w", whatsappImageDownloadError(err))
40 name, err := store.Save(data)
42 return "", fmt.Errorf("store image: %w", err)
48 // whatsappImageDownloadError keeps only known safe causes. Library errors can
49 // contain signed URLs or direct paths even when request preparation fails, so
50 // arbitrary error text cannot go to the daemon log.
51 func whatsappImageDownloadError(err error) error {
53 case errors.Is(err, context.Canceled):
54 return context.Canceled
55 case errors.Is(err, context.DeadlineExceeded):
56 return context.DeadlineExceeded
59 var response whatsmeow.DownloadHTTPError
61 if errors.As(err, &response) && response.Response != nil {
62 return fmt.Errorf("media server returned HTTP %d", response.StatusCode)
65 return errors.New("media download failed")
68 // whatsappImageMessage uploads stored pixels and uses the upload keys only in
69 // the outgoing message. The text is the caption, not a separate message.
70 func whatsappImageMessage(ctx context.Context, client WhatsAppClient, data []byte, text string) (*waE2E.Message, error) {
71 ctx, cancel := context.WithTimeout(ctx, whatsappImageUploadTimeout)
74 upload, err := client.Upload(ctx, data, whatsmeow.MediaImage)
76 return nil, fmt.Errorf("upload image: %w", whatsappImageSendError(err))
79 return &waE2E.Message{ImageMessage: &waE2E.ImageMessage{
81 Mimetype: new(http.DetectContentType(data)),
83 DirectPath: &upload.DirectPath,
84 MediaKey: upload.MediaKey,
85 FileEncSHA256: upload.FileEncSHA256,
86 FileSHA256: upload.FileSHA256,
87 FileLength: &upload.FileLength,
91 // whatsappImageSendError drops arbitrary upload and send errors, which can hold
92 // media URLs and keys. Cancellation and deadline causes are safe to keep.
93 func whatsappImageSendError(err error) error {
95 case errors.Is(err, context.Canceled):
96 return context.Canceled
97 case errors.Is(err, context.DeadlineExceeded):
98 return context.DeadlineExceeded
100 return errors.New("media send failed")