Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement asynchronous AMD for Twilio IVR #476

Merged
merged 1 commit into from
Aug 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion core/ivr/ivr.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,11 @@ func ResumeIVRFlow(
return WriteErrorResponse(ctx, rt.DB, client, conn, w, errors.Errorf("active session: %d does not match connection: %d", session.ID(), *session.ConnectionID()))
}

// check connection is still marked as in progress
if conn.Status() != models.ConnectionStatusInProgress {
return WriteErrorResponse(ctx, rt.DB, client, conn, w, errors.Errorf("connection in invalid state: %s", conn.Status()))
}

// preprocess this request
body, err := client.PreprocessResume(ctx, rt.DB, rt.RP, conn, r)
if err != nil {
Expand Down Expand Up @@ -590,7 +595,7 @@ func HandleIVRStatus(ctx context.Context, rt *runtime.Runtime, oa *models.OrgAss
// no associated start? this is a permanent failure
if conn.StartID() == models.NilStartID {
conn.MarkFailed(ctx, rt.DB, time.Now())
return client.WriteEmptyResponse(w, "status updated: F")
return client.WriteEmptyResponse(w, "no flow start found, status updated: F")
}

// on errors we need to look up the flow to know how long to wait before retrying
Expand Down
18 changes: 9 additions & 9 deletions core/ivr/twiml/twiml.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func (c *client) RequestCall(number urns.URN, callbackURL string, statusURL stri
form.Set("StatusCallback", statusURL)
form.Set("MachineDetection", "Enable")
form.Set("AsyncAmd", "true")
form.Set("AsyncAmdStatusCallback", callbackURL)
form.Set("AsyncAmdStatusCallback", statusURL)

sendURL := c.baseURL + strings.Replace(callPath, "{AccountSID}", c.accountSID, -1)

Expand Down Expand Up @@ -289,14 +289,14 @@ func (c *client) ResumeForRequest(r *http.Request) (ivr.Resume, error) {

// StatusForRequest returns the current call status for the passed in status (and optional duration if known)
func (c *client) StatusForRequest(r *http.Request) (models.ConnectionStatus, int) {
// we re-use our status callback for AMD results which will have an AnsweredBy field but no CallStatus field
answeredBy := r.Form.Get("AnsweredBy")

switch answeredBy {
case "human", "unknown", "":
break

default:
return models.ConnectionStatusErrored, 0
if answeredBy != "" {
switch answeredBy {
case "machine_start", "fax":
return models.ConnectionStatusErrored, 0
}
return models.ConnectionStatusInProgress, 0
}

status := r.Form.Get("CallStatus")
Expand All @@ -316,7 +316,7 @@ func (c *client) StatusForRequest(r *http.Request) (models.ConnectionStatus, int
return models.ConnectionStatusErrored, 0

default:
logrus.WithField("call_status", status).Error("unknown call status in ivr callback")
logrus.WithField("call_status", status).Error("unknown call status in status callback")
return models.ConnectionStatusFailed, 0
}
}
Expand Down