@@ -3,6 +3,7 @@ package bridge
33import (
44 "context"
55 "fmt"
6+ "net/url"
67 "strconv"
78 "strings"
89 "time"
@@ -69,18 +70,20 @@ type Thread struct {
6970
7071// Attachment represents a media attachment
7172type Attachment struct {
72- Type string `json:"type"` // "image", "video", "audio", "file", "sticker", "gif", "voice", "location"
73- URL string `json:"url,omitempty"`
74- FileName string `json:"fileName,omitempty"`
75- MimeType string `json:"mimeType,omitempty"`
76- FileSize int64 `json:"fileSize,omitempty"`
77- Width int `json:"width,omitempty"`
78- Height int `json:"height,omitempty"`
79- Duration int `json:"duration,omitempty"` // in seconds for audio/video
80- StickerID int64 `json:"stickerId,omitempty"`
81- Latitude float64 `json:"latitude,omitempty"`
82- Longitude float64 `json:"longitude,omitempty"`
83- PreviewURL string `json:"previewUrl,omitempty"`
73+ Type string `json:"type"` // "image", "video", "audio", "file", "sticker", "gif", "voice", "location", "link"
74+ URL string `json:"url,omitempty"`
75+ FileName string `json:"fileName,omitempty"`
76+ MimeType string `json:"mimeType,omitempty"`
77+ FileSize int64 `json:"fileSize,omitempty"`
78+ Width int `json:"width,omitempty"`
79+ Height int `json:"height,omitempty"`
80+ Duration int `json:"duration,omitempty"` // in seconds for audio/video
81+ StickerID int64 `json:"stickerId,omitempty"`
82+ Latitude float64 `json:"latitude,omitempty"`
83+ Longitude float64 `json:"longitude,omitempty"`
84+ PreviewURL string `json:"previewUrl,omitempty"`
85+ Description string `json:"description,omitempty"` // For link attachments
86+ SourceText string `json:"sourceText,omitempty"` // Domain/source for link attachments
8487 // For E2EE media download
8588 MediaKey []byte `json:"mediaKey,omitempty"`
8689 MediaSHA256 []byte `json:"mediaSha256,omitempty"`
@@ -442,12 +445,25 @@ func (c *Client) convertWrappedMessage(msg *table.WrappedMessage) *Message {
442445
443446 // Handle XMA attachments (links, shares, etc.)
444447 for _ , xma := range msg .XMAAttachments {
445- if xma .PreviewUrl != "" {
448+ // Get the actual URL from CTA ActionUrl or fallback to xma.ActionUrl
449+ var linkURL string
450+ if xma .CTA != nil && xma .CTA .ActionUrl != "" {
451+ linkURL = extractURLFromLPHP (xma .CTA .ActionUrl )
452+ } else if xma .ActionUrl != "" {
453+ linkURL = extractURLFromLPHP (xma .ActionUrl )
454+ }
455+
456+ // Only add as link attachment if we have a URL or preview
457+ if linkURL != "" || xma .PreviewUrl != "" {
446458 m .Attachments = append (m .Attachments , & Attachment {
447- Type : "link" ,
448- URL : xma .ActionUrl ,
449- PreviewURL : xma .PreviewUrl ,
450- FileName : xma .TitleText ,
459+ Type : "link" ,
460+ URL : linkURL ,
461+ PreviewURL : xma .PreviewUrl ,
462+ FileName : xma .TitleText ,
463+ Description : xma .SubtitleText ,
464+ SourceText : xma .SourceText ,
465+ Width : int (xma .PreviewWidth ),
466+ Height : int (xma .PreviewHeight ),
451467 })
452468 }
453469 }
@@ -949,11 +965,23 @@ func (c *Client) extractE2EEMessage(e *events.FBMessage, senderID int64) *E2EEMe
949965 msg .Mentions = mentions
950966 }
951967 }
952- if extMsg .GetCanonicalURL () != "" {
968+ if extMsg .GetCanonicalURL () != "" || extMsg . GetMatchedText () != "" {
953969 att := & Attachment {
954- Type : "link" ,
955- URL : extMsg .GetCanonicalURL (),
956- FileName : extMsg .GetTitle (),
970+ Type : "link" ,
971+ URL : extMsg .GetCanonicalURL (),
972+ FileName : extMsg .GetTitle (),
973+ Description : extMsg .GetDescription (),
974+ }
975+ // If no canonical URL, use matched text as the URL
976+ if att .URL == "" && extMsg .GetMatchedText () != "" {
977+ att .URL = extMsg .GetMatchedText ()
978+ }
979+ // Try to decode thumbnail for preview
980+ if thumb , err := extMsg .DecodeThumbnail (); err == nil && thumb != nil {
981+ if ancillary := thumb .GetAncillary (); ancillary != nil {
982+ att .Width = int (ancillary .GetWidth ())
983+ att .Height = int (ancillary .GetHeight ())
984+ }
957985 }
958986 msg .Attachments = append (msg .Attachments , att )
959987 }
@@ -1137,3 +1165,22 @@ func (c *Client) extractE2EEStickerAttachment(sticker *waConsumerApplication.Con
11371165
11381166 return att
11391167}
1168+
1169+ // extractURLFromLPHP extracts the actual URL from Facebook's l.php redirect URL
1170+ // e.g., "https://l.facebook.com/l.php?u=https%3A%2F%2Fexample.com&h=..." -> "https://example.com"
1171+ func extractURLFromLPHP (addr string ) string {
1172+ if addr == "" {
1173+ return ""
1174+ }
1175+ parsed , err := url .Parse (addr )
1176+ if err != nil {
1177+ return addr
1178+ }
1179+ // Check if this is a Facebook l.php redirect
1180+ if parsed .Path == "/l.php" || strings .HasSuffix (parsed .Path , "/l.php" ) {
1181+ if u := parsed .Query ().Get ("u" ); u != "" {
1182+ return u
1183+ }
1184+ }
1185+ return addr
1186+ }
0 commit comments