feat: add forwardingScore support to send text/media#86
Conversation
WhatsApp renders the Encaminhada badge when ContextInfo.ForwardingScore > 0. Evolution Go v1 did not expose this field, so forwarded messages arrived without the badge in the recipient's WhatsApp. Changes: - TextStruct and MediaStruct accept optional forwardingScore (uint32) - SendDataStruct carries it through to SendMessage - SendMessage applies it to the ContextInfo of every message type (text, image, video, ptv, audio, document, poll, sticker, location, contact, interactive, list)
Reviewer's GuideAdds optional forwardingScore support to text and media sending flows, threading it from API payload structs through SendDataStruct into SendMessage, where it is applied to the ContextInfo.ForwardingScore field for all supported WhatsApp message types when greater than zero. Sequence diagram for applying forwardingScore in SendMessagesequenceDiagram
actor Client
participant sendService
participant SendMessage
participant WhatsAppServer
Client->>sendService: POST /send/text (forwardingScore)
sendService->>sendService: sendTextWithRetry
sendService->>SendMessage: SendMessage(instance, msg, ExtendedTextMessage, SendDataStruct)
alt [ForwardingScore > 0]
SendMessage->>SendMessage: set ContextInfo.ForwardingScore from SendDataStruct.ForwardingScore
end
SendMessage->>WhatsAppServer: send msg with ContextInfo.ForwardingScore
WhatsAppServer-->>Client: message rendered with Encaminhada badge
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- The
SendMessageforwardingScore application introduces a long, repetitive switch overmessageType; consider extracting a helper (e.g.applyForwardingScore(msg *waE2E.Message, messageType string, score uint32)) or reusing any existing message-type handling pattern to reduce duplication and lower the risk of missing future message types. - In
SendMessage, you dereferencedata.ForwardingScorewithout a nil-guard ondataitself; ifSendMessagecan ever be called with a nilSendDataStruct(or if that changes in the future), this will panic, so it may be safer to early-return or guarddata != nilbefore accessing it.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `SendMessage` forwardingScore application introduces a long, repetitive switch over `messageType`; consider extracting a helper (e.g. `applyForwardingScore(msg *waE2E.Message, messageType string, score uint32)`) or reusing any existing message-type handling pattern to reduce duplication and lower the risk of missing future message types.
- In `SendMessage`, you dereference `data.ForwardingScore` without a nil-guard on `data` itself; if `SendMessage` can ever be called with a nil `SendDataStruct` (or if that changes in the future), this will panic, so it may be safer to early-return or guard `data != nil` before accessing it.
## Individual Comments
### Comment 1
<location path="pkg/sendMessage/service/send_service.go" line_range="2201-2210" />
<code_context>
+ // Apply ForwardingScore to whichever ContextInfo was set above.
</code_context>
<issue_to_address>
**suggestion:** The large switch for setting `ForwardingScore` is repetitive and may be brittle as new message types are added.
Consider extracting a helper like `getContextInfo(msg *waE2E.Message, messageType string) *waE2E.ContextInfo` that encapsulates the mapping (including the `DocumentWithCaptionMessage` special case), and then set `ctx.ForwardingScore` only if the returned context is non-nil. Centralizing this logic would reduce duplication and make it easier to keep in sync as new message types are added.
Suggested implementation:
```golang
// Apply ForwardingScore to whichever ContextInfo was set above.
// WhatsApp renders "Encaminhada" when ContextInfo.ForwardingScore > 0.
if data.ForwardingScore != nil && *data.ForwardingScore > 0 {
if ctxInfo := getContextInfo(msg, messageType); ctxInfo != nil {
ctxInfo.ForwardingScore = data.ForwardingScore
}
```
To fully implement the refactor:
1. Define the helper in `pkg/sendMessage/service/send_service.go` (near the other send helpers / below this function):
```go
func getContextInfo(msg *waE2E.Message, messageType string) *waE2E.ContextInfo {
switch messageType {
case "ExtendedTextMessage":
if msg.ExtendedTextMessage != nil {
return msg.ExtendedTextMessage.ContextInfo
}
case "ImageMessage":
if msg.ImageMessage != nil {
return msg.ImageMessage.ContextInfo
}
case "DocumentWithCaptionMessage":
// Use the same logic that currently exists in the large switch, e.g.:
// - If the DocumentWithCaptionMessage has its own ContextInfo, return it
// - Otherwise, return the underlying DocumentMessage.ContextInfo (or whatever the current special-case logic is)
// Example skeleton (adjust field names to match the existing code):
if msg.DocumentWithCaptionMessage != nil {
if msg.DocumentWithCaptionMessage.ContextInfo != nil {
return msg.DocumentWithCaptionMessage.ContextInfo
}
if msg.DocumentWithCaptionMessage.DocumentMessage != nil {
return msg.DocumentWithCaptionMessage.DocumentMessage.ContextInfo
}
}
// Add remaining message types currently handled in the big ForwardingScore switch:
// - VideoMessage
// - AudioMessage
// - DocumentMessage
// - etc.
// Each case should mirror the existing logic used to select which ContextInfo gets ForwardingScore.
}
return nil
}
```
2. For every `case` that was previously present in the large `switch messageType { ... }` (that set `ForwardingScore` directly on `msg.<Type>.ContextInfo`), add a corresponding `case` in `getContextInfo` that returns the same `ContextInfo` pointer instead of setting the score.
3. Ensure the special `DocumentWithCaptionMessage` handling in `getContextInfo` exactly mirrors the current behavior from the old switch (including any fallback to `DocumentMessage.ContextInfo` or other fields).
Once this helper contains all the cases, the call site you edited will correctly centralize the mapping and reduce duplication.
</issue_to_address>
### Comment 2
<location path="pkg/sendMessage/service/send_service.go" line_range="2225" />
<code_context>
+ if msg.AudioMessage != nil && msg.AudioMessage.ContextInfo != nil {
+ msg.AudioMessage.ContextInfo.ForwardingScore = data.ForwardingScore
+ }
+ case "DocumentMessage":
+ if msg.DocumentMessage != nil && msg.DocumentMessage.ContextInfo != nil {
+ msg.DocumentMessage.ContextInfo.ForwardingScore = data.ForwardingScore
</code_context>
<issue_to_address>
**nitpick:** The nested `DocumentWithCaptionMessage` condition is quite dense and could be made more readable.
Consider extracting `msg.DocumentWithCaptionMessage.Message.DocumentMessage` into a local variable (and/or splitting the checks into separate `if` blocks or early returns) to simplify the condition and make future changes less error‑prone.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| // Apply ForwardingScore to whichever ContextInfo was set above. | ||
| // WhatsApp renders "Encaminhada" when ContextInfo.ForwardingScore > 0. | ||
| if data.ForwardingScore != nil && *data.ForwardingScore > 0 { | ||
| switch messageType { | ||
| case "ExtendedTextMessage": | ||
| if msg.ExtendedTextMessage != nil && msg.ExtendedTextMessage.ContextInfo != nil { | ||
| msg.ExtendedTextMessage.ContextInfo.ForwardingScore = data.ForwardingScore | ||
| } | ||
| case "ImageMessage": | ||
| if msg.ImageMessage != nil && msg.ImageMessage.ContextInfo != nil { |
There was a problem hiding this comment.
suggestion: The large switch for setting ForwardingScore is repetitive and may be brittle as new message types are added.
Consider extracting a helper like getContextInfo(msg *waE2E.Message, messageType string) *waE2E.ContextInfo that encapsulates the mapping (including the DocumentWithCaptionMessage special case), and then set ctx.ForwardingScore only if the returned context is non-nil. Centralizing this logic would reduce duplication and make it easier to keep in sync as new message types are added.
Suggested implementation:
// Apply ForwardingScore to whichever ContextInfo was set above.
// WhatsApp renders "Encaminhada" when ContextInfo.ForwardingScore > 0.
if data.ForwardingScore != nil && *data.ForwardingScore > 0 {
if ctxInfo := getContextInfo(msg, messageType); ctxInfo != nil {
ctxInfo.ForwardingScore = data.ForwardingScore
}To fully implement the refactor:
- Define the helper in
pkg/sendMessage/service/send_service.go(near the other send helpers / below this function):
func getContextInfo(msg *waE2E.Message, messageType string) *waE2E.ContextInfo {
switch messageType {
case "ExtendedTextMessage":
if msg.ExtendedTextMessage != nil {
return msg.ExtendedTextMessage.ContextInfo
}
case "ImageMessage":
if msg.ImageMessage != nil {
return msg.ImageMessage.ContextInfo
}
case "DocumentWithCaptionMessage":
// Use the same logic that currently exists in the large switch, e.g.:
// - If the DocumentWithCaptionMessage has its own ContextInfo, return it
// - Otherwise, return the underlying DocumentMessage.ContextInfo (or whatever the current special-case logic is)
// Example skeleton (adjust field names to match the existing code):
if msg.DocumentWithCaptionMessage != nil {
if msg.DocumentWithCaptionMessage.ContextInfo != nil {
return msg.DocumentWithCaptionMessage.ContextInfo
}
if msg.DocumentWithCaptionMessage.DocumentMessage != nil {
return msg.DocumentWithCaptionMessage.DocumentMessage.ContextInfo
}
}
// Add remaining message types currently handled in the big ForwardingScore switch:
// - VideoMessage
// - AudioMessage
// - DocumentMessage
// - etc.
// Each case should mirror the existing logic used to select which ContextInfo gets ForwardingScore.
}
return nil
}-
For every
casethat was previously present in the largeswitch messageType { ... }(that setForwardingScoredirectly onmsg.<Type>.ContextInfo), add a correspondingcaseingetContextInfothat returns the sameContextInfopointer instead of setting the score. -
Ensure the special
DocumentWithCaptionMessagehandling ingetContextInfoexactly mirrors the current behavior from the old switch (including any fallback toDocumentMessage.ContextInfoor other fields).
Once this helper contains all the cases, the call site you edited will correctly centralize the mapping and reduce duplication.
| if msg.AudioMessage != nil && msg.AudioMessage.ContextInfo != nil { | ||
| msg.AudioMessage.ContextInfo.ForwardingScore = data.ForwardingScore | ||
| } | ||
| case "DocumentMessage": |
There was a problem hiding this comment.
nitpick: The nested DocumentWithCaptionMessage condition is quite dense and could be made more readable.
Consider extracting msg.DocumentWithCaptionMessage.Message.DocumentMessage into a local variable (and/or splitting the checks into separate if blocks or early returns) to simplify the condition and make future changes less error‑prone.
There was a problem hiding this comment.
Pull request overview
This PR adds support for propagating WhatsApp forwarding metadata (ContextInfo.ForwardingScore) through the send pipeline so recipients can see the forwarded (“Encaminhada”) badge when applicable.
Changes:
- Added optional
forwardingScoretoTextStructandMediaStructrequest payloads. - Plumbed
ForwardingScorethroughSendDataStructintoSendMessage. - Set
ContextInfo.ForwardingScoreinsideSendMessageacross multiple message types.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| case "InteractiveMessage": | ||
| if msg.InteractiveMessage != nil && msg.InteractiveMessage.ContextInfo != nil { | ||
| msg.InteractiveMessage.ContextInfo.ForwardingScore = data.ForwardingScore | ||
| } |
| case "ListMessage": | ||
| if msg.ListMessage != nil && msg.ListMessage.ContextInfo != nil { | ||
| msg.ListMessage.ContextInfo.ForwardingScore = data.ForwardingScore | ||
| } |
Summary
WhatsApp renders the Encaminhada badge when
ContextInfo.ForwardingScore > 0. Evolution Go v1 did not expose this field, so forwarded messages arrived without the badge in the recipient's WhatsApp.Changes
TextStructandMediaStructaccept optionalforwardingScore(uint32)SendDataStructcarries it through toSendMessageSendMessageapplies it to theContextInfoof every message type (text, image, video, ptv, audio, document, poll, sticker, location, contact, interactive, list)Usage
When
forwardingScore > 0, the recipient's WhatsApp shows the Encaminhada badge.Summary by Sourcery
Add support for propagating WhatsApp message forwarding metadata through send text and media APIs so clients can mark messages as forwarded.
New Features:
Enhancements: