From 8d60f4b0695c89da81df6d9fee4908424d802d13 Mon Sep 17 00:00:00 2001 From: Rowan Seymour Date: Wed, 18 Aug 2021 15:20:00 -0500 Subject: [PATCH] Make IVR machine detection an option in channel config --- core/ivr/ivr.go | 4 ++-- core/ivr/twiml/twiml.go | 11 +++++++---- core/ivr/vonage/vonage.go | 6 ++++-- core/models/channels.go | 7 ++++++- core/tasks/ivr/worker_test.go | 2 +- web/ivr/ivr_test.go | 2 +- 6 files changed, 21 insertions(+), 11 deletions(-) diff --git a/core/ivr/ivr.go b/core/ivr/ivr.go index 8253f3ce9..79202ec5d 100644 --- a/core/ivr/ivr.go +++ b/core/ivr/ivr.go @@ -66,7 +66,7 @@ func GetClient(channel *models.Channel) (Client, error) { // Client defines the interface IVR clients must satisfy type Client interface { - RequestCall(number urns.URN, handleURL string, statusURL string) (CallID, *httpx.Trace, error) + RequestCall(number urns.URN, handleURL string, statusURL string, machineDetection bool) (CallID, *httpx.Trace, error) HangupCall(externalID string) (*httpx.Trace, error) @@ -250,7 +250,7 @@ func RequestCallStartForConnection(ctx context.Context, config *config.Config, d } // try to request our call start - callID, trace, err := c.RequestCall(telURN, resumeURL, statusURL) + callID, trace, err := c.RequestCall(telURN, resumeURL, statusURL, channel.MachineDetection()) /// insert an channel log if we have an HTTP trace if trace != nil { diff --git a/core/ivr/twiml/twiml.go b/core/ivr/twiml/twiml.go index 3bdc2c707..a54b51479 100644 --- a/core/ivr/twiml/twiml.go +++ b/core/ivr/twiml/twiml.go @@ -182,15 +182,18 @@ type CallResponse struct { } // RequestCall causes this client to request a new outgoing call for this provider -func (c *client) RequestCall(number urns.URN, callbackURL string, statusURL string) (ivr.CallID, *httpx.Trace, error) { +func (c *client) RequestCall(number urns.URN, callbackURL string, statusURL string, machineDetection bool) (ivr.CallID, *httpx.Trace, error) { form := url.Values{} form.Set("To", number.Path()) form.Set("From", c.channel.Address()) form.Set("Url", callbackURL) form.Set("StatusCallback", statusURL) - form.Set("MachineDetection", "Enable") - form.Set("AsyncAmd", "true") - form.Set("AsyncAmdStatusCallback", statusURL) + + if machineDetection { + form.Set("MachineDetection", "Enable") + form.Set("AsyncAmd", "true") + form.Set("AsyncAmdStatusCallback", statusURL) + } sendURL := c.baseURL + strings.Replace(callPath, "{AccountSID}", c.accountSID, -1) diff --git a/core/ivr/vonage/vonage.go b/core/ivr/vonage/vonage.go index a05b628dd..80783b55a 100644 --- a/core/ivr/vonage/vonage.go +++ b/core/ivr/vonage/vonage.go @@ -404,15 +404,17 @@ type CallResponse struct { } // RequestCall causes this client to request a new outgoing call for this provider -func (c *client) RequestCall(number urns.URN, resumeURL string, statusURL string) (ivr.CallID, *httpx.Trace, error) { +func (c *client) RequestCall(number urns.URN, resumeURL string, statusURL string, machineDetection bool) (ivr.CallID, *httpx.Trace, error) { callR := &CallRequest{ AnswerURL: []string{resumeURL + "&sig=" + url.QueryEscape(c.calculateSignature(resumeURL))}, AnswerMethod: http.MethodPost, EventURL: []string{statusURL + "?sig=" + url.QueryEscape(c.calculateSignature(statusURL))}, EventMethod: http.MethodPost, + } - MachineDetection: "hangup", // if an answering machine answers, just hangup + if machineDetection { + callR.MachineDetection = "hangup" // if an answering machine answers, just hangup } callR.To = append(callR.To, Phone{Type: "phone", Number: strings.TrimLeft(number.Path(), "+")}) diff --git a/core/models/channels.go b/core/models/channels.go index 432875f32..16ede2f29 100644 --- a/core/models/channels.go +++ b/core/models/channels.go @@ -54,6 +54,7 @@ type Channel struct { Roles []assets.ChannelRole `json:"roles"` MatchPrefixes []string `json:"match_prefixes"` AllowInternational bool `json:"allow_international"` + MachineDetection bool `json:"machine_detection"` Config map[string]interface{} `json:"config"` } } @@ -91,6 +92,9 @@ func (c *Channel) MatchPrefixes() []string { return c.c.MatchPrefixes } // AllowInternational returns whether this channel allows sending internationally (only applies to TEL schemes) func (c *Channel) AllowInternational() bool { return c.c.AllowInternational } +// MachineDetection returns whether this channel should do answering machine detection (only applies to IVR) +func (c *Channel) MachineDetection() bool { return c.c.MachineDetection } + // Parent returns a reference to the parent channel of this channel (if any) func (c *Channel) Parent() *assets.ChannelReference { return c.c.Parent } @@ -169,7 +173,8 @@ SELECT ROW_TO_JSON(r) FROM (SELECT FROM unnest(regexp_split_to_array(c.role,'')) as r) ) as roles, JSON_EXTRACT_PATH(c.config::json, 'matching_prefixes') as match_prefixes, - JSON_EXTRACT_PATH(c.config::json, 'allow_international') as allow_international + JSON_EXTRACT_PATH(c.config::json, 'allow_international') as allow_international, + JSON_EXTRACT_PATH(c.config::json, 'machine_detection') as machine_detection FROM channels_channel c WHERE diff --git a/core/tasks/ivr/worker_test.go b/core/tasks/ivr/worker_test.go index 05671071e..c0958db76 100644 --- a/core/tasks/ivr/worker_test.go +++ b/core/tasks/ivr/worker_test.go @@ -78,7 +78,7 @@ type MockClient struct { callError error } -func (c *MockClient) RequestCall(number urns.URN, handleURL string, statusURL string) (ivr.CallID, *httpx.Trace, error) { +func (c *MockClient) RequestCall(number urns.URN, handleURL string, statusURL string, machineDetection bool) (ivr.CallID, *httpx.Trace, error) { return c.callID, nil, c.callError } diff --git a/web/ivr/ivr_test.go b/web/ivr/ivr_test.go index 04117c9d7..2ef31f101 100644 --- a/web/ivr/ivr_test.go +++ b/web/ivr/ivr_test.go @@ -78,7 +78,7 @@ func TestTwilioIVR(t *testing.T) { defer server.Stop() // add auth tokens - db.MustExec(`UPDATE channels_channel SET config = '{"auth_token": "token", "account_sid": "sid", "callback_domain": "localhost:8090"}' WHERE id = $1`, testdata.TwilioChannel.ID) + db.MustExec(`UPDATE channels_channel SET config = '{"auth_token": "token", "account_sid": "sid", "callback_domain": "localhost:8090", "machine_detection": true}' WHERE id = $1`, testdata.TwilioChannel.ID) // create a flow start for cathy bob, and george parentSummary := json.RawMessage(`{