Skip to content

Commit 94881e1

Browse files
committed
Support send Twilio content templates for Twilio Whatsapp
1 parent d4d683f commit 94881e1

File tree

6 files changed

+224
-29
lines changed

6 files changed

+224
-29
lines changed

Diff for: handlers/dialog360/handler_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ var SendTestCasesD3C = []OutgoingTestCase{
425425
MsgText: "templated message",
426426
MsgURN: "whatsapp:250788123123",
427427
MsgLocale: "eng",
428-
MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": [{"type": "body", "params": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}], "language": "en_US"}}`),
428+
MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": [{"type": "body", "params": [{"type":"text", "name": "1", "value":"Chef"}, {"type": "text", "name": "2", "value": "tomorrow"}]}], "language": "en_US"}}`),
429429
MockResponses: map[string][]*httpx.MockResponse{
430430
"https://waba-v2.360dialog.io/messages": {
431431
httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),

Diff for: handlers/meta/whataspp_test.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ var whatsappOutgoingTests = []OutgoingTestCase{
394394
MsgText: "templated message",
395395
MsgURN: "whatsapp:250788123123",
396396
MsgLocale: "eng",
397-
MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": [{"type":"body", "params": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}], "language": "en_US"}}`),
397+
MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "external_id": "ext_id_revive_issue", "components": [{"type":"body", "params": [{"type":"text", "name": "1", "value":"Chef"}, {"type": "text" , "name": "2", "value": "tomorrow"}]}], "language": "en_US"}}`),
398398
MockResponses: map[string][]*httpx.MockResponse{
399399
"*/12345_ID/messages": {
400400
httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),
@@ -410,7 +410,7 @@ var whatsappOutgoingTests = []OutgoingTestCase{
410410
MsgText: "templated message",
411411
MsgURN: "whatsapp:250788123123",
412412
MsgLocale: "eng",
413-
MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": [], "variables": [], "language": "en_US"}}`),
413+
MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "external_id": "ext_id_revive_issue", "components": [], "variables": [], "language": "en_US"}}`),
414414
MockResponses: map[string][]*httpx.MockResponse{
415415
"*/12345_ID/messages": {
416416
httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),
@@ -426,17 +426,19 @@ var whatsappOutgoingTests = []OutgoingTestCase{
426426
MsgText: "templated message",
427427
MsgURN: "whatsapp:250788123123",
428428
MsgLocale: "eng",
429-
MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" },"components": [
429+
MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "external_id": "ext_id_revive_issue", "components": [
430430
{
431431
"type": "body",
432432
"name": "body",
433433
"params": [
434434
{
435435
"type": "text",
436+
"name": "1",
436437
"value": "Ryan Lewis"
437438
},
438439
{
439440
"type": "text",
441+
"name": "2",
440442
"value": "niño"
441443
}
442444
]
@@ -447,6 +449,7 @@ var whatsappOutgoingTests = []OutgoingTestCase{
447449
"params": [
448450
{
449451
"type": "text",
452+
"name": "1",
450453
"value": "Sip"
451454
}
452455
]
@@ -457,6 +460,7 @@ var whatsappOutgoingTests = []OutgoingTestCase{
457460
"params": [
458461
{
459462
"type": "url",
463+
"name": "1",
460464
"value": "id00231"
461465
}
462466
]

Diff for: handlers/meta/whatsapp/templates.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ type MsgTemplating struct {
1414
Name string `json:"name" validate:"required"`
1515
UUID string `json:"uuid" validate:"required"`
1616
} `json:"template" validate:"required,dive"`
17-
Namespace string `json:"namespace"`
18-
ExternalID_ string `json:"external_id"`
19-
Components []struct {
17+
Namespace string `json:"namespace"`
18+
ExternalID string `json:"external_id"`
19+
Components []struct {
2020
Type string `json:"type"`
2121
Name string `json:"name"`
2222
Params []struct {

Diff for: handlers/twiml/handlers.go

+105-19
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"crypto/sha1"
1212
_ "embed"
1313
"encoding/base64"
14+
"encoding/json"
1415
"fmt"
1516
"log/slog"
1617
"net/http"
@@ -22,9 +23,11 @@ import (
2223
"github.com/buger/jsonparser"
2324
"github.com/nyaruka/courier"
2425
"github.com/nyaruka/courier/handlers"
26+
"github.com/nyaruka/courier/handlers/meta/whatsapp"
2527
"github.com/nyaruka/courier/utils"
2628
"github.com/nyaruka/gocommon/httpx"
2729
"github.com/nyaruka/gocommon/urns"
30+
"github.com/pkg/errors"
2831
)
2932

3033
const (
@@ -230,34 +233,35 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen
230233
return err
231234
}
232235

233-
parts := handlers.SplitMsgByChannel(msg.Channel(), msg.Text(), maxMsgLength)
234-
for i, part := range parts {
236+
// do we have a template?
237+
templating, err := whatsapp.GetTemplating(msg)
238+
if err != nil {
239+
return errors.Wrapf(err, "unable to decode template: %s for channel: %s", string(msg.Metadata()), msg.Channel().UUID())
240+
}
241+
242+
if templating != nil && channel.IsScheme(urns.WhatsAppScheme) {
235243
// build our request
236244
form := url.Values{
237-
"To": []string{msg.URN().Path()},
238-
"Body": []string{part},
245+
"To": []string{fmt.Sprintf("%s:+%s", urns.WhatsAppScheme, msg.URN().Path())},
239246
"StatusCallback": []string{callbackURL},
247+
"ContentSid": []string{templating.ExternalID},
248+
"From": []string{fmt.Sprintf("%s:%s", urns.WhatsAppScheme, channel.Address())},
240249
}
241250

242-
// add any attachments to the first part
243-
if i == 0 {
244-
for _, a := range attachments {
245-
form.Add("MediaUrl", a.URL)
251+
contentVariables := make(map[string]string)
252+
253+
for _, comp := range templating.Components {
254+
if comp.Type == "body" {
255+
for _, p := range comp.Params {
256+
contentVariables[p.Name] = p.Value
257+
}
246258
}
247259
}
248260

249-
// set our from, either as a messaging service or from our address
250-
serviceSID := channel.StringConfigForKey(configMessagingServiceSID, "")
251-
if serviceSID != "" {
252-
form["MessagingServiceSid"] = []string{serviceSID}
253-
} else {
254-
form["From"] = []string{channel.Address()}
255-
}
261+
contentVariablesJson, _ := json.Marshal(contentVariables)
256262

257-
// for whatsapp channels, we have to prepend whatsapp to the To and From
258-
if channel.IsScheme(urns.WhatsAppScheme) {
259-
form["To"][0] = fmt.Sprintf("%s:+%s", urns.WhatsAppScheme, form["To"][0])
260-
form["From"][0] = fmt.Sprintf("%s:%s", urns.WhatsAppScheme, form["From"][0])
263+
if len(contentVariables) > 0 {
264+
form["ContentVariables"] = []string{string(contentVariablesJson)}
261265
}
262266

263267
// build our URL
@@ -310,6 +314,88 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen
310314
res.AddExternalID(externalID)
311315
}
312316

317+
} else {
318+
parts := handlers.SplitMsgByChannel(msg.Channel(), msg.Text(), maxMsgLength)
319+
for i, part := range parts {
320+
// build our request
321+
form := url.Values{
322+
"To": []string{msg.URN().Path()},
323+
"Body": []string{part},
324+
"StatusCallback": []string{callbackURL},
325+
}
326+
327+
// add any attachments to the first part
328+
if i == 0 {
329+
for _, a := range attachments {
330+
form.Add("MediaUrl", a.URL)
331+
}
332+
}
333+
334+
// set our from, either as a messaging service or from our address
335+
serviceSID := channel.StringConfigForKey(configMessagingServiceSID, "")
336+
if serviceSID != "" {
337+
form["MessagingServiceSid"] = []string{serviceSID}
338+
} else {
339+
form["From"] = []string{channel.Address()}
340+
}
341+
342+
// for whatsapp channels, we have to prepend whatsapp to the To and From
343+
if channel.IsScheme(urns.WhatsAppScheme) {
344+
form["To"][0] = fmt.Sprintf("%s:+%s", urns.WhatsAppScheme, form["To"][0])
345+
form["From"][0] = fmt.Sprintf("%s:%s", urns.WhatsAppScheme, form["From"][0])
346+
}
347+
348+
// build our URL
349+
baseURL := h.baseURL(channel)
350+
if baseURL == "" {
351+
return courier.ErrChannelConfig
352+
}
353+
354+
sendURL, err := utils.AddURLPath(baseURL, "2010-04-01", "Accounts", accountSID, "Messages.json")
355+
if err != nil {
356+
return err
357+
}
358+
359+
req, err := http.NewRequest(http.MethodPost, sendURL, strings.NewReader(form.Encode()))
360+
if err != nil {
361+
return err
362+
}
363+
req.SetBasicAuth(accountSID, accountToken)
364+
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
365+
req.Header.Set("Accept", "application/json")
366+
367+
resp, respBody, err := h.RequestHTTP(req, clog)
368+
if err != nil || resp.StatusCode/100 == 5 {
369+
return courier.ErrConnectionFailed
370+
}
371+
372+
// see if we can parse the error if we have one
373+
if resp.StatusCode/100 != 2 && len(respBody) > 0 {
374+
errorCode, _ := jsonparser.GetInt(respBody, "code")
375+
if errorCode != 0 {
376+
if errorCode == errorStopped {
377+
return courier.ErrContactStopped
378+
}
379+
codeAsStr := strconv.Itoa(int(errorCode))
380+
errMsg, err := jsonparser.GetString(errorCodes, codeAsStr)
381+
if err != nil {
382+
errMsg = fmt.Sprintf("Service specific error: %s.", codeAsStr)
383+
}
384+
return courier.ErrFailedWithReason(codeAsStr, errMsg)
385+
}
386+
387+
return courier.ErrResponseStatus
388+
}
389+
390+
// grab the external id
391+
externalID, err := jsonparser.GetString(respBody, "sid")
392+
if err != nil {
393+
clog.Error(courier.ErrorResponseValueMissing("sid"))
394+
} else {
395+
res.AddExternalID(externalID)
396+
}
397+
398+
}
313399
}
314400

315401
return nil

Diff for: handlers/twiml/handlers_test.go

+107-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package twiml
22

33
import (
44
"context"
5+
"encoding/json"
56
"net/http"
67
"net/url"
78
"testing"
@@ -1051,7 +1052,25 @@ var waSendTestCases = []OutgoingTestCase{
10511052
},
10521053
},
10531054
ExpectedRequests: []ExpectedRequest{{
1054-
Form: url.Values{"Body": {"Simple Message ☺"}, "To": {"whatsapp:+250788383383"}, "From": {"whatsapp:+12065551212"}, "StatusCallback": {"https://localhost/c/t/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&action=callback"}},
1055+
Form: url.Values{"Body": {"Simple Message ☺"}, "To": {"whatsapp:+250788383383"}, "From": {"whatsapp:+12065551212"}, "StatusCallback": {"https://localhost/c/sw/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&action=callback"}},
1056+
Headers: map[string]string{"Authorization": "Basic YWNjb3VudFNJRDphdXRoVG9rZW4="},
1057+
}},
1058+
ExpectedExtIDs: []string{"1002"},
1059+
},
1060+
{
1061+
Label: "Template Send",
1062+
MsgText: "templated message",
1063+
MsgURN: "whatsapp:250788383383",
1064+
MsgLocale: "eng",
1065+
MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "external_id": "ext_id_revive_issue", "components": [{"type":"body", "params": [{"type":"text", "name": "1", "value":"Chef"}, {"type": "text" , "name": "2", "value": "tomorrow"}]}]}}`),
1066+
MockResponses: map[string][]*httpx.MockResponse{
1067+
"http://example.com/sigware_api/2010-04-01/Accounts/accountSID/Messages.json": {
1068+
httpx.NewMockResponse(200, nil, []byte(`{ "sid": "1002" }`)),
1069+
},
1070+
},
1071+
1072+
ExpectedRequests: []ExpectedRequest{{
1073+
Form: url.Values{"To": {"whatsapp:+250788383383"}, "From": {"whatsapp:+12065551212"}, "StatusCallback": {"https://localhost/c/sw/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&action=callback"}, "ContentSid": {"ext_id_revive_issue"}, "ContentVariables": {"{\"1\":\"Chef\",\"2\":\"tomorrow\"}"}},
10551074
Headers: map[string]string{"Authorization": "Basic YWNjb3VudFNJRDphdXRoVG9rZW4="},
10561075
}},
10571076
ExpectedExtIDs: []string{"1002"},
@@ -1074,6 +1093,92 @@ var twaSendTestCases = []OutgoingTestCase{
10741093
}},
10751094
ExpectedExtIDs: []string{"1002"},
10761095
},
1096+
{
1097+
Label: "Template Send",
1098+
MsgText: "templated message",
1099+
MsgURN: "whatsapp:250788383383",
1100+
MsgLocale: "eng",
1101+
MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "external_id": "ext_id_revive_issue", "components": [{"type":"body", "params": [{"type":"text", "name": "1", "value":"Chef"}, {"type": "text" , "name": "2", "value": "tomorrow"}]}]}}`),
1102+
MockResponses: map[string][]*httpx.MockResponse{
1103+
"https://api.twilio.com/2010-04-01/Accounts/accountSID/Messages.json": {
1104+
httpx.NewMockResponse(200, nil, []byte(`{ "sid": "1002" }`)),
1105+
},
1106+
},
1107+
ExpectedRequests: []ExpectedRequest{{
1108+
Form: url.Values{"To": {"whatsapp:+250788383383"}, "From": {"whatsapp:+12065551212"}, "StatusCallback": {"https://localhost/c/twa/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&action=callback"}, "ContentSid": {"ext_id_revive_issue"}, "ContentVariables": {"{\"1\":\"Chef\",\"2\":\"tomorrow\"}"}},
1109+
Headers: map[string]string{"Authorization": "Basic YWNjb3VudFNJRDphdXRoVG9rZW4="},
1110+
}},
1111+
ExpectedExtIDs: []string{"1002"},
1112+
},
1113+
1114+
{
1115+
Label: "Error Code",
1116+
MsgText: "Error Code",
1117+
MsgURN: "whatsapp:250788383383",
1118+
MsgLocale: "eng",
1119+
MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "external_id": "ext_id_revive_issue", "components": [{"type":"body", "params": [{"type":"text", "name": "1", "value":"Chef"}, {"type": "text" , "name": "2", "value": "tomorrow"}]}]}}`),
1120+
MockResponses: map[string][]*httpx.MockResponse{
1121+
"https://api.twilio.com/2010-04-01/Accounts/accountSID/Messages.json": {
1122+
httpx.NewMockResponse(400, nil, []byte(`{ "code": 1001 }`)),
1123+
},
1124+
},
1125+
ExpectedRequests: []ExpectedRequest{{
1126+
Form: url.Values{"To": {"whatsapp:+250788383383"}, "From": {"whatsapp:+12065551212"}, "StatusCallback": {"https://localhost/c/twa/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&action=callback"}, "ContentSid": {"ext_id_revive_issue"}, "ContentVariables": {"{\"1\":\"Chef\",\"2\":\"tomorrow\"}"}},
1127+
Headers: map[string]string{"Authorization": "Basic YWNjb3VudFNJRDphdXRoVG9rZW4="},
1128+
}},
1129+
ExpectedError: courier.ErrFailedWithReason("1001", "Service specific error: 1001."),
1130+
},
1131+
{
1132+
Label: "Stopped Contact Code",
1133+
MsgText: "Stopped Contact",
1134+
MsgURN: "whatsapp:250788383383",
1135+
MsgLocale: "eng",
1136+
MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "external_id": "ext_id_revive_issue", "components": [{"type":"body", "params": [{"type":"text", "name": "1", "value":"Chef"}, {"type": "text" , "name": "2", "value": "tomorrow"}]}]}}`),
1137+
MockResponses: map[string][]*httpx.MockResponse{
1138+
"https://api.twilio.com/2010-04-01/Accounts/accountSID/Messages.json": {
1139+
httpx.NewMockResponse(400, nil, []byte(`{ "code": 21610 }`)),
1140+
},
1141+
},
1142+
ExpectedRequests: []ExpectedRequest{{
1143+
Form: url.Values{"To": {"whatsapp:+250788383383"}, "From": {"whatsapp:+12065551212"}, "StatusCallback": {"https://localhost/c/twa/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&action=callback"}, "ContentSid": {"ext_id_revive_issue"}, "ContentVariables": {"{\"1\":\"Chef\",\"2\":\"tomorrow\"}"}},
1144+
Headers: map[string]string{"Authorization": "Basic YWNjb3VudFNJRDphdXRoVG9rZW4="},
1145+
}},
1146+
ExpectedError: courier.ErrContactStopped,
1147+
},
1148+
{
1149+
Label: "No SID",
1150+
MsgText: "No SID",
1151+
MsgURN: "whatsapp:250788383383",
1152+
MsgLocale: "eng",
1153+
MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "external_id": "ext_id_revive_issue", "components": [{"type":"body", "params": [{"type":"text", "name": "1", "value":"Chef"}, {"type": "text" , "name": "2", "value": "tomorrow"}]}]}}`),
1154+
MockResponses: map[string][]*httpx.MockResponse{
1155+
"https://api.twilio.com/2010-04-01/Accounts/accountSID/Messages.json": {
1156+
httpx.NewMockResponse(200, nil, []byte(`{ }`)),
1157+
},
1158+
},
1159+
ExpectedRequests: []ExpectedRequest{{
1160+
Form: url.Values{"To": {"whatsapp:+250788383383"}, "From": {"whatsapp:+12065551212"}, "StatusCallback": {"https://localhost/c/twa/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&action=callback"}, "ContentSid": {"ext_id_revive_issue"}, "ContentVariables": {"{\"1\":\"Chef\",\"2\":\"tomorrow\"}"}},
1161+
Headers: map[string]string{"Authorization": "Basic YWNjb3VudFNJRDphdXRoVG9rZW4="},
1162+
}},
1163+
ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseValueMissing("sid")},
1164+
},
1165+
{
1166+
Label: "Error Sending",
1167+
MsgText: "Error Message",
1168+
MsgURN: "whatsapp:250788383383",
1169+
MsgLocale: "eng",
1170+
MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "external_id": "ext_id_revive_issue", "components": [{"type":"body", "params": [{"type":"text", "name": "1", "value":"Chef"}, {"type": "text" , "name": "2", "value": "tomorrow"}]}]}}`),
1171+
MockResponses: map[string][]*httpx.MockResponse{
1172+
"https://api.twilio.com/2010-04-01/Accounts/accountSID/Messages.json": {
1173+
httpx.NewMockResponse(401, nil, []byte(`{ "error": "out of credits" }`)),
1174+
},
1175+
},
1176+
ExpectedRequests: []ExpectedRequest{{
1177+
Form: url.Values{"To": {"whatsapp:+250788383383"}, "From": {"whatsapp:+12065551212"}, "StatusCallback": {"https://localhost/c/twa/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&action=callback"}, "ContentSid": {"ext_id_revive_issue"}, "ContentVariables": {"{\"1\":\"Chef\",\"2\":\"tomorrow\"}"}},
1178+
Headers: map[string]string{"Authorization": "Basic YWNjb3VudFNJRDphdXRoVG9rZW4="},
1179+
}},
1180+
ExpectedError: courier.ErrResponseStatus,
1181+
},
10771182
}
10781183

10791184
func TestOutgoing(t *testing.T) {
@@ -1117,7 +1222,7 @@ func TestOutgoing(t *testing.T) {
11171222
)
11181223
waChannel.SetScheme(urns.WhatsAppScheme)
11191224

1120-
RunOutgoingTestCases(t, waChannel, newTWIMLHandler("T", "Twilio Whatsapp", true), waSendTestCases, []string{httpx.BasicAuth("accountSID", "authToken")}, nil)
1225+
RunOutgoingTestCases(t, waChannel, newTWIMLHandler("SW", "SignalWire", true), waSendTestCases, []string{httpx.BasicAuth("accountSID", "authToken")}, nil)
11211226

11221227
twaChannel := test.NewMockChannel("8eb23e93-5ecb-45ba-b726-3b064e0c56ab", "TWA", "+12065551212", "US",
11231228
map[string]any{

Diff for: handlers/whatsapp_legacy/handler_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,7 @@ var defaultSendTestCases = []OutgoingTestCase{
766766
MsgText: "templated message",
767767
MsgURN: "whatsapp:250788123123",
768768
MsgLocale: "eng",
769-
MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "components": [{"type":"body", "params": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}]}}`),
769+
MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "external_id": "ext_id_revive_issue", "components": [{"type":"body", "params": [{"type":"text", "name": "1", "value":"Chef"}, {"type": "text" , "name": "2", "value": "tomorrow"}]}]}}`),
770770
MockResponses: map[string][]*httpx.MockResponse{
771771
"*/v1/messages": {
772772
httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)),

0 commit comments

Comments
 (0)