Repository navigation
Conversation
- added the high-quality link thumbnail upload so whatsapp renders the large preview card instead of the small one - used the url, title, description and imgUrl sent by the caller, fetching the page only for missing fields - fetched the page with a link-preview user agent, a 10s timeout and a size limit, rejecting non-2xx responses - kept the send going when the preview fails, with a smaller card or none - changed the preview type from video to none and read og:title and relative og:image urls Closes evolution-foundation#103
Reviewer's GuideThe PR replaces the small inline-only link preview flow with a bounded metadata/image pipeline that preserves caller metadata, uploads a properly populated high-quality WhatsApp thumbnail for direct chats, keeps newsletter previews inline-only, and allows sends to proceed when preview generation fails. Sequence diagram for high-quality link thumbnail deliverysequenceDiagram
participant Caller
participant SendService
participant Page
participant ImageHost
participant WhatsApp
Caller->>SendService: SendLink(data)
SendService->>SendService: linkPreviewTarget(data)
opt title or imgUrl is missing
SendService->>Page: fetchLinkMetadata(target)
Page-->>SendService: title, description, imageURL
end
opt imageURL is available
SendService->>ImageHost: fetchLinkPreviewResource(imageURL)
ImageHost-->>SendService: image bytes
SendService->>SendService: prepareLinkPreviewImage(raw)
SendService->>WhatsApp: Upload(highQuality, MediaLinkThumbnail)
WhatsApp-->>SendService: UploadResponse
SendService->>SendService: applyLinkThumbnailUpload(ext, uploaded, width, height)
end
SendService->>WhatsApp: SendMessage(ExtendedTextMessage)
WhatsApp-->>Caller: Large link card or inline-only preview
Flow diagram for resilient link preview generationflowchart TD
A[SendLink] --> B[linkPreviewTarget]
B --> C{Metadata complete?}
C -->|Yes| D[Preserve caller metadata]
C -->|No| E[fetchLinkMetadata]
E --> F{Fetch succeeds?}
F -->|No| G[Log warning and continue with text]
F -->|Yes| H[Merge only missing metadata]
D --> I{Image URL available?}
H --> I
I -->|No| J[Send text without thumbnail]
I -->|Yes| K[fetchLinkPreviewResource]
K --> L{Image valid and within limit?}
L -->|No| M[Log warning and send text or inline-only preview]
L -->|Yes| N[prepareLinkPreviewImage]
N --> O{Newsletter?}
O -->|Yes| P[Set JPEGThumbnail only]
O -->|No| Q[Upload MediaLinkThumbnail]
Q --> R{Upload succeeds?}
R -->|No| S[Log warning and keep inline thumbnail]
R -->|Yes| T[applyLinkThumbnailUpload]
P --> U[SendMessage]
S --> U
T --> U
J --> U
G --> U
M --> U
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="pkg/sendMessage/service/link_preview.go" line_range="74" />
<code_context>
+// page that refuses bots.
+func resolveLinkPreview(data *LinkStruct, target string, fetch func(string) (string, string, string, error)) (linkPreviewData, error) {
+ preview := linkPreviewData{Title: data.Title, Description: data.Description, ImageURL: data.ImgUrl}
+ if target == "" || (preview.Title != "" && preview.ImageURL != "") {
+ return preview, nil
+ }
+
</code_context>
<issue_to_address>
**issue (bug_risk):** When the caller supplies both `Title` and `ImgUrl` but omits `Description`, the condition returns before fetching the page, so the preview description is always left empty even though the page could provide it.
**Triggers:** When caller metadata includes a title and image URL but no description.
**Suggested fix:** Also fetch when `preview.Description == ""`, or otherwise fetch the individual missing metadata fields.
```suggestion
if target == "" || (preview.Title != "" && preview.Description != "" && preview.ImageURL != "") {
```
</issue_to_address>Sourcery assessment
Needs a human reviewer. 1 finding to address first, and the service now follows caller- and page-supplied image URLs, decodes untrusted image bodies, and uploads the result to WhatsApp; redirects or an attacker-controlled og:image could enable SSRF/data exposure, while crafted compressed images could cause excessive memory use. Reverting stops future fetches, but already uploaded or sent thumbnails and any resulting outage or data exposure cannot be undone.
Blocking findings: pkg/sendMessage/service/link_preview.go:74
| // page that refuses bots. | ||
| func resolveLinkPreview(data *LinkStruct, target string, fetch func(string) (string, string, string, error)) (linkPreviewData, error) { | ||
| preview := linkPreviewData{Title: data.Title, Description: data.Description, ImageURL: data.ImgUrl} | ||
| if target == "" || (preview.Title != "" && preview.ImageURL != "") { |
There was a problem hiding this comment.
issue (bug_risk): When the caller supplies both Title and ImgUrl but omits Description, the condition returns before fetching the page, so the preview description is always left empty even though the page could provide it.
Triggers: When caller metadata includes a title and image URL but no description.
Suggested fix: Also fetch when preview.Description == "", or otherwise fetch the individual missing metadata fields.
| if target == "" || (preview.Title != "" && preview.ImageURL != "") { | |
| if target == "" || (preview.Title != "" && preview.Description != "" && preview.ImageURL != "") { |
- added a header-only dimension check that rejects preview images above 25 megapixels, so a small file declaring a huge bitmap can no longer exhaust memory - simplified decoding to image.Decode, since the webp decoder registers itself in the image package - added tests for the dimension guard and for webp input
|
Review notes:
|
Description
POST /send/linkonly ever produced the small link preview. The message carried the image as an inlineJPEGThumbnailand never uploaded a high-quality link thumbnail, so WhatsApp rendered the small card no matter how large the image was.This PR uploads the preview image with
whatsmeow.MediaLinkThumbnailand fillsThumbnailDirectPath,ThumbnailSHA256,ThumbnailEncSHA256,MediaKey,MediaKeyTimestamp,ThumbnailWidthandThumbnailHeight. With those fields WhatsApp renders the large card (image on top, tappable through to the URL), the same card a phone produces when a user pastes a link.Other changes in the same path:
urlfield was accepted but ignored (the first URL of the text was always used), and a fetched page always overwrote the caller'stitle,descriptionandimgUrl. The page is now fetched only for the fields that are missing, so a caller that already has the metadata skips the fetch entirely.meli.lashort links), whose<title>ended up in the card, and a slow site could hang the request.PreviewTypeisNONEinstead ofVIDEO,og:titleis preferred over<title>, and relativeog:imageURLs are resolved against the page URL.@newsletter) keep the inline thumbnail only, since they take unencrypted uploads.Related Issue
Closes #103
Type of Change
Testing
Manual testing completed
Functionality verified in development environment
No breaking changes introduced
Unit tests:
pkg/sendMessage/service/link_preview_test.go, 14 tests covering the target URL choice, caller metadata precedence, user agent and status handling, the size limit, relativeog:image, JPEG encoding and downscaling, and the upload field mapping.go build ./...,go vet ./pkg/sendMessage/...andgo test ./...pass ongolang:1.25.0-alpine(linux/arm64).Manual test (2026-09-26): image built from this branch, instance paired to a real phone, two messages sent to another number. Both rendered the large card: one with caller-supplied
titleandimgUrl(no page fetch), one with onlyurl(metadata fetched from a Mercado Livre product page).Screenshots (if applicable)
Checklist
Additional Notes
golang.org/x/image/drawcomes from a module already ingo.mod./send/linkrequest body is unchanged, so the Swagger docs did not need regenerating.gofmtis clean andgolangci-lintv2.1.6 reports no findings in the new code. The twoerrcheckfindings the replaced code had are gone with it.Summary by Sourcery
Enable reliable high-quality link previews for link messages while keeping preview failures non-blocking.
New Features:
Bug Fixes:
Enhancements:
Tests: