4
votes

The new Twilio 5x libraries have introduced a bit of an odd approach to gathering DTMF digits on phonecalls.

The old 4x code for a gather would have looked something like:

twiml.BeginGathertwiml.BeginGather(new { numDigits = "1", action = "/TwilioCallbacks/InputResponse" });
if(x == 10){
    twiml.Say("I am saying a thing because x = 10");
}
else{
    twiml.Say("I am saying the other thing");
}
twiml.EndGather();

Now, if you wanted to let the user punch digits on the keypad while your bot was talking to them, that'd work just fine.

However in Twilio 5x, it looks like this:

twiml.Say("I am saying a really long thing where the user must wait until the twiml script reaches the gather phrase");
twiml.Say("press 1 if stack overflow is awesome, press 2 to quit programming forever");
twiml.Gather(
            numDigits: 1,
            input: new List<InputEnum>() { InputEnum.Dtmf },
            timeout: 10,
            method: "POST",
            action: new System.Uri(Startup.hostAddress + "/TwilioCallbacks/InputResponse")
        );

Right after Gather(...) you have a short window to collect the response, if you set a timeout on the response, the twiml won't proceed to the next say until the timeout expires.

How can I Gather digits in such a way that the user can interact with the keypad at any point during the message? The new approach seems to be a step backward.

edit: Clarified the 4xx use case, such that folks can understand why chaining .Say won't work here.

edit: Some people below are suggesting chaining the .Say() verb right after .Gather().

This actually doesn't behave as expected either. This is the C# code.

    twiml.Gather(
        numDigits: 1,
        input: new List<InputEnum>() { InputEnum.Dtmf },
        timeout: 10,
        method: "POST",
        action: new System.Uri(Startup.hostAddress + "/TwilioCallBot/InputResponse")
    ).Say("this is a test");

This is the resulting twiml:

<Gather input="dtmf" action="https://callbot21.ngrok.io/RCHHRATool//TwilioCallBot/InputResponse" method="POST" timeout="10" numDigits="1">
</Gather>
<Say>this is a test</Say>

The say verb needs to be inside the gather tag to get the behavior we're looking for.

1
Looking at the API example for using 5.x it looks a bit different than the way you've structured things in your question. Look here, maybe it'll help you figure it out. twilio.com/docs/libraries/csharp-dotnet/usage-guide#codefourwhey
It's a little different but to the same effect. Calling twml.Append(gather) has the exact same effect as calling twiml.Gather(...) Including the new behavior in the dtmf digit gathering.Scuba Steve
If you look at the example you'll notice that, for nesting verbs, you don't call Say() on the twml/ response object, rather you call Say() on the newly created Gather object...IronGeek
@ScubaSteve In order to provide actual code to satisfy your requirements you need to provide the current actual code in a minimal reproducible example. Not pseudo code, like you did above, so that a proper migration example can be presented.Nkosi
@Nkosi - EditedScuba Steve

1 Answers

3
votes

Okay I've found the issue. It looks like fourwhey was correct to point at the API docs there. What I didn't catch was that the .Say was being appended to the gather in a specific way.

This:

twiml.Gather(...).Say("say a thing");

doesn't work the same as this:

var gather = new Twilio.TwiML.Voice.Gather(...).Say("say a thing");

best I can work out is that there's actually two gather methods, and that twiml.Gather(...) is actually calling Twilio.TwiML.Gather.

From here we can build our dynamic voice message and nest the Say verb like so:

gather.Say("Say a thing");
gather.Say("Say another thing");

And the twiml will get spit out the way we intended like so:

<gather>
    <say>say a thing</say>
    <say>say another thing</say>
</gather>