Twilio TWIML nodejs gather sample code control flow - node.js

I was checking this sample on twilio docs (v2.x but v3.x also is similar and my question won't be altered).
// This example uses JavaScript language features present in Node.js 6+
'use strict';
const express = require('express');
const twilio = require('twilio');
const urlencoded = require('body-parser').urlencoded;
let app = express();
// Parse incoming POST params with Express middleware
app.use(urlencoded({ extended: false }));
// Create a route that will handle Twilio webhook requests, sent as an
// HTTP POST to /voice in our application
app.post('/voice', (request, response) => {
// Use the Twilio Node.js SDK to build an XML response
let twiml = new twilio.TwimlResponse();
// Use the <Gather> verb to collect user input
twiml.gather({ numDigits: 1 }, (gatherNode) => {
gatherNode.say('For sales, press 1. For support, press 2.');
});
// If the user doesn't enter input, loop
twiml.redirect('/voice');
// Render the response as XML in reply to the webhook request
response.type('text/xml');
response.send(twiml.toString());
});
// Create an HTTP server and listen for requests on port 3000
app.listen(3000);
So here is the fragment below blocking?
twiml.gather({ numDigits: 1 }, (gatherNode) => {
gatherNode.say('For sales, press 1. For support, press 2.');
});
If yes then assuming user enters something and we then move to
twiml.redirect('/voice');
and other statements execute in sequence.
BUT if its non blocking, then /voice endpoint is called immediately and this continues in an infinite loop.
I was wondering how would the flow work.
EDIT:
The confusion seems to be caused by this comment
// If the user doesn't enter input, loop
If user enters something then also twiml.redirect('/voice') is being called. I am not sure how does that code even work properly?

Ricky from Twilio here.
This code doesn't create an infinite loop, but for a bit of a different reason than blocking vs non-blocking code. The way that you control a Twilio call flow is through TwiML, which is XML containing a set of instructions of what to do with an incoming call. The node code in your /voice route isn't handling the control flow itself, but generating XML that looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Gather numDigits="1">
<Say>For sales, press 1. For support, press 2.</Say>
</Gather>
<Redirect>/voice</Redirect>
</Response>

Related

Indicating a WhatsApp message is received, via the Twilio API, in NodeJS?

We are creating a service that will receive WhatsApp messages via the Twilio service. This works, but our issue is that we can't work out how to tell the sender that our server has 'read' the message. The messages always appear as being 'delivered' and never 'read', even after responding to the message. We have looked in the documentation, but can't seem to see how to do this.
Our server is written in NodeJS and is using Express for HTTP side.
Below is an equivalent of the code we are using (not a running example):
import { twiml } from 'twilio';
const { MessagingResponse } = twiml;
async receiveMessage(req: Request, res: Response, next: NextFunction) {
const message = req.body;
// Send back an empty response, we will process asynchronously
const immediateResponse = new MessagingResponse();
res.setHeader('content-type', 'text/xml');
res.send(immediateResponse.toString());
// TODO indicate message as read
// Do what ever logic is needed for given message
const replyMessage = await processMessage(message);
const messageToTwilio = {
body: replyMessage,
from: message.To,
to: message.From
};
const twilioResponse = await this.client.messages.create(messageToTwilio);
// Record value of twilioResponse in DB
}
Can anyone suggest what in the API I should be using for this?
I contacted Twilio on this issue and it turns out this is not currently possible. While they consider this a useful functionality, it is not currently a priority for implementation.
Note, It is possible to get the delivery status of outgoing messages, via the status webhook, but it is not possible to indicate to the remote party that the incoming message was 'read'.

How to use twilio and twiml to record a call and redirect the call or join someone else to the call

Hi im trying with node to receive a call, start recording the call and join someone else to the call (also tried redirect) and maintain the call recording. all this have been without success.
i tried
twiml.say('HI, Your Calling XXXXX, ');
twiml.dial("XXXXXXX");
twiml.record();
but when the dial command is used the record command doesn't work, if i comment the dial command, the call get recorded.
pleas hope you can help me find how i can record and redirect a call and while keep recording it
Twilio developer evangelist here.
Using <Record> only records one leg of a call and is mostly used for voicemail style operations.
To record inbound calls with Twilio you need to use the Call Recordings API to trigger the recording to start.
Note, you need to have upgraded your account to trigger recordings like this.
You can trigger this API call as soon as you receive the inbound webhook. You can see the documentation for the call recording resource here. If you were writing this in Node.js using Express, your route might look like this:
// Load your Account Sid and Auth Token somehow, for example, from the environment
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const twilio = require('twilio')
const client = twilio(accountSid, authToken);
app.post('/calls', (req, res) => {
const callSid = req.body.CallSid;
client.calls(callSid)
.recordings
.create()
.then(recording => console.log('Recording started')
.catch(err => console.error('Recording failed. ', err);
const twiml = new twilio.twiml.VoiceResponse();
const numberToDial = process.env.NUMBER_TO_DIAL;
twiml.say(`Hi, you're calling ${numberToDial}.`);
twiml.dial(numberToDial);
res.set('Content-Type', 'text/xml');
res.send(twiml.toString());
});
Let me know if that helps at all.

Prompt agent for acceptance before dequeuing an incoming call

I've set up a basic workflow on Twilio/node.js, that will transfer an incoming call to agents' phones. Because the agents might have a voicemail setup on their phone/cell phone, I wanted to prompt them for acceptance through a "gather" instruction. If they press 1, then the call will be transferred ("dequeue" instruction). Otherwise, the reservation is to be rejected.
My problem is that I don't know how to order the instructions: if I gather before enqueuing, then gather doesn't seem to know yet "who to ask". But if enqueue before gathering, then the prompt message will be heard by the caller (incoming client) and it will be too late to reject the reservation...
Twilio developer evangelist here.
In this case you should not respond to the assignment callback with "dequeue". Instead, you should use the "call" instruction to initiate a call to the agent instead.
With the call instruction you include a URL that will be requested when the call connects to the agent. At this point you should read out your message and use <Gather> to ensure the agent is ready for the call. When the agent presses a button or the <Gather> times out, the action attribute of the <Gather> will be requested. Then, if you find the user did enter a digit, by checking the Digits parameter you can respond with TwiML to <Dial> the <Queue>. In order to dial the actual caller that the reservation refers to, you need to add the reservationSid attribute to the <Queue> element. You will receive the ReservationSid as a parameter in the task assignment webhook. When the call bridges it will accept the reservation.
If the <Gather> times out, then you should just reject the reservation and it will be passed to the next worker.
Let me know if that helps at all.
Edit
Example TwiML for this situation.
You first respond to the assignment callback with JSON, using a call instruction:
{
"instruction": "call",
"to": AGENT_PHONE_NUMBER,
"from": YOUR_TWILIO_NUMBER,
"url": "https://example.com/worker_call?ReservationSid=${req.body.ReservationSid}&TaskSid=${req.body.TaskSid}"
}
Note, I have added the ReservationSid and TaskSid from the incoming request to the URL of the webhook that is called when the worker answers their phone.
Then when the worker answers their phone Twilio calls to the url we set above and we use it to ask them if they want to take the call.
# /worker_call
<Response>
<Gather action="/gather_result?ReservationSid=${req.query.ReservationSid}&TaskSid=${req.query.TaskSid}" numDigits="1">
<Say voice="alice">You have an incoming call, dial any number to accept</Say>
</Gather>
<Redirect>/gather_result?ReservationSid=${req.query.ReservationSid}&TaskSid=${req.query.TaskSid}</Redirect>
</Response>
Note, I am passing the ReservationSid and TaskSid along from the incoming request URL to the URL of the <Gather> action. Now, when the action URL is called we need to respond based on whether the call was accepted or not. I need to use some actual code for this rather than just TwiML, so I'll do it with Node. We need to be able to call the reservations resource in the REST API, so this comes with some setup:
const accountSid = 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
const authToken = 'your_auth_token';
const workspaceSid = 'WSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
const twilio = require('twilio')
const client = twilio(accountSid, authToken);
app.post('/gather_result', function(req, res) {
const twiml = new twilio.twiml.VoiceResponse();
if (req.body.Digits && req.body.Digits.length > 0) {
// There was a positive result, call the Queue and connect the caller
const dial = twiml.dial();
dial.queue({queue: req.query.ReservationSid});
res.send(twiml.toString());
} else {
// no response, reject the reservation.
client.taskrouter.v1
.workspaces(workspaceSid)
.tasks(req.query.TaskSid)
.reservations(req.query.ReservationSid)
.update({ reservationStatus: 'rejected' })
.then(function() {
// once the reservation is updated, then hangup the call.
twiml.hangup();
res.send(twiml.toString());
});
}
});

Access transcriptionText from twilio

I want to access the transcription text that has been generated by transcribe in my Twilio account under transcriptions as I want to compare user recorded response as text
twiml.say('Hi there! Please speak your response after the beep,-Get ready!')
.record({
transcribe:true,
timeout:5,
maxLength:30,
transcribeCallback:'/recording',
action:'/recording'
});
app.post('/recording', (request,response) => {
if(transcriptionText=='yes'){
twiml.say('thank you for positive response');
}
response.type('text/xml');
response.send(twiml.toString());
});
Twilio developer evangelist here.
When using transcription with <Record>, once the recording is complete the call will continue on to make a request to the action attribute synchronously. Whatever you return from the action attribute URL will control the call.
The actual transcription, however, takes a bit more time and when you get a webhook to the transcribeCallback URL it is done asynchronously, outside the context of the call. So, returning TwiML will not affect the call at all.
You will get the transcription text by inspecting the body of the request. There are plenty of parameters sent to the transcribeCallback, but the one you are looking for is the TranscriptionText. In your Node.js app, which looks like Express to me, you can get hold of it by calling request.body.TranscriptionText.
If you do want to affect the call when you receive the transcribe callback you will need to use the REST API to modify the call and redirect it to some new TwiML.
Let me know if that helps at all.
[edit]
From the comments I can see you are trying to drive a part of the call from a spoken response. The transcribeCallback URL isn't called immediately as the transcription needs to be done, so you need an action URL that you can send your caller to while you wait.
So, adjust your recording route to have different endpoints for action and transcribeCallback:
app.post("/voice", (request, response) => {
var twiml = new twilio.TwimlResponse();
twiml.say('Hi there! Please speak your response after the beep,-Get ready!')
.record({
transcribe:true,
timeout:5,
maxLength:30,
transcribeCallback:'/transcribe',
action:'/recording'
});
response.type('text/xml');
response.send(twiml.toString());
})
Then your recording endpoint will need to keep the user waiting while Twilio transcribes the text.
app.post('/recording', (request,response) => {
var twiml = new twilio.TwimlResponse();
// A message for the user
twiml.say('Please wait while we transcribe your answer.');
twiml.pause();
// Then redirect around again while we wait
twiml.redirect('/recording');
response.type('text/xml');
response.send(twiml.toString());
});
Finally, when you get the transcribe callback you can figure out the course from the transcribed text somehow and then redirect the live call into a new endpoint that carries on the call with the new information.
app.post('/transcribe', (request, response) => {
var text = request.body.TranscriptionText;
var callSid = require.body.CallSid;
// Do something with the text
var courseId = getCourseFromText(text);
var accountSid = '{{ account_sid }}'; // Your Account SID from www.twilio.com/console
var authToken = '{{ auth_token }}'; // Your Auth Token from www.twilio.com/console
var client = new twilio.RestClient(accountSid, authToken);
// Redirect the call
client.calls(callSid).update({
url: '/course?courseId=' + courseId,
method: 'POST'
}, (err, res) => {
response.sendStatus(200);
})
});

how to start a conference with twilio?

Plan for my Programm: Someone is calling a Twilio Number, then I get called on my mobile. As long im not accept the call, the caller should hear music or something like that and if I pick up, a conference should start.
At the moment:
Someone is calling the number and gets in queue with music, then my mobile gets called. Sounds great yet, but when i pick up we don't get connected.
So I guess I don't get properly how Twilio conference works, may you have some advices how to get this done step by step.
Twilio developer evangelist here.
OK, you're building with Node.js and I know I've seen your code in other questions, but I'm going to start this one from scratch.
With Twilio the flow that you described should look like this.
Person calls your Twilio number
Twilio makes an HTTP request to the number's Voice Request URL (which points at your application)
Your application now needs to do two things
Call your mobile number
Put the caller into a Conference with hold music playing
First you need to decide on the name of the Conference
Then to call your number, your application needs to make a call to the Twilio REST API to initiate the call to your mobile
You need to provide a callback URL in this call that will put you into the conference too, you can do this by including the conference name as a paramater in the URL
Your application also needs to return TwiML to put the caller into the Conference, using the same conference name
Finally, when you answer the phone Twilio will make an HTTP request to the URL you provided when initiating the call
That will request your URL which should respond with TwiML to put you into the same conference call
Let's look at how we can do this in a Node.js Express application:
You need two routes, one that will receive the initial request and one that will respond to the request made when you answer your phone. Check out the code and the comments below.
var express = require("express");
var bodyParser = require("body-parser");
var twilio = require("twilio");
var app = express();
app.use(bodyParser.urlencoded({ extended: true }));
var client = twilio(YOUR_ACCOUNT_SID, YOUR_AUTH_TOKEN);
// This is the endpoint your Twilio number's Voice Request URL should point at
app.post('/calls', function(req, res, next) {
// conference name will be a random number between 0 and 10000
var conferenceName = Math.floor(Math.random() * 10000).toString();
// Create a call to your mobile and add the conference name as a parameter to
// the URL.
client.calls.create({
from: YOUR_TWILIO_NUMBER,
to: YOUR_MOBILE_NUMBER,
url: "/join_conference?id=" + conferenceName
});
// Now return TwiML to the caller to put them in the conference, using the
// same name.
var twiml = new twilio.TwimlResponse();
twiml.dial(function(node) {
node.conference(conferenceName, {
waitUrl: "http://twimlets.com/holdmusic?Bucket=com.twilio.music.rock",
startConferenceOnEnter: false
});
});
res.set('Content-Type', 'text/xml');
res.send(twiml.toString());
});
// This is the endpoint that Twilio will call when you answer the phone
app.post("/join_conference", function(req, res, next) {
var conferenceName = req.query.id;
// We return TwiML to enter the same conference
var twiml = new twilio.TwimlResponse();
twiml.dial(function(node) {
node.conference(conferenceName, {
startConferenceOnEnter: true
});
});
res.set('Content-Type', 'text/xml');
res.send(twiml.toString());
});
Let me know if this helps at all.
Update
In the latest version of the Twilio Node.js library you cannot use twilio.TwimlResponse. Instead there are individual classes for voice and messaging. In this example the line
var twiml = new twilio.TwimlResponse();
Should be updated to:
var twiml = new twilio.twiml.VoiceResponse();
I solved it by myself, you need to answer both participants with an conference, it will start automatically when the other person accepted the call
Example
resp.say({voice:'woman'}, 'Welcome to our hotline. This could take a moment, please wait.')
.dial({},function(err){
this.conference('example');
});

Resources