From 29a169d96e2d89ed4feb66c5d0254b7237c10730 Mon Sep 17 00:00:00 2001 From: Norbert Kwizera Date: Mon, 17 Jun 2024 19:01:37 +0200 Subject: [PATCH] Adjust WA template to be send if present even when we have attachments --- handlers/dialog360/handler.go | 311 ++++++++++---------- handlers/dialog360/handler_test.go | 22 ++ handlers/meta/handlers.go | 444 +++++++++++++++-------------- handlers/meta/whataspp_test.go | 29 ++ 4 files changed, 439 insertions(+), 367 deletions(-) diff --git a/handlers/dialog360/handler.go b/handlers/dialog360/handler.go index c53fe2896..e311d4308 100644 --- a/handlers/dialog360/handler.go +++ b/handlers/dialog360/handler.go @@ -311,15 +311,24 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen var payloadAudio whatsapp.SendRequest - for i := 0; i < len(msgParts)+len(msg.Attachments()); i++ { + // do we have a template? + if msg.Templating() != nil { payload := whatsapp.SendRequest{MessagingProduct: "whatsapp", RecipientType: "individual", To: msg.URN().Path()} + payload.Type = "template" + payload.Template = whatsapp.GetTemplatePayload(msg.Templating()) + + err := h.requestD3C(payload, accessToken, res, sendURL, clog) + if err != nil { + return err + } + + } else { + + for i := 0; i < len(msgParts)+len(msg.Attachments()); i++ { + payload := whatsapp.SendRequest{MessagingProduct: "whatsapp", RecipientType: "individual", To: msg.URN().Path()} + + if len(msg.Attachments()) == 0 { - if len(msg.Attachments()) == 0 { - // do we have a template? - if msg.Templating() != nil { - payload.Type = "template" - payload.Template = whatsapp.GetTemplatePayload(msg.Templating()) - } else { if i < (len(msgParts) + len(msg.Attachments()) - 1) { // this is still a msg part text := &whatsapp.Text{PreviewURL: false} @@ -395,169 +404,169 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen payload.Text = text } } - } - - } else if i < len(msg.Attachments()) && (len(qrs) == 0 || len(qrs) > 3) { - attType, attURL := handlers.SplitAttachment(msg.Attachments()[i]) - attType = strings.Split(attType, "/")[0] - if attType == "application" { - attType = "document" - } - payload.Type = attType - media := whatsapp.Media{Link: attURL} - if len(msgParts) == 1 && attType != "audio" && len(msg.Attachments()) == 1 && len(msg.QuickReplies()) == 0 { - media.Caption = msgParts[i] - hasCaption = true - } - - if attType == "image" { - payload.Image = &media - } else if attType == "audio" { - payload.Audio = &media - } else if attType == "video" { - payload.Video = &media - } else if attType == "document" { - filename, err := utils.BasePathForURL(attURL) - if err != nil { - filename = "" + } else if i < len(msg.Attachments()) && (len(qrs) == 0 || len(qrs) > 3) { + attType, attURL := handlers.SplitAttachment(msg.Attachments()[i]) + attType = strings.Split(attType, "/")[0] + if attType == "application" { + attType = "document" } - if filename != "" { - media.Filename = filename + payload.Type = attType + media := whatsapp.Media{Link: attURL} + + if len(msgParts) == 1 && attType != "audio" && len(msg.Attachments()) == 1 && len(msg.QuickReplies()) == 0 { + media.Caption = msgParts[i] + hasCaption = true } - payload.Document = &media - } - } else { - if len(qrs) > 0 { - payload.Type = "interactive" - // if we have more than 10 quick replies, truncate and add channel error - if len(qrs) > 10 { - clog.Error(courier.NewChannelError("", "", "too many quick replies D3C supports only up to 10 quick replies")) - qrs = qrs[:10] + + if attType == "image" { + payload.Image = &media + } else if attType == "audio" { + payload.Audio = &media + } else if attType == "video" { + payload.Video = &media + } else if attType == "document" { + filename, err := utils.BasePathForURL(attURL) + if err != nil { + filename = "" + } + if filename != "" { + media.Filename = filename + } + payload.Document = &media } + } else { + if len(qrs) > 0 { + payload.Type = "interactive" + // if we have more than 10 quick replies, truncate and add channel error + if len(qrs) > 10 { + clog.Error(courier.NewChannelError("", "", "too many quick replies D3C supports only up to 10 quick replies")) + qrs = qrs[:10] + } - // We can use buttons - if len(qrs) <= 3 { - interactive := whatsapp.Interactive{Type: "button", Body: struct { - Text string "json:\"text\"" - }{Text: msgParts[i]}} - - if len(msg.Attachments()) > 0 { - hasCaption = true - attType, attURL := handlers.SplitAttachment(msg.Attachments()[i]) - attType = strings.Split(attType, "/")[0] - if attType == "application" { - attType = "document" - } - if attType == "image" { - image := whatsapp.Media{ - Link: attURL, + // We can use buttons + if len(qrs) <= 3 { + interactive := whatsapp.Interactive{Type: "button", Body: struct { + Text string "json:\"text\"" + }{Text: msgParts[i]}} + + if len(msg.Attachments()) > 0 { + hasCaption = true + attType, attURL := handlers.SplitAttachment(msg.Attachments()[i]) + attType = strings.Split(attType, "/")[0] + if attType == "application" { + attType = "document" } - interactive.Header = &struct { - Type string "json:\"type\"" - Text string "json:\"text,omitempty\"" - Video *whatsapp.Media "json:\"video,omitempty\"" - Image *whatsapp.Media "json:\"image,omitempty\"" - Document *whatsapp.Media "json:\"document,omitempty\"" - }{Type: "image", Image: &image} - } else if attType == "video" { - video := whatsapp.Media{ - Link: attURL, - } - interactive.Header = &struct { - Type string "json:\"type\"" - Text string "json:\"text,omitempty\"" - Video *whatsapp.Media "json:\"video,omitempty\"" - Image *whatsapp.Media "json:\"image,omitempty\"" - Document *whatsapp.Media "json:\"document,omitempty\"" - }{Type: "video", Video: &video} - } else if attType == "document" { - filename, err := utils.BasePathForURL(attURL) - if err != nil { - return err - } - document := whatsapp.Media{ - Link: attURL, - Filename: filename, - } - interactive.Header = &struct { - Type string "json:\"type\"" - Text string "json:\"text,omitempty\"" - Video *whatsapp.Media "json:\"video,omitempty\"" - Image *whatsapp.Media "json:\"image,omitempty\"" - Document *whatsapp.Media "json:\"document,omitempty\"" - }{Type: "document", Document: &document} - } else if attType == "audio" { - payloadAudio = whatsapp.SendRequest{MessagingProduct: "whatsapp", RecipientType: "individual", To: msg.URN().Path(), Type: "audio", Audio: &whatsapp.Media{Link: attURL}} - err := h.requestD3C(payloadAudio, accessToken, res, sendURL, clog) - if err != nil { - return nil + if attType == "image" { + image := whatsapp.Media{ + Link: attURL, + } + interactive.Header = &struct { + Type string "json:\"type\"" + Text string "json:\"text,omitempty\"" + Video *whatsapp.Media "json:\"video,omitempty\"" + Image *whatsapp.Media "json:\"image,omitempty\"" + Document *whatsapp.Media "json:\"document,omitempty\"" + }{Type: "image", Image: &image} + } else if attType == "video" { + video := whatsapp.Media{ + Link: attURL, + } + interactive.Header = &struct { + Type string "json:\"type\"" + Text string "json:\"text,omitempty\"" + Video *whatsapp.Media "json:\"video,omitempty\"" + Image *whatsapp.Media "json:\"image,omitempty\"" + Document *whatsapp.Media "json:\"document,omitempty\"" + }{Type: "video", Video: &video} + } else if attType == "document" { + filename, err := utils.BasePathForURL(attURL) + if err != nil { + return err + } + document := whatsapp.Media{ + Link: attURL, + Filename: filename, + } + interactive.Header = &struct { + Type string "json:\"type\"" + Text string "json:\"text,omitempty\"" + Video *whatsapp.Media "json:\"video,omitempty\"" + Image *whatsapp.Media "json:\"image,omitempty\"" + Document *whatsapp.Media "json:\"document,omitempty\"" + }{Type: "document", Document: &document} + } else if attType == "audio" { + payloadAudio = whatsapp.SendRequest{MessagingProduct: "whatsapp", RecipientType: "individual", To: msg.URN().Path(), Type: "audio", Audio: &whatsapp.Media{Link: attURL}} + err := h.requestD3C(payloadAudio, accessToken, res, sendURL, clog) + if err != nil { + return nil + } + } else { + interactive.Type = "button" + interactive.Body.Text = msgParts[i] } - } else { - interactive.Type = "button" - interactive.Body.Text = msgParts[i] } - } - btns := make([]whatsapp.Button, len(qrs)) - for i, qr := range qrs { - btns[i] = whatsapp.Button{ - Type: "reply", + btns := make([]whatsapp.Button, len(qrs)) + for i, qr := range qrs { + btns[i] = whatsapp.Button{ + Type: "reply", + } + btns[i].Reply.ID = fmt.Sprint(i) + btns[i].Reply.Title = qr } - btns[i].Reply.ID = fmt.Sprint(i) - btns[i].Reply.Title = qr - } - interactive.Action = &struct { - Button string "json:\"button,omitempty\"" - Sections []whatsapp.Section "json:\"sections,omitempty\"" - Buttons []whatsapp.Button "json:\"buttons,omitempty\"" - }{Buttons: btns} - payload.Interactive = &interactive + interactive.Action = &struct { + Button string "json:\"button,omitempty\"" + Sections []whatsapp.Section "json:\"sections,omitempty\"" + Buttons []whatsapp.Button "json:\"buttons,omitempty\"" + }{Buttons: btns} + payload.Interactive = &interactive - } else { - interactive := whatsapp.Interactive{Type: "list", Body: struct { - Text string "json:\"text\"" - }{Text: msgParts[i-len(msg.Attachments())]}} + } else { + interactive := whatsapp.Interactive{Type: "list", Body: struct { + Text string "json:\"text\"" + }{Text: msgParts[i-len(msg.Attachments())]}} - section := whatsapp.Section{ - Rows: make([]whatsapp.SectionRow, len(qrs)), - } - for i, qr := range qrs { - section.Rows[i] = whatsapp.SectionRow{ - ID: fmt.Sprint(i), - Title: qr, + section := whatsapp.Section{ + Rows: make([]whatsapp.SectionRow, len(qrs)), + } + for i, qr := range qrs { + section.Rows[i] = whatsapp.SectionRow{ + ID: fmt.Sprint(i), + Title: qr, + } } - } - interactive.Action = &struct { - Button string "json:\"button,omitempty\"" - Sections []whatsapp.Section "json:\"sections,omitempty\"" - Buttons []whatsapp.Button "json:\"buttons,omitempty\"" - }{Button: menuButton, Sections: []whatsapp.Section{ - section, - }} + interactive.Action = &struct { + Button string "json:\"button,omitempty\"" + Sections []whatsapp.Section "json:\"sections,omitempty\"" + Buttons []whatsapp.Button "json:\"buttons,omitempty\"" + }{Button: menuButton, Sections: []whatsapp.Section{ + section, + }} - payload.Interactive = &interactive - } - } else { - // this is still a msg part - text := &whatsapp.Text{PreviewURL: false} - payload.Type = "text" - if strings.Contains(msgParts[i-len(msg.Attachments())], "https://") || strings.Contains(msgParts[i-len(msg.Attachments())], "http://") { - text.PreviewURL = true + payload.Interactive = &interactive + } + } else { + // this is still a msg part + text := &whatsapp.Text{PreviewURL: false} + payload.Type = "text" + if strings.Contains(msgParts[i-len(msg.Attachments())], "https://") || strings.Contains(msgParts[i-len(msg.Attachments())], "http://") { + text.PreviewURL = true + } + text.Body = msgParts[i-len(msg.Attachments())] + payload.Text = text } - text.Body = msgParts[i-len(msg.Attachments())] - payload.Text = text } - } - err := h.requestD3C(payload, accessToken, res, sendURL, clog) - if err != nil { - return err - } + err := h.requestD3C(payload, accessToken, res, sendURL, clog) + if err != nil { + return err + } - if hasCaption { - break + if hasCaption { + break + } } } diff --git a/handlers/dialog360/handler_test.go b/handlers/dialog360/handler_test.go index 25c28474b..14d64c4b2 100644 --- a/handlers/dialog360/handler_test.go +++ b/handlers/dialog360/handler_test.go @@ -442,6 +442,28 @@ var SendTestCasesD3C = []OutgoingTestCase{ }}, ExpectedExtIDs: []string{"157b5e14568e8"}, }, + { + Label: "Template Send with attachment", + MsgText: "templated message", + MsgURN: "whatsapp:250788123123", + MsgLocale: "eng", + MsgAttachments: []string{"image/jpeg:https://foo.bar/example.jpg"}, + MsgTemplating: `{ + "template": {"uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3", "name": "revive_issue"}, + "components": [{"name": "header","type": "header/media", "variables": {"1": 0}},{"type": "body/text", "name": "body", "variables": {"1": 1, "2": 2}}], + "variables": [{"type":"image", "value":"image/jpeg:https://foo.bar/image.jpg"},{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}], + "language": "en_US" + }`, + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + }, + }, + ExpectedRequests: []ExpectedRequest{{ + Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"template","template":{"name":"revive_issue","language":{"policy":"deterministic","code":"en_US"},"components":[{"type":"header","parameters":[{"type":"image","image":{"link":"https://foo.bar/image.jpg"}}]},{"type":"body","parameters":[{"type":"text","text":"Chef"},{"type":"text","text":"tomorrow"}]}]}}`, + }}, + ExpectedExtIDs: []string{"157b5e14568e8"}, + }, { Label: "Interactive Button Message Send", MsgText: "Interactive Button Msg", diff --git a/handlers/meta/handlers.go b/handlers/meta/handlers.go index b35a77ca0..0e6954314 100644 --- a/handlers/meta/handlers.go +++ b/handlers/meta/handlers.go @@ -799,82 +799,29 @@ func (h *handler) sendWhatsAppMsg(ctx context.Context, msg courier.MsgOut, res * menuButton := handlers.GetText("Menu", msg.Locale()) var payloadAudio whatsapp.SendRequest - - for i := 0; i < len(msgParts)+len(msg.Attachments()); i++ { + // do we have a template? + if msg.Templating() != nil { payload := whatsapp.SendRequest{MessagingProduct: "whatsapp", RecipientType: "individual", To: msg.URN().Path()} + payload.Type = "template" + payload.Template = whatsapp.GetTemplatePayload(msg.Templating()) + err := h.requestWAC(payload, accessToken, res, wacPhoneURL, clog) + if err != nil { + return err + } - if len(msg.Attachments()) == 0 { - // do we have a template? - if msg.Templating() != nil { - payload.Type = "template" - payload.Template = whatsapp.GetTemplatePayload(msg.Templating()) - - } else { - if i < (len(msgParts) + len(msg.Attachments()) - 1) { - // this is still a msg part - text := &whatsapp.Text{PreviewURL: false} - payload.Type = "text" - if strings.Contains(msgParts[i-len(msg.Attachments())], "https://") || strings.Contains(msgParts[i-len(msg.Attachments())], "http://") { - text.PreviewURL = true - } - text.Body = msgParts[i-len(msg.Attachments())] - payload.Text = text - } else { - if len(qrs) > 0 { - payload.Type = "interactive" - - // if we have more than 10 quick replies, truncate and add channel error - if len(qrs) > 10 { - clog.Error(courier.NewChannelError("", "", "too many quick replies WAC supports only up to 10 quick replies")) - qrs = qrs[:10] - } - - // We can use buttons - if len(qrs) <= 3 { - interactive := whatsapp.Interactive{Type: "button", Body: struct { - Text string "json:\"text\"" - }{Text: msgParts[i-len(msg.Attachments())]}} - - btns := make([]whatsapp.Button, len(qrs)) - for i, qr := range qrs { - btns[i] = whatsapp.Button{ - Type: "reply", - } - btns[i].Reply.ID = fmt.Sprint(i) - btns[i].Reply.Title = qr - } - interactive.Action = &struct { - Button string "json:\"button,omitempty\"" - Sections []whatsapp.Section "json:\"sections,omitempty\"" - Buttons []whatsapp.Button "json:\"buttons,omitempty\"" - }{Buttons: btns} - payload.Interactive = &interactive - } else { - interactive := whatsapp.Interactive{Type: "list", Body: struct { - Text string "json:\"text\"" - }{Text: msgParts[i-len(msg.Attachments())]}} + } else { - section := whatsapp.Section{ - Rows: make([]whatsapp.SectionRow, len(qrs)), - } - for i, qr := range qrs { - section.Rows[i] = whatsapp.SectionRow{ - ID: fmt.Sprint(i), - Title: qr, - } - } + for i := 0; i < len(msgParts)+len(msg.Attachments()); i++ { + payload := whatsapp.SendRequest{MessagingProduct: "whatsapp", RecipientType: "individual", To: msg.URN().Path()} - interactive.Action = &struct { - Button string "json:\"button,omitempty\"" - Sections []whatsapp.Section "json:\"sections,omitempty\"" - Buttons []whatsapp.Button "json:\"buttons,omitempty\"" - }{Button: menuButton, Sections: []whatsapp.Section{ - section, - }} + if len(msg.Attachments()) == 0 { + // do we have a template? + if msg.Templating() != nil { + payload.Type = "template" + payload.Template = whatsapp.GetTemplatePayload(msg.Templating()) - payload.Interactive = &interactive - } - } else { + } else { + if i < (len(msgParts) + len(msg.Attachments()) - 1) { // this is still a msg part text := &whatsapp.Text{PreviewURL: false} payload.Type = "text" @@ -883,173 +830,238 @@ func (h *handler) sendWhatsAppMsg(ctx context.Context, msg courier.MsgOut, res * } text.Body = msgParts[i-len(msg.Attachments())] payload.Text = text - } - } - } + } else { + if len(qrs) > 0 { + payload.Type = "interactive" - } else if i < len(msg.Attachments()) && (len(qrs) == 0 || len(qrs) > 3) { - attType, attURL := handlers.SplitAttachment(msg.Attachments()[i]) - attType = strings.Split(attType, "/")[0] - if attType == "application" { - attType = "document" - } - payload.Type = attType - media := whatsapp.Media{Link: attURL} + // if we have more than 10 quick replies, truncate and add channel error + if len(qrs) > 10 { + clog.Error(courier.NewChannelError("", "", "too many quick replies WAC supports only up to 10 quick replies")) + qrs = qrs[:10] + } - if len(msgParts) == 1 && attType != "audio" && len(msg.Attachments()) == 1 && len(msg.QuickReplies()) == 0 { - media.Caption = msgParts[i] - hasCaption = true - } + // We can use buttons + if len(qrs) <= 3 { + interactive := whatsapp.Interactive{Type: "button", Body: struct { + Text string "json:\"text\"" + }{Text: msgParts[i-len(msg.Attachments())]}} + + btns := make([]whatsapp.Button, len(qrs)) + for i, qr := range qrs { + btns[i] = whatsapp.Button{ + Type: "reply", + } + btns[i].Reply.ID = fmt.Sprint(i) + btns[i].Reply.Title = qr + } + interactive.Action = &struct { + Button string "json:\"button,omitempty\"" + Sections []whatsapp.Section "json:\"sections,omitempty\"" + Buttons []whatsapp.Button "json:\"buttons,omitempty\"" + }{Buttons: btns} + payload.Interactive = &interactive + } else { + interactive := whatsapp.Interactive{Type: "list", Body: struct { + Text string "json:\"text\"" + }{Text: msgParts[i-len(msg.Attachments())]}} + + section := whatsapp.Section{ + Rows: make([]whatsapp.SectionRow, len(qrs)), + } + for i, qr := range qrs { + section.Rows[i] = whatsapp.SectionRow{ + ID: fmt.Sprint(i), + Title: qr, + } + } - if attType == "image" { - payload.Image = &media - } else if attType == "audio" { - payload.Audio = &media - } else if attType == "video" { - payload.Video = &media - } else if attType == "document" { - filename, err := utils.BasePathForURL(attURL) - if err != nil { - filename = "" + interactive.Action = &struct { + Button string "json:\"button,omitempty\"" + Sections []whatsapp.Section "json:\"sections,omitempty\"" + Buttons []whatsapp.Button "json:\"buttons,omitempty\"" + }{Button: menuButton, Sections: []whatsapp.Section{ + section, + }} + + payload.Interactive = &interactive + } + } else { + // this is still a msg part + text := &whatsapp.Text{PreviewURL: false} + payload.Type = "text" + if strings.Contains(msgParts[i-len(msg.Attachments())], "https://") || strings.Contains(msgParts[i-len(msg.Attachments())], "http://") { + text.PreviewURL = true + } + text.Body = msgParts[i-len(msg.Attachments())] + payload.Text = text + } + } } - if filename != "" { - media.Filename = filename + + } else if i < len(msg.Attachments()) && (len(qrs) == 0 || len(qrs) > 3) { + attType, attURL := handlers.SplitAttachment(msg.Attachments()[i]) + attType = strings.Split(attType, "/")[0] + if attType == "application" { + attType = "document" } - payload.Document = &media - } - } else { - if len(qrs) > 0 { - payload.Type = "interactive" + payload.Type = attType + media := whatsapp.Media{Link: attURL} - // if we have more than 10 quick replies, truncate and add channel error - if len(qrs) > 10 { - clog.Error(courier.NewChannelError("", "", "too many quick replies WAC supports only up to 10 quick replies")) - qrs = qrs[:10] + if len(msgParts) == 1 && attType != "audio" && len(msg.Attachments()) == 1 && len(msg.QuickReplies()) == 0 { + media.Caption = msgParts[i] + hasCaption = true } - // We can use buttons - if len(qrs) <= 3 { - interactive := whatsapp.Interactive{Type: "button", Body: struct { - Text string "json:\"text\"" - }{Text: msgParts[i]}} - - if len(msg.Attachments()) > 0 { - hasCaption = true - attType, attURL := handlers.SplitAttachment(msg.Attachments()[i]) - attType = strings.Split(attType, "/")[0] - if attType == "application" { - attType = "document" - } - if attType == "image" { - image := whatsapp.Media{ - Link: attURL, - } - interactive.Header = &struct { - Type string "json:\"type\"" - Text string "json:\"text,omitempty\"" - Video *whatsapp.Media "json:\"video,omitempty\"" - Image *whatsapp.Media "json:\"image,omitempty\"" - Document *whatsapp.Media "json:\"document,omitempty\"" - }{Type: "image", Image: &image} - } else if attType == "video" { - video := whatsapp.Media{ - Link: attURL, - } - interactive.Header = &struct { - Type string "json:\"type\"" - Text string "json:\"text,omitempty\"" - Video *whatsapp.Media "json:\"video,omitempty\"" - Image *whatsapp.Media "json:\"image,omitempty\"" - Document *whatsapp.Media "json:\"document,omitempty\"" - }{Type: "video", Video: &video} - } else if attType == "document" { - filename, err := utils.BasePathForURL(attURL) - if err != nil { - return err - } - document := whatsapp.Media{ - Link: attURL, - Filename: filename, + if attType == "image" { + payload.Image = &media + } else if attType == "audio" { + payload.Audio = &media + } else if attType == "video" { + payload.Video = &media + } else if attType == "document" { + filename, err := utils.BasePathForURL(attURL) + if err != nil { + filename = "" + } + if filename != "" { + media.Filename = filename + } + payload.Document = &media + } + } else { + if len(qrs) > 0 { + payload.Type = "interactive" + + // if we have more than 10 quick replies, truncate and add channel error + if len(qrs) > 10 { + clog.Error(courier.NewChannelError("", "", "too many quick replies WAC supports only up to 10 quick replies")) + qrs = qrs[:10] + } + + // We can use buttons + if len(qrs) <= 3 { + interactive := whatsapp.Interactive{Type: "button", Body: struct { + Text string "json:\"text\"" + }{Text: msgParts[i]}} + + if len(msg.Attachments()) > 0 { + hasCaption = true + attType, attURL := handlers.SplitAttachment(msg.Attachments()[i]) + attType = strings.Split(attType, "/")[0] + if attType == "application" { + attType = "document" } - interactive.Header = &struct { - Type string "json:\"type\"" - Text string "json:\"text,omitempty\"" - Video *whatsapp.Media "json:\"video,omitempty\"" - Image *whatsapp.Media "json:\"image,omitempty\"" - Document *whatsapp.Media "json:\"document,omitempty\"" - }{Type: "document", Document: &document} - } else if attType == "audio" { - - payloadAudio = whatsapp.SendRequest{MessagingProduct: "whatsapp", RecipientType: "individual", To: msg.URN().Path(), Type: "audio", Audio: &whatsapp.Media{Link: attURL}} - err := h.requestWAC(payloadAudio, accessToken, res, wacPhoneURL, clog) - if err != nil { - return err + if attType == "image" { + image := whatsapp.Media{ + Link: attURL, + } + interactive.Header = &struct { + Type string "json:\"type\"" + Text string "json:\"text,omitempty\"" + Video *whatsapp.Media "json:\"video,omitempty\"" + Image *whatsapp.Media "json:\"image,omitempty\"" + Document *whatsapp.Media "json:\"document,omitempty\"" + }{Type: "image", Image: &image} + } else if attType == "video" { + video := whatsapp.Media{ + Link: attURL, + } + interactive.Header = &struct { + Type string "json:\"type\"" + Text string "json:\"text,omitempty\"" + Video *whatsapp.Media "json:\"video,omitempty\"" + Image *whatsapp.Media "json:\"image,omitempty\"" + Document *whatsapp.Media "json:\"document,omitempty\"" + }{Type: "video", Video: &video} + } else if attType == "document" { + filename, err := utils.BasePathForURL(attURL) + if err != nil { + return err + } + document := whatsapp.Media{ + Link: attURL, + Filename: filename, + } + interactive.Header = &struct { + Type string "json:\"type\"" + Text string "json:\"text,omitempty\"" + Video *whatsapp.Media "json:\"video,omitempty\"" + Image *whatsapp.Media "json:\"image,omitempty\"" + Document *whatsapp.Media "json:\"document,omitempty\"" + }{Type: "document", Document: &document} + } else if attType == "audio" { + + payloadAudio = whatsapp.SendRequest{MessagingProduct: "whatsapp", RecipientType: "individual", To: msg.URN().Path(), Type: "audio", Audio: &whatsapp.Media{Link: attURL}} + err := h.requestWAC(payloadAudio, accessToken, res, wacPhoneURL, clog) + if err != nil { + return err + } + } else { + interactive.Type = "button" + interactive.Body.Text = msgParts[i] } - } else { - interactive.Type = "button" - interactive.Body.Text = msgParts[i] } - } - btns := make([]whatsapp.Button, len(qrs)) - for i, qr := range qrs { - btns[i] = whatsapp.Button{ - Type: "reply", + btns := make([]whatsapp.Button, len(qrs)) + for i, qr := range qrs { + btns[i] = whatsapp.Button{ + Type: "reply", + } + btns[i].Reply.ID = fmt.Sprint(i) + btns[i].Reply.Title = qr } - btns[i].Reply.ID = fmt.Sprint(i) - btns[i].Reply.Title = qr - } - interactive.Action = &struct { - Button string "json:\"button,omitempty\"" - Sections []whatsapp.Section "json:\"sections,omitempty\"" - Buttons []whatsapp.Button "json:\"buttons,omitempty\"" - }{Buttons: btns} - payload.Interactive = &interactive + interactive.Action = &struct { + Button string "json:\"button,omitempty\"" + Sections []whatsapp.Section "json:\"sections,omitempty\"" + Buttons []whatsapp.Button "json:\"buttons,omitempty\"" + }{Buttons: btns} + payload.Interactive = &interactive - } else { - interactive := whatsapp.Interactive{Type: "list", Body: struct { - Text string "json:\"text\"" - }{Text: msgParts[i-len(msg.Attachments())]}} + } else { + interactive := whatsapp.Interactive{Type: "list", Body: struct { + Text string "json:\"text\"" + }{Text: msgParts[i-len(msg.Attachments())]}} - section := whatsapp.Section{ - Rows: make([]whatsapp.SectionRow, len(qrs)), - } - for i, qr := range qrs { - section.Rows[i] = whatsapp.SectionRow{ - ID: fmt.Sprint(i), - Title: qr, + section := whatsapp.Section{ + Rows: make([]whatsapp.SectionRow, len(qrs)), + } + for i, qr := range qrs { + section.Rows[i] = whatsapp.SectionRow{ + ID: fmt.Sprint(i), + Title: qr, + } } - } - interactive.Action = &struct { - Button string "json:\"button,omitempty\"" - Sections []whatsapp.Section "json:\"sections,omitempty\"" - Buttons []whatsapp.Button "json:\"buttons,omitempty\"" - }{Button: menuButton, Sections: []whatsapp.Section{ - section, - }} + interactive.Action = &struct { + Button string "json:\"button,omitempty\"" + Sections []whatsapp.Section "json:\"sections,omitempty\"" + Buttons []whatsapp.Button "json:\"buttons,omitempty\"" + }{Button: menuButton, Sections: []whatsapp.Section{ + section, + }} - payload.Interactive = &interactive - } - } else { - // this is still a msg part - text := &whatsapp.Text{PreviewURL: false} - payload.Type = "text" - if strings.Contains(msgParts[i-len(msg.Attachments())], "https://") || strings.Contains(msgParts[i-len(msg.Attachments())], "http://") { - text.PreviewURL = true + payload.Interactive = &interactive + } + } else { + // this is still a msg part + text := &whatsapp.Text{PreviewURL: false} + payload.Type = "text" + if strings.Contains(msgParts[i-len(msg.Attachments())], "https://") || strings.Contains(msgParts[i-len(msg.Attachments())], "http://") { + text.PreviewURL = true + } + text.Body = msgParts[i-len(msg.Attachments())] + payload.Text = text } - text.Body = msgParts[i-len(msg.Attachments())] - payload.Text = text } - } - err := h.requestWAC(payload, accessToken, res, wacPhoneURL, clog) - if err != nil { - return err - } + err := h.requestWAC(payload, accessToken, res, wacPhoneURL, clog) + if err != nil { + return err + } - if hasCaption { - break + if hasCaption { + break + } } } return nil diff --git a/handlers/meta/whataspp_test.go b/handlers/meta/whataspp_test.go index 00e4f55ea..23062b0fb 100644 --- a/handlers/meta/whataspp_test.go +++ b/handlers/meta/whataspp_test.go @@ -414,6 +414,35 @@ var whatsappOutgoingTests = []OutgoingTestCase{ }}, ExpectedExtIDs: []string{"157b5e14568e8"}, }, + { + Label: "Template Send with attachment", + MsgText: "templated message", + MsgURN: "whatsapp:250788123123", + MsgLocale: "eng", + MsgAttachments: []string{"image/jpeg:https://foo.bar/example.jpg"}, + MsgTemplating: `{ + "template": {"uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3", "name": "revive_issue"}, + "components": [ + {"name": "header","type": "header/media", "variables": {"1": 0}}, + {"type": "body", "name": "body", "variables": {"1": 1, "2": 2}} + ], + "variables": [ + {"type":"image", "value":"image/jpeg:https://foo.bar/image.jpg"}, + {"type": "text", "value": "Chef"}, + {"type": "text" , "value": "tomorrow"} + ], + "language": "en_US" + }`, + MockResponses: map[string][]*httpx.MockResponse{ + "*/12345_ID/messages": { + httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + }, + }, + ExpectedRequests: []ExpectedRequest{{ + Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"template","template":{"name":"revive_issue","language":{"policy":"deterministic","code":"en_US"},"components":[{"type":"header","parameters":[{"type":"image","image":{"link":"https://foo.bar/image.jpg"}}]},{"type":"body","parameters":[{"type":"text","text":"Chef"},{"type":"text","text":"tomorrow"}]}]}}`, + }}, + ExpectedExtIDs: []string{"157b5e14568e8"}, + }, { Label: "Template Send, no variables", MsgText: "templated message",