Voicemail calling back 20 seconds later - twilio

I'm trying to set up voicemail for a Twilio # (Not Twilio Cell)... And it works, for the most part, except I get called back 20 seconds later with nothing there (Step 5 Below). I think my issues are in TWIML 1/2 from testing, but I don't know.
I'll call in to my Twilo # from my cell.
I hit the reject on my cell (Even though my time out is 1, it doesn't give up.).
Leave the message
Hang up || press button to end
[~20 seconds after step 4] Get blank call back
[10-15 secs after step 5] Get my text / email as expected
Am I doing something wrong in TWIML 2?
or
Is using my cell for orig/term causing problems?
Incoming:
TWIML 1:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial action="https://-->TWIML 2"
timeout="1"
callerId="{{To}}"
>
<Number>##CELL##</Number>
</Dial>
</Response>
TWIML 2:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say>
Please leave a message
</Say>
<Record action="https://TWIML 3"
method="POST"
maxLength="120"
playBeep="false"
trim="do-not-trim"
transcribe="true"
transcribeCallback="https://FUNCTION 1"
/>
<Say>I did not receive a recording.</Say>
</Response>
TWIML 3
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Say>Bi</Say>
</Response>
FUNCTION 1
exports.handler = function(context, event, callback) {
var client = context.getTwilioClient();
const mailgun = require('mailgun-js')({apiKey: context.MG_KEY, domain:context.MG_DOMAIN});
var async = require('async');
var sendSMS = function(callback){
let dataSMS = {
to: '##CELL##',
from: event.To,
body: 'Voicemail from:\n ' + event.From + '\n\nText:\n' + event.TranscriptionText + '\n\n---MSG Recording---\n' + event.RecordingUrl
};
client.messages.create(dataSMS).then(function(response){
console.log('N [T1]: ' + response.status);
return callback(null, response);
}).catch(function(err){
console.log('T2: ' + err);
});
};
var sendEmail = function(callback){
let dataMG = {
to: '##TO##',
from: '##FROM##',
subject: 'Voicemail: ' + event.From,
text: event.TranscriptionText + '\n\n--- MSG Recording --->\n' + event.RecordingUrl
};
console.log('-------- Sending Mail (Mailgun API)---->' + dataMG.to);
mailgun.messages().send(dataMG, function (error, body) {
console.log('N [MB] Mailgun Body :' + body);
return callback(null, body);
});
};
var waitTime = function(callback){
setTimeout(function() {
return callback(200);
}, 1000);
};
var asyncTasks = [sendSMS, sendEmail, waitTime];
async.parallel(asyncTasks, function(err, result){
console.log('--------------------------DONE--------------------------' + result[0] + result[1]);
callback(null, 200);
});
};
Resolved:
So a couple days later and it is working as expected, without being phoned back ~20 seconds later. I don't think I changed anything, but who knows; now I'm having trouble forcing the unexpected behavior to occur :)
Below is an example of how to set up a voicemail system that will transcribe your message to a MMS including a link to the original voice message. This also includes an email copy, if you had a mailgun account set up.
There is an extra fee associated with using the transcription service, and you will have to delete the recordings eventually after you no longer want access to the recording.
If you are to try this out, when clean the code for you situation, the timeout variable in Twiml 1 is how many seconds it will ring for. Function 1 requires async, but you could write it without when tailoring for you situation.

Related

Twilio Redirecting the wrong call when using CallSid and trying to update live call

So here's the logic of what I am working on:
Someone calls my Twilio Number I use the dial twiml to forward to a cell phone
I use gather and play a whisper to the operator answering the phone (so his cell phone).
The operator has a choice - press 1 to accept, press 2 (transfers to
a different agent).
step 3 is where I am having trouble I am using the code below:
$call = $twilio->calls($CallSid)
->update([
"method" => "POST",
"url" => "http:www.example.com/directcall.php"
]
);
Here's the problem it is modifying the call but it's redirecting the operators phone number instead of the person who is calling in. So the operator is getting redirected to the other operator and the customer is being hung up on. I tried using the parentcallsid too but that doesn't seem to work either.
Any ideas what I am doing wrong?
so just to be clear I want the flow to work like this:
Customer calls phone number -> redirects to designated operator -> if designated operator presses 2 it redirects the customer to operator 2 and disconnects operator 1 from the call. Is this possible?
Thanks for the help, I greatly appreciate it.
UPDATE PLEASE FIND THE CODE SAMPLES BELOW
Index.php
<?php
include ("config.php");
require_once './vendor/autoload.php';
use Twilio\TwiML\VoiceResponse;
$response = new VoiceResponse();
$twilionumber = ltrim($_POST['To'], '+');
$callernumber=ltrim($_POST['From'], '+');
createCall($phonenumbertouse,$response,$twilionumber);
echo $response;
function createCall($phonenumbertouse,$response,$twilionumber) {
$dial = $response->dial('',['timeout' => '30']);
$dial->number($phonenumbertouse, ['action' => "http://example.com/whisper.php",'method' => 'GET']);
}
WHISPER.PHP
<?php
include ("config.php");
require_once './vendor/autoload.php';
use Twilio\TwiML\VoiceResponse;
$response = new VoiceResponse();
$gather = $response->gather(['action' => "http://example.com/route.php",
'method' => 'GET']);
$gather->say($whisper, ['voice' => 'woman', 'language' => 'en-US']);
echo $response;
?>
route.php
<?php
include ("config.php");
require_once './vendor/autoload.php';
use Twilio\TwiML\VoiceResponse;
use Twilio\Rest\Client;
$response = new VoiceResponse();
$keyedInput=$_REQUEST['Digits'];
$mycallsid=$_REQUEST['ParentCallSid'];
if ($keyedInput == 1){
$response->say('connecting the call');
}
elseif ($keyedInput == 2){
$twilio = new Client($sid, $token);
$call = $twilio->calls($mycallsid)
->update([
"method" => "POST",
"url" => "http://example.com/redirect.php"
]
);
}
elseif ($keyedInput == 3){
$response->say('you selected 3');
}
else {
$response->say('Sorry, I don\'t understand that choice.');
}
echo $response;
?>
**Redirect.php **
<?php
include ("config.php");
require_once './vendor/autoload.php';
use Twilio\TwiML\VoiceResponse;
$response = new VoiceResponse();
$dial = $response->dial('+14151234567',['timeout' => '30']);
echo $response;
?>
Twilio developer evangelist here.
Your issue here is that you are using the wrong CallSid to update.
In this case there are two CallSids at play. When your user dials in to your Twilio number, that call between the user and Twilio has one CallSid. Then, when Twilio creates an outbound call to the operator, that call has a different CallSid.
In your application, when you get the <Gather> response from the operator, the CallSid being sent to your endpoint is the CallSid of the operator's call leg. Instead, you need to find the CallSid of the original call.
You should find that the ParentCallSid parameter is sent to the webhook endpoint as well. That ParentCallSid is the original inbound call's Sid and is what you should use to redirect the caller to another operator.
Edit
OK, so I had a go at building this. I don't normally work in PHP, so I wrote this in Node.js (as Twilio Functions). I got it to work and the answer still seems to me to be "use the ParentCallSid, so hopefully it gives you some idea where you might have gone wrong.
The incoming call
This makes an outbound call to my cell phone number, with a url set to make the whisper to me when I answer the call.
exports.handler = function (context, event, callback) {
const twiml = new Twilio.twiml.VoiceResponse();
const dial = twiml.dial();
dial.number({ url: "/whisper" }, MY_CELL_PHONE_NUMBER);
callback(null, twiml);
};
<Response>
<Dial><Number url="/whisper">MY_CELL_PHONE_NUMBER</Number></Dial>
</Response>
The whisper
This returns a <Gather> spoken to the person answering the cell phone number. It offers a choice, dial "1" to connect or "2" to hang up, triggering the original caller to dial to another number.
exports.handler = function (context, event, callback) {
const twiml = new Twilio.twiml.VoiceResponse();
const gather = twiml.gather({ digits: 1, action: "/after-whisper" });
gather.say("Dial 1 to connect, dial 2 to hang up.");
callback(null, twiml);
};
<Response>
<Gather action="/after-whisper" digits="1">
<Say>Dial 1 to connect, dial 2 to hang up.</Say>
</Gather>
</Response>
The Gather action "/after-whisper"
This checks the Digits parameter. If it is "1" it immediately calls back with an empty response, signalling the end of the whisper and leading the call to connect with the original caller.
If the Digits parameter is not "1" then we initialise a Twilio client and make a request to the API to update the call with the sid ParentCallSid to a new URL. Once that is complete we return TwiML that says to <Hangup> the whisper call (which wasn't strictly necessary, but I like the intention).
exports.handler = async function (context, event, callback) {
const twiml = new Twilio.twiml.VoiceResponse();
if (event.Digits === "1") {
callback(null, twiml);
} else {
const client = context.getTwilioClient();
try {
// Update the parent call with a new URL.
await client.calls(event.ParentCallSid).update({
url: URL_TO_NEXT_TWIML,
});
} catch (error) {
console.log(error);
}
twiml.hangup();
callback(null, twiml);
}
};
The next TwiML
This is the endpoint that the url above relates to, when it gets the callback it just returns a <Dial> to another number, though it could include another whisper and go round this loop again if desired.
exports.handler = async function (context, event, callback) {
const twiml = new Twilio.twiml.VoiceResponse();
twiml.dial(NEXT_NUMBER_TO_DIAL);
callback(null, twiml);
};
<Response>
<Dial>NEXT_NUMBER_TO_DIAL</Dial>
</Response>

Twilio Functions Error 20429 - Too many requests multiple sms messages

I am using Twilio functions and Programable SMS to send SMS Messages to a list of numbers form my iOS App. There are just over 100 mobile numbers (113 on the time of this error) in the list. Most of these messages send but then the function says that it timed out after 502ms.
I am using the example code from Twilio to send to group messages (that I have copied below) and making a URLSession request from my iOS app.
Is there a way that I can fix this issue so that I can send to this fairly large list of phone numbers or make the function run for longer?
Thank you very much for your help.
Tom
Request:
let messagesPhoneNumberString = [+447987654321abc,+447123789456def,+447123456789ghi]
"https://myfunction.twil.io/send-messages?phoneAndID=\(messagesPhoneNumberString)&globalID=\(current globalID)"
My Twilio Function Code:
exports.handler = function(context, event, callback) {
let phoneAndIDString = event['phoneAndID'];
let globalID String = event['globalID'];
let numbersArray = phoneAndIDString.split(",");
Promise.all(
numbersArray(number => {
let phoneNumberSplit = "+" + number.slice(1, 13);
let idSplit = number.slice(13);
console.log('Send to number: ' + phoneNumberSplit + ' - with ID: ' + idSplit);
return context.getTwilioClient().messages.create({
to: phoneNumberSplit,
from: 'Example',
body: 'Hello World: ' + idSplit
});
})
)
.then(messages => {
console.log('Messages sent!');
callback(null, response);
})
.catch(err => console.error(err));
};
Twilio developer evangelist here.
Twilio Functions has a timeout of 5 seconds, so it is likely not the best idea to use a Twilio Function to send that many messages in one go.
You have some options though.
If you are sending all those numbers the same message then you could use the Twilio Notify passthrough API. Check out the details in this blog post about sending mass messages with Node.js.
Otherwise, if you have to send different messages then you could split up the numbers into batches and use the same function multiple times.
Finally, you could use a different platform to send the messages that doesn't have a 5 second limit.
In addition to the options provided in Phil's answer you could use recursion.
You could trigger the process from your app and pass all numbers in the initial function call just like you do now.
Then, the idea is to send just one message per function call and let the Twilio function call itself after it receives the response from .create(). This means no concurent calls to send messages, messages are sent one after another though the order in which they are received is not necessary the order in which the numbers are passed in the query string.
You'll need to add axios in the function dependencies configuration (https://www.twilio.com/console/runtime/functions/configure).
Axios is used to make the HTTP request to the function from within the function.
Each function run, tests for the stop condition which happens when the phone numbers query string length is zero. Then, uses .shift() to remove the first element from the numbers array to work with it. The remaining array is passed to the next function call.
This is the code I've tried, and it worked for me, but you'll have to change (the 11 length on .slice() method) for +44 because I've tested with US numbers +1 which are shorter in length.
exports.handler = function(context, event, callback) {
const axios = require("axios");
let phoneAndIDString = event["phoneAndID"].trim();
console.log(phoneAndIDString);
let globalIDString = event["globalID"].trim();
// stop condition for recursive call
if (phoneAndIDString.length === 0) {
return callback(null, true);
}
let numbersArray = phoneAndIDString.split(",");
console.log(numbersArray);
// take the first item of array
let number = numbersArray.shift();
console.log(number);
// the remaining array will be passed to the next function call
phoneAndIDString = numbersArray.join();
console.log(phoneAndIDString);
let phoneNumberSplit = "+" + number.slice(0, 11);
let idSplit = number.slice(11);
console.log("Send to number: " + phoneNumberSplit + " - with ID: " + idSplit);
context
.getTwilioClient()
.messages.create({
to: phoneNumberSplit,
from: "+17775553333",
body: "Hello World: " + idSplit
})
.then(msg => {
console.log("Message sent: " + msg.sid);
axios
.get(
"https://foo-bar-1234.twil.io/send-messages?globalID=baz&phoneAndID=" +
phoneAndIDString
)
.then(function(response) {
// handle success
console.log(response.status);
return callback(null, true);
})
.catch(function(err) {
// handle error
console.error(err);
});
})
.catch(function(err) {
console.error(err);
});
};
Try to go step by step with it while testing, console log things and then return early with return callback(null, true) as you go from top to bottom so you make sure you don't go in a loop.

Twilio Studio > Function > Voicemail Issue

We are currently using Twilio Studio to accept incoming calls. These calls are then handed off to a function, which is a modified version of https://github.com/philnash/useful-twilio-functions/tree/master/hunt, which attempts to reach the agent on their cellphone using whisper (which Studio doesn't support natively). This is the main function:
exports.handler = function(context, event, callback) {
const numbers = context.AGENT1_NUMBERS.split(',').map(number => number.trim());
const response = new Twilio.twiml.VoiceResponse();
if (event.DialCallStatus === 'complete') {
// Call was answered and completed
response.hangup();
} else if (event.finished === 'true') {
if (context.AGENT1_FINAL_URL) {
response.redirect(context.AGENT1_FINAL_URL);
} else {
response.hangup();
}
} else {
const numberToDial = event.nextNumber ? event.nextNumber : numbers[0];
const currentNumberIndex = numbers.indexOf(numberToDial);
let url;
if (currentNumberIndex + 1 === numbers.length) {
// No more numbers to call after this.
url = 'https://redacted.twil.io/agent1?finished=true';
} else {
const nextNumber = numbers[currentNumberIndex + 1];
url = 'https://redacted.twil.io/agent1?nextNumber=' + encodeURIComponent(nextNumber);
}
const dial = response.dial({ action: url });
dial.number({ url: 'https://URL_TO_WHISPER_TWIML' }, numberToDial);
}
callback(null, response);
};
This is the TwiML Bin for whisper.
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather action="URL_TO_FUNCTION" numDigits="1">
<Say>You are receiving a call, please dial 1 to accept or anything else to reject</Say>
</Gather>
</Response>
This is the next function which connects the call.
exports.handler = function(context, event, callback) {
const response = new Twilio.twiml.VoiceResponse();
if (event.Digits === '1') {
response.say('Connecting');
} else {
response.hangup();
}
callback(null, response);
}
Now, the issue here is that the AGENT1_FINAL_URL points to a TwiML bin that handles voicemail. This process occurs just fine if the call is answered by the agent and disconnected without pressing 1. The caller is sent to the Twilio voicemail. If, however, the agent just misses the call or rejects the call, the call continues to ring on the caller's end until I suspect it's times out, after which the caller is connected anyway and will hear a cut off version of the agents personal voicemail on their cellphone. Any idea why this is happening?

Hunt Group for Twilio, using Twilio Functions. (aka FindMe )

I am trying to set up a hunt group with Twilio Twiml
Do I have to set up a different twimlbin for each number in the hunt group?
Or is there a way to join all this together into a single Twimlbin?
Twimlbin 1:
<Response>
<Dial
action="http://www.ourapp.com/webhook;FailUrl=/Twimlbin 2"
timeout="10"
callerId="555-555-5555">
NUMBER1
</Dial>
</Response>
Twimlbin 2:
<Response>
<Dial
action="http://www.ourapp.com/webhook;FailUrl=/Twimlbin 3"
timeout="10"
callerId="555-555-5555">
NUMBER2
</Dial>
</Response>
... Repeat N times for each agent ...
Thank you :-)
Twilio developer evangelist here.
TwiML Bins are great for static bits of TwiML, but your use case needs a bit more than that.
I recommend you check out Twilio Functions which allow you to run Node.js code in Twilio's infrastructure. I've built and tested a version of this that works with Twilio Functions.
Here's an explanation of how it works:
Start with an array of your numbers:
const numbers = [...];
Then, in the function, check if the callstatus is completed and hang up if it is.
exports.handler = function(context, event, callback) {
const response = new Twilio.twiml.VoiceResponse();
if (event.DialCallStatus === "complete" || event.finished) {
// Call was answered and completed or no one picked up
response.hangup();
} else {
If it's not, we work out the next number to call. If you have the next number in the URL. If you do save it to a variable, otherwise pick the first number in the array:
const numberToDial = event.nextNumber ? event.nextNumber : numbers[0];
Then, assign the next number to dial from the array.
let url;
const currentNumberIndex = numbers.indexOf(numberToDial);
if (currentNumberIndex + 1 === numbers.length) {
// no more numbers to call after this.
url = "/hunt?finished=true";
} else {
const nextNumber = numbers[currentNumberIndex + 1];
url = "/hunt?nextNumber=" + encodeURIComponent(nextNumber);
}
Then generate the TwiML to Dial the next number and pass the URL as the action. You can add your own URL as the statusCallbackUrl to keep a track of the statuses.
const dial = response.dial({ action: url });
dial.number({ statusCallback: "https://yourapp.com/statuscallback" }, numberToDial);
}
callback(null, response);
}
I can't promise this will work, but I hope you see where I'm going with it. Let me know if it helps at all.

TwiML App unexpected end of call: Cannot find the declaration of element 'response'

I created TwiML application and set Voice request URL the web deployed ASP.NET-MVC aplication action method: http://voiceapp-001-site1.myasp.net/voice
This action method is invoked when somebody goes to the URL posted above:
public ActionResult Voice() {
Response.ContentType = "text/xml";
// put a phone number you've verified with Twilio to use as a caller ID number
string callerId = Settings.TwilioNumber;
// put your default Twilio Client name here, for when a phone number isn't given
string number = "jenny";
// get the phone number from the page request parameters, if given
if (Request["PhoneNumber"] != null) {
number = Request["PhoneNumber"];
}
// wrap the phone number or client name in the appropriate TwiML verb
// by checking if the number given has only digits and format symbols
string numberOrClient;
var m = Regex.Match(number, #"^[\d\+\-\(\) ]+$");
if (m.Success) {
numberOrClient = string.Format("<Number>{0}</Number>", number);
} else {
numberOrClient = string.Format("<Client>{0}</Client>", number);
}
ViewBag.CallerId = callerId;
ViewBag.NumberOfClient = numberOrClient;
return View();
}
The Voice view looks like:
<?xml version="1.0" encoding="UTF-8" ?>
<response>
<dial callerid="#ViewBag.CallerId">
#Html.Raw(ViewBag.NumberOfClient)
</dial>
</response>
Then I try to make test call:
but after 13 seconds call is automatically terminated and In the error log I get:
Notification SID NOe85ffe80dfc52e81f942a7414c9f7c9c
Warning 12200 Schema validation warning
Description Cannot find the declaration of element 'response'.
But below in the Body section I can see the element response:
Hi Twilio developer evangelist here.
Can you try modifying your view to look like this instead?
<?xml version="1.0" encoding="UTF-8" ?>
<Response>
<Dial callerId="#ViewBag.CallerId">
#Html.Raw(ViewBag.NumberOfClient)
</Dial>
</Response>
Remember XML is case-sensitive, so the casing in your XML tags actually matter. Also, I would suggest using the Twilio.TwiML helper library. With that, you can get your XML generated for you with the right casing, and avoiding typos altogether.
Here's how you'd do it:
var twiml = new TwilioResponse();
var dialAttributes = new {callerId = "48326304351"};
var dial = twiml.Dial("+15555555555", dialAttributes);
return TwiML(dial);

Resources