@@ -42,6 +42,8 @@ type WhatsAppService struct {
4242 done chan struct {}
4343 mu sync.RWMutex
4444 stopped bool
45+ lidMu sync.RWMutex
46+ lidByPhone map [string ]string // canonical phone -> JID string for LID addressing
4547}
4648
4749// NewWhatsAppService creates a new WhatsAppService wrapping the given WhatsAppSender.
@@ -51,6 +53,7 @@ func NewWhatsAppService(client whatsapp.WhatsAppSender) *WhatsAppService {
5153 receipts : make (chan models.Receipt , DefaultChannelBufferSize ),
5254 responses : make (chan models.Response , DefaultChannelBufferSize ),
5355 done : make (chan struct {}),
56+ lidByPhone : make (map [string ]string ),
5457 }
5558
5659 // If the client is a full Client (not just an interface), store it for event handling
@@ -64,6 +67,33 @@ func NewWhatsAppService(client whatsapp.WhatsAppSender) *WhatsAppService {
6467 return service
6568}
6669
70+ func (s * WhatsAppService ) storeRecipientLID (canonicalPhone string , lidJID string ) {
71+ if canonicalPhone == "" || lidJID == "" {
72+ return
73+ }
74+
75+ s .lidMu .Lock ()
76+ defer s .lidMu .Unlock ()
77+
78+ if existing , ok := s .lidByPhone [canonicalPhone ]; ok && existing == lidJID {
79+ return
80+ }
81+
82+ s .lidByPhone [canonicalPhone ] = lidJID
83+ slog .Debug ("WhatsAppService stored LID mapping" , "phone" , canonicalPhone , "lid" , lidJID )
84+ }
85+
86+ func (s * WhatsAppService ) resolveRecipientAddress (canonicalPhone string ) string {
87+ s .lidMu .RLock ()
88+ defer s .lidMu .RUnlock ()
89+
90+ if lidJID , ok := s .lidByPhone [canonicalPhone ]; ok && lidJID != "" {
91+ return lidJID
92+ }
93+
94+ return canonicalPhone
95+ }
96+
6797// ValidateAndCanonicalizeRecipient validates and canonicalizes a WhatsApp phone number.
6898// It removes all non-numeric characters and validates the result has at least 6 digits.
6999func (s * WhatsAppService ) ValidateAndCanonicalizeRecipient (recipient string ) (string , error ) {
@@ -148,10 +178,15 @@ func (s *WhatsAppService) SendMessage(ctx context.Context, to string, body strin
148178 return err
149179 }
150180
151- slog .Debug ("WhatsAppService SendMessage invoked" , "to" , canonicalTo , "body_length" , len (body ))
152- err = s .client .SendMessage (ctx , canonicalTo , body )
181+ sendTo := s .resolveRecipientAddress (canonicalTo )
182+ if sendTo != canonicalTo {
183+ slog .Debug ("WhatsAppService SendMessage using LID recipient" , "to" , sendTo , "phone" , canonicalTo )
184+ } else {
185+ slog .Debug ("WhatsAppService SendMessage invoked" , "to" , canonicalTo , "body_length" , len (body ))
186+ }
187+ err = s .client .SendMessage (ctx , sendTo , body )
153188 if err != nil {
154- slog .Error ("WhatsAppService SendMessage error" , "error" , err , "to" , canonicalTo )
189+ slog .Error ("WhatsAppService SendMessage error" , "error" , err , "to" , sendTo , "phone" , canonicalTo )
155190 return err
156191 }
157192
@@ -183,21 +218,23 @@ func (s *WhatsAppService) SendPromptWithButtons(ctx context.Context, to string,
183218 return err
184219 }
185220
221+ sendTo := s .resolveRecipientAddress (canonicalTo )
222+
186223 var sendErr error
187224 if sender , ok := s .client .(promptButtonsClient ); ok {
188- slog .Debug ("WhatsAppService SendPromptWithButtons using poll message" , "to" , canonicalTo )
189- sendErr = sender .SendPromptButtons (ctx , canonicalTo , body )
225+ slog .Debug ("WhatsAppService SendPromptWithButtons using poll message" , "to" , sendTo , "phone" , canonicalTo )
226+ sendErr = sender .SendPromptButtons (ctx , sendTo , body )
190227 } else {
191- slog .Debug ("WhatsAppService SendPromptWithButtons falling back to text message" , "to" , canonicalTo )
192- sendErr = s .client .SendMessage (ctx , canonicalTo , body )
228+ slog .Debug ("WhatsAppService SendPromptWithButtons falling back to text message" , "to" , sendTo , "phone" , canonicalTo )
229+ sendErr = s .client .SendMessage (ctx , sendTo , body )
193230 }
194231
195232 if sendErr != nil {
196- slog .Error ("WhatsAppService SendPromptWithButtons error" , "error" , sendErr , "to" , canonicalTo )
233+ slog .Error ("WhatsAppService SendPromptWithButtons error" , "error" , sendErr , "to" , sendTo , "phone" , canonicalTo )
197234 return sendErr
198235 }
199236 s .safeEmitReceipt (models.Receipt {To : canonicalTo , Status : models .MessageStatusSent , Time : time .Now ().Unix ()})
200- slog .Info ("WhatsAppService prompt with poll sent and receipt emitted" , "to" , canonicalTo )
237+ slog .Info ("WhatsAppService prompt with poll sent and receipt emitted" , "to" , sendTo , "phone" , canonicalTo )
201238 return nil
202239}
203240
@@ -217,23 +254,25 @@ func (s *WhatsAppService) SendIntensityAdjustmentPoll(ctx context.Context, to st
217254 return err
218255 }
219256
257+ sendTo := s .resolveRecipientAddress (canonicalTo )
258+
220259 var sendErr error
221260 if sender , ok := s .client .(promptButtonsClient ); ok {
222- slog .Debug ("WhatsAppService SendIntensityAdjustmentPoll using poll message" , "to" , canonicalTo , "currentIntensity" , currentIntensity )
223- sendErr = sender .SendIntensityAdjustmentPoll (ctx , canonicalTo , currentIntensity )
261+ slog .Debug ("WhatsAppService SendIntensityAdjustmentPoll using poll message" , "to" , sendTo , "phone" , canonicalTo , "currentIntensity" , currentIntensity )
262+ sendErr = sender .SendIntensityAdjustmentPoll (ctx , sendTo , currentIntensity )
224263 } else {
225264 // Fallback: send text message asking about intensity
226- slog .Debug ("WhatsAppService SendIntensityAdjustmentPoll falling back to text message" , "to" , canonicalTo )
227- sendErr = s .client .SendMessage (ctx , canonicalTo , "How's the intensity? Reply with 'low', 'normal', or 'high'." )
265+ slog .Debug ("WhatsAppService SendIntensityAdjustmentPoll falling back to text message" , "to" , sendTo , "phone" , canonicalTo )
266+ sendErr = s .client .SendMessage (ctx , sendTo , "How's the intensity? Reply with 'low', 'normal', or 'high'." )
228267 }
229268
230269 if sendErr != nil {
231- slog .Error ("WhatsAppService SendIntensityAdjustmentPoll error" , "error" , sendErr , "to" , canonicalTo )
270+ slog .Error ("WhatsAppService SendIntensityAdjustmentPoll error" , "error" , sendErr , "to" , sendTo , "phone" , canonicalTo )
232271 return sendErr
233272 }
234273
235274 s .safeEmitReceipt (models.Receipt {To : canonicalTo , Status : models .MessageStatusSent , Time : time .Now ().Unix ()})
236- slog .Info ("WhatsAppService intensity adjustment poll sent and receipt emitted" , "to" , canonicalTo , "currentIntensity" , currentIntensity )
275+ slog .Info ("WhatsAppService intensity adjustment poll sent and receipt emitted" , "to" , sendTo , "phone" , canonicalTo , "currentIntensity" , currentIntensity )
237276 return nil
238277}
239278
@@ -253,9 +292,11 @@ func (s *WhatsAppService) SendTypingIndicator(ctx context.Context, to string, ty
253292 return err
254293 }
255294
256- slog .Debug ("WhatsAppService SendTypingIndicator invoked" , "to" , canonicalTo , "typing" , typing )
257- if err := s .client .SendTypingIndicator (ctx , canonicalTo , typing ); err != nil {
258- slog .Warn ("WhatsAppService SendTypingIndicator error" , "error" , err , "to" , canonicalTo , "typing" , typing )
295+ sendTo := s .resolveRecipientAddress (canonicalTo )
296+
297+ slog .Debug ("WhatsAppService SendTypingIndicator invoked" , "to" , sendTo , "phone" , canonicalTo , "typing" , typing )
298+ if err := s .client .SendTypingIndicator (ctx , sendTo , typing ); err != nil {
299+ slog .Warn ("WhatsAppService SendTypingIndicator error" , "error" , err , "to" , sendTo , "phone" , canonicalTo , "typing" , typing )
259300 return err
260301 }
261302
@@ -364,10 +405,47 @@ func (s *WhatsAppService) handleIncomingMessage(evt *events.Message) {
364405 return
365406 }
366407
367- // Convert JID to E.164 format (remove @s.whatsapp.net suffix)
368- fromNumber := strings .TrimSuffix (evt .Info .Sender .User , "" )
369- if ! strings .HasPrefix (fromNumber , "+" ) {
370- fromNumber = "+" + fromNumber
408+ // Convert JID to E.164 format and store LID mapping if available
409+ fromNumber := ""
410+ canonicalPhone := ""
411+
412+ sender := evt .Info .Sender .ToNonAD ()
413+ senderAlt := evt .Info .SenderAlt .ToNonAD ()
414+
415+ var phoneJID types.JID
416+ var lidJID types.JID
417+
418+ if evt .Info .AddressingMode == types .AddressingModeLID {
419+ lidJID = sender
420+ phoneJID = senderAlt
421+ } else {
422+ phoneJID = sender
423+ lidJID = senderAlt
424+ }
425+
426+ if phoneJID .User == "" || phoneJID .Server != types .DefaultUserServer {
427+ phoneJID = sender
428+ }
429+
430+ if phoneJID .User != "" {
431+ fromNumber = phoneJID .User
432+ if ! strings .HasPrefix (fromNumber , "+" ) {
433+ fromNumber = "+" + fromNumber
434+ }
435+ }
436+
437+ if fromNumber == "" {
438+ fromNumber = sender .User
439+ if ! strings .HasPrefix (fromNumber , "+" ) {
440+ fromNumber = "+" + fromNumber
441+ }
442+ }
443+
444+ if canonical , err := s .ValidateAndCanonicalizeRecipient (fromNumber ); err == nil {
445+ canonicalPhone = canonical
446+ if lidJID .User != "" && lidJID .Server == types .HiddenUserServer {
447+ s .storeRecipientLID (canonicalPhone , lidJID .String ())
448+ }
371449 }
372450
373451 response := models.Response {
@@ -376,7 +454,7 @@ func (s *WhatsAppService) handleIncomingMessage(evt *events.Message) {
376454 Time : evt .Info .Timestamp .Unix (),
377455 }
378456
379- slog .Debug ("WhatsAppService processing incoming message" , "from" , response .From , "body_length" , len (response .Body ))
457+ slog .Debug ("WhatsAppService processing incoming message" , "from" , response .From , "body_length" , len (response .Body ), "addressingMode" , evt . Info . AddressingMode , "canonicalPhone" , canonicalPhone )
380458
381459 // Send to responses channel (non-blocking)
382460 s .safeEmitResponse (response )
0 commit comments