Twilio Studio & Functions - Call Whispers - twilio

I'm trying to implement a simple call whisper that lets our agent know which phone number/product has been dialed. When a call comes in, it goes through a studio flow where the caller chooses a language. The call is then routed to a taskrouter queue via the "Enqueue" widget. From what I've read in the documentation, I need to pass the callback function a instruction: 'call' parameter in order to be able to specify a URL to be "played" to the agent.
I'm currently limited to implementing everything inside of twilio itself via Studio and Functions.
Why isn't this working, and what specifically do I need to do?
Thanks!
Assignment Callback Function
exports.handler = function(context, event, callback) {
const client = context.getTwilioClient();
let eventInfo = JSON.parse(event.TaskAttributes);
// const response = new Twilio.twiml.VoiceResponse();
console.log(Object.keys(event));
console.log(JSON.parse(event.TaskAttributes));
console.log(Object.keys(JSON.parse(event.TaskAttributes)));
// console.log(JSON.parse(event.WorkerAttributes));
const worker = JSON.parse(event.WorkerAttributes);
// console.log("ReservationSid: " + event.ReservationSid);
// console.log(typeof eventInfo);
// // ATTEMPT 1
// // This works to dequeue a call to a worker, but there is no whisper functionality
// callback(null, {
// 'instruction':'dequeue',
// 'post_work_activity_sid' : 'WORKSPACEACTIVITYSID',
// 'from' : eventInfo.from
// });
// // ATTEMPT 2
// // This works to play the whisper to the agent, but the caller is never connected to the agent.
let callbackURL = 'TWILIOFUNCTIONURL';
callback(null, {
'instruction':'call',
'post_work_activity_sid' : 'WORKSPACEACTIVITYSID',
'from' : eventInfo.from,
'url' : callbackURL
});
// // ATTEMPT 3 - Building a twiml version of attempt #2
// // This doesn't work.
//
// let callbackURL = 'TWILIOFUNCTIONURL';
// let twiml = new Twilio.twiml.VoiceResponse();
// const dial = twiml.dial();
// dial.queue({
// 'instruction':'call',
// // 'post_work_activity_sid' : 'WORKSPACEACTIVITYSID',
// 'from' : eventInfo.from,
// 'accept' : true,
// 'url' : callbackURL,
// // 'reservationSid' : event.ReservationSid
// }, event.selected_language);
// console.log(dial.toString());
// console.log(twiml.toString());
// callback(null, twiml);
// // ATTEMPT 4 - Build a twiml version of attempt #1
// // This doesn't work.
//
// let twiml = new Twilio.twiml.VoiceResponse();
// twiml.dial({
// 'instruction':'dequeue',
// 'post_work_activity_sid' : 'WORKSPACEACTIVITYSID',
// 'from' : eventInfo.from
// // 'to' : 'WORKSPACEQUEUESID'
// });
// console.log(twiml.toString());
// callback(null, twiml);
};
Callback URL containing queue announcement stuff.
exports.handler = function(context, event, callback) {
let twiml = new Twilio.twiml.VoiceResponse();
// let client = context.getTwilioClient();
// console.log("BEGIN Queue Announcer - context");
// console.log(Object.keys(context));
// console.log(context);
// console.log("BEGIN Queue Announcer - event");
// console.log(Object.keys(event));
// console.log(event);
// console.log("BEGIN Queue Announcer - client");
// console.log(Object.keys(client));
// console.log(client);
// console.log("END Queue Announcer");
// Random attempt to get the call info
// console.log("BEGIN Queue Announcer - CallSid");
// console.log(event.CallSid);
// client.calls(event.CallSid)
// .fetch()
// .then(call => console.log("Call: " + call));
// console.log("END Queue Announcer - CallSid");
let client = context.getTwilioClient();
twiml.say("QUEUE NAME GOES HERE");
twiml.dial()
.queue({
// 'reservationSid' : event.ReservationSid
}, 'english');
callback(null, twiml);
};

Here's what I did that worked. This is contained within a function that is effectively the "assignment callback URL".
Steps:
Use the 'echo' twimlet to generate the twiml. Modify the supplied URL to include the 'event.RegistrationSid' where appropriate.
Accept the registration via a POST request.
Do the callback, including the 'instruction : call' parameter.
exports.handler = function(context, event, callback) {
const client = context.getTwilioClient();
let eventInfo = JSON.parse(event.TaskAttributes);
// Generate the callback url
let callbackURL = `http://twimlets.com/echo?Twiml=%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%0A%3CResponse%3E%0A%3CSay%3ECALLWHISPERGOESHERE%3C%2FSay%3E%0A%3CDial%3E%0A%3CQueue%20reservationSid%3D%22${event.ReservationSid}%22%3E%3C%2FQueue%3E%0A%3C%2FDial%3E%0A%3C%2FResponse%3E&`;
// Generate POST request to accept the reservation
const rp = require("request-promise");
const postURI = `https://taskrouter.twilio.com/v1/Workspaces/${event.WorkspaceSid}/Tasks/${event.TaskSid}/Reservations/${event.ReservationSid}`;
const postForm = {
ReservationStatus: "accepted"
};
const postAuth = {
user: context.ACCOUNT_SID,
pass: context.AUTH_TOKEN
};
const options = {
method: "POST",
uri: postURI,
auth: postAuth,
form: postForm
};
rp(options)
.then(body => {
console.log(body);
callback(null, null);
})
.catch(err => {
console.log(err);
callback(null, err);
});
// Call the agent
callback(null, {
instruction: "call",
post_work_activity_sid: "POSTWORKACTIVITYSID",
from: eventInfo.from,
url: callbackURL
});
};

Related

Twilio Whatsapp: Send Location not working

I'm using the FUNCTION Widget in Twilio Studio.
I need to send location inside a message in whatsapp.
I've tried this peice of code inside a function in Twilio:
// This is your new function. To start, set the name and path on the left.
exports.handler = function(context, event, callback) {
// The pre-initialized Twilio Client is available from the `context` object
const twilioClient = context.getTwilioClient();
// Determine message details from the incoming event, with fallback values
const from = event.From || 'whatsapp:+14155238886';
const to = event.To || 'whatsapp:+556181584246';
const body = event.Body || 'Ahoy, World!';
const persistentAction = 'geo:-1.232453, 36.878987';
twilioClient.messages
.create({to, body, from, persistentAction})
.then((result) => {
console.log('Created message using callback');
console.log(result);
console.log(result.sid);
return callback();
})
.catch((error) => {
console.error(error);
return callback(error);
});
};

Broadcasting to Members of a Database Using Twilio Messaging Services

fine people of Stack Overflow.
I'm trying to solve a problem I'm having involving twilio functions, messaging services, and databases.
What I'm attempting to do is send a message to all members of a database at once.
My code is a mess, as Javascript isn't my native language and I'm rather new to twilio.
The problem I believe I'm having is with the async/await feature of javascript.
Here is my code so far:
// Boiler Plate Deta Code
const { Deta } = require("deta");
// Function to access database and get object of conta
async function FetchDB(db) {
let res = await db.fetch();
allItems = res.items;
// continue fetching until last is not seen
while (res.last){
res = await db.fetch({}, {last: res.last});
allItems = allItems.concat(res.items);
}
}
// Function to get total number of contacts.
async function ReturnNumberOfContacts(allItems) {
number_of_contacts = allItems.length;
}
// Function to send message to contact in database.
async function SendMessages(allItems, message) {
allItems.forEach(contact => {
let users_name = contact.name
client.messages
.create({
body: `Hey ${users_name}! ${message}`,
messagingServiceSid: messaging_service,
to: contact.key
})
});
}
// Function to submit response to broadcaster.
async function SuccessResponse(user_name, number_of_contacts) {
responseObject = {
"actions": [
{
"say": `${user_name}, your broadcast has successfully sent to ${number_of_contacts} contacts.`
},
{
"listen": true
}
]
}
}
// Main Function
exports.handler = async function(context, event, callback) {
// Placeholder for number of contacts
let number_of_contacts;
// Place holder for object from database of all contacts
let allItems;
// Placeholder for users message
let message;
// Placeholder for response to user
let responseObject;
//Twilio and Deta, Etc Const
const client = require('twilio')(context.ACCOUNT_SID, context.AUTH_TOKEN);
const deta = Deta(context.DETA_PROJECT_KEY);
const db = deta.Base("users2");
const messaging_service = context.MESSAGING_SERVICE;
// From Phone Number
const from = event.UserIdentifier;
// Parse memory
const memory = JSON.parse(event.Memory);
// Fetch all items from database and return total number of contacts.
// Update relavent variables
await FetchDB(db, allItems).then(ReturnNumberOfContacts(allItems));
// Figure out if message came from short circuit broadcast or normal
if (memory.triggered) {
message = memory.message;
} else {
message = memory.twilio.collected_data.broadcast_message.answers.message_input.answer;
}
// Check if verified and set name.
const current_user = await db.get(from);
// Get the current users name or set a default value
let user_name = current_user.name || "friend";
// Determine if user is an authorized broadcaster
if (from === context.BROADCAST_NUMBER) {
// Decide if the sending of a message should be cancelled.
if (message.toLowerCase() === "c" || message.toLowerCase() === "cancel") {
responseObject = {
"actions": [
{
"say": `${user_name}, you have canceled your request and no messages have been sent.`
},
{
"listen": false
}
]
}
// Return Callback and end task
callback(null, responseObject);
}
// Move forward with sending a message.
else {
// Send message to users in database and send success message to broadcaster.
await SendMessages(message, client, messaging_service)
.then(SuccessResponse(user_name, number_of_contacts))
return callback(null, responseObject);
}
// The user is not authorized so return this.
}
return callback(null, {
"actions": [
{
"say": "You are not authorized to broadcast."
},
{
"listen": false
}
]
})
};
So when the Fetch() function is triggered, I want the database to load a list of everyone and have twilio send them the desired message saved in the message variable. I have the code working so that I can read from the database and get the proper values, and send a single text message with the desired message, but the problem I'm having now is integrating it all together.
Thanks if anyone can point me in the right direction here.
Again, I'm new to javascript and more specifically asynchronous programming.
Twilio developer evangelist here.
The issue from the error says that allItems is undefined when you call ReturnNumberOfContacts.
I think the issue comes from trying to use allItems as a sort of global variable, same for number_of_contacts. It would be better for FetchDB to resolve with the list of items and ReturnNumberOfContacts to resolve with the number of the items.
You also have some arguments missing when you call SendMessages in your function. I've updated it to the point that I think it will work:
// Boiler Plate Deta Code
const { Deta } = require("deta");
// Function to access database and get object of conta
async function FetchDB(db) {
let res = await db.fetch();
let allItems = res.items;
// continue fetching until last is not seen
while (res.last) {
res = await db.fetch({}, { last: res.last });
allItems = allItems.concat(res.items);
}
return allItems;
}
// Function to send message to contact in database.
async function SendMessages(allItems, message, client, messagingService) {
return Promise.all(
allItems.map((contact) => {
let usersName = contact.name;
return client.messages.create({
body: `Hey ${usersName}! ${message}`,
messagingServiceSid: messagingService,
to: contact.key,
});
})
);
}
// Main Function
exports.handler = async function (context, event, callback) {
// Placeholder for users message
let message;
//Twilio and Deta, Etc Const
const client = require("twilio")(context.ACCOUNT_SID, context.AUTH_TOKEN);
const deta = Deta(context.DETA_PROJECT_KEY);
const db = deta.Base("users2");
const messagingService = context.MESSAGING_SERVICE;
// From Phone Number
const from = event.UserIdentifier;
// Parse memory
const memory = JSON.parse(event.Memory);
// Fetch all items from database and return total number of contacts.
// Update relavent variables
const allItems = await FetchDB(db);
const numberOfContacts = allItems.length;
// Figure out if message came from short circuit broadcast or normal
if (memory.triggered) {
message = memory.message;
} else {
message =
memory.twilio.collected_data.broadcast_message.answers.message_input
.answer;
}
// Check if verified and set name.
const currentUser = await db.get(from);
// Get the current users name or set a default value
let userName = currentUser.name || "friend";
// Determine if user is an authorized broadcaster
if (from === context.BROADCAST_NUMBER) {
// Decide if the sending of a message should be cancelled.
if (message.toLowerCase() === "c" || message.toLowerCase() === "cancel") {
// Return Callback and end task
callback(null, {
actions: [
{
say: `${userName}, you have canceled your request and no messages have been sent.`,
},
{
listen: false,
},
],
});
}
// Move forward with sending a message.
else {
// Send message to users in database and send success message to broadcaster.
await SendMessages(allItems, message, client, messagingService);
return callback(null, {
actions: [
{
say: `${userName}, your broadcast has successfully sent to ${numberOfContacts} contacts.`,
},
{
listen: true,
},
],
});
}
// The user is not authorized so return this.
}
return callback(null, {
actions: [
{
say: "You are not authorized to broadcast.",
},
{
listen: false,
},
],
});
};
What I did here was change FetchDB to only take the db as an argument, then to create a local allItems variable that collects all the contacts and then returns them.
async function FetchDB(db) {
let res = await db.fetch();
let allItems = res.items;
// continue fetching until last is not seen
while (res.last) {
res = await db.fetch({}, { last: res.last });
allItems = allItems.concat(res.items);
}
return allItems;
}
This is then called in the main body of the function to assign a local variable. I also replaced the ReturnNumberOfContacts function with a simple assignment.
const allItems = await FetchDB(db);
const numberOfContacts = allItems.length;
One thing you may want to consider is how many contacts you are trying to send messages to during this function. There are a few limits you need to be aware of.
Firstly, Function execution time is limited to 10 seconds so you need to make sure you can load and send all your messages within that amount of time if you want to use a Twilio Function for this.
Also, there are limits for the number of concurrent connections you can make to the Twilio API. That limit used to be 100 connections per account, but it may vary these days. When sending asynchronous API requests as you do in JavaScript, the platform will attempt to create as many connections to the API that it can in order to trigger all the requests asynchronously. If you have more than 100 contacts you are trying to send messages to here, that will quickly exhaust your available concurrent connections and you will receive 429 errors. You may choose to use a queue, like p-queue, to ensure your concurrent connections never get too high. The issue in this case is that it will then take longer to process the queue which brings me back to the original limit of 10 seconds of function execution.
So, I think the above code may work in theory now, but using it in practice may have other issues that you will need to consider.

Why aren't my messages sending using Twilio Messaging Service and Twilio Functions? [duplicate]

fine people of Stack Overflow.
I'm trying to solve a problem I'm having involving twilio functions, messaging services, and databases.
What I'm attempting to do is send a message to all members of a database at once.
My code is a mess, as Javascript isn't my native language and I'm rather new to twilio.
The problem I believe I'm having is with the async/await feature of javascript.
Here is my code so far:
// Boiler Plate Deta Code
const { Deta } = require("deta");
// Function to access database and get object of conta
async function FetchDB(db) {
let res = await db.fetch();
allItems = res.items;
// continue fetching until last is not seen
while (res.last){
res = await db.fetch({}, {last: res.last});
allItems = allItems.concat(res.items);
}
}
// Function to get total number of contacts.
async function ReturnNumberOfContacts(allItems) {
number_of_contacts = allItems.length;
}
// Function to send message to contact in database.
async function SendMessages(allItems, message) {
allItems.forEach(contact => {
let users_name = contact.name
client.messages
.create({
body: `Hey ${users_name}! ${message}`,
messagingServiceSid: messaging_service,
to: contact.key
})
});
}
// Function to submit response to broadcaster.
async function SuccessResponse(user_name, number_of_contacts) {
responseObject = {
"actions": [
{
"say": `${user_name}, your broadcast has successfully sent to ${number_of_contacts} contacts.`
},
{
"listen": true
}
]
}
}
// Main Function
exports.handler = async function(context, event, callback) {
// Placeholder for number of contacts
let number_of_contacts;
// Place holder for object from database of all contacts
let allItems;
// Placeholder for users message
let message;
// Placeholder for response to user
let responseObject;
//Twilio and Deta, Etc Const
const client = require('twilio')(context.ACCOUNT_SID, context.AUTH_TOKEN);
const deta = Deta(context.DETA_PROJECT_KEY);
const db = deta.Base("users2");
const messaging_service = context.MESSAGING_SERVICE;
// From Phone Number
const from = event.UserIdentifier;
// Parse memory
const memory = JSON.parse(event.Memory);
// Fetch all items from database and return total number of contacts.
// Update relavent variables
await FetchDB(db, allItems).then(ReturnNumberOfContacts(allItems));
// Figure out if message came from short circuit broadcast or normal
if (memory.triggered) {
message = memory.message;
} else {
message = memory.twilio.collected_data.broadcast_message.answers.message_input.answer;
}
// Check if verified and set name.
const current_user = await db.get(from);
// Get the current users name or set a default value
let user_name = current_user.name || "friend";
// Determine if user is an authorized broadcaster
if (from === context.BROADCAST_NUMBER) {
// Decide if the sending of a message should be cancelled.
if (message.toLowerCase() === "c" || message.toLowerCase() === "cancel") {
responseObject = {
"actions": [
{
"say": `${user_name}, you have canceled your request and no messages have been sent.`
},
{
"listen": false
}
]
}
// Return Callback and end task
callback(null, responseObject);
}
// Move forward with sending a message.
else {
// Send message to users in database and send success message to broadcaster.
await SendMessages(message, client, messaging_service)
.then(SuccessResponse(user_name, number_of_contacts))
return callback(null, responseObject);
}
// The user is not authorized so return this.
}
return callback(null, {
"actions": [
{
"say": "You are not authorized to broadcast."
},
{
"listen": false
}
]
})
};
So when the Fetch() function is triggered, I want the database to load a list of everyone and have twilio send them the desired message saved in the message variable. I have the code working so that I can read from the database and get the proper values, and send a single text message with the desired message, but the problem I'm having now is integrating it all together.
Thanks if anyone can point me in the right direction here.
Again, I'm new to javascript and more specifically asynchronous programming.
Twilio developer evangelist here.
The issue from the error says that allItems is undefined when you call ReturnNumberOfContacts.
I think the issue comes from trying to use allItems as a sort of global variable, same for number_of_contacts. It would be better for FetchDB to resolve with the list of items and ReturnNumberOfContacts to resolve with the number of the items.
You also have some arguments missing when you call SendMessages in your function. I've updated it to the point that I think it will work:
// Boiler Plate Deta Code
const { Deta } = require("deta");
// Function to access database and get object of conta
async function FetchDB(db) {
let res = await db.fetch();
let allItems = res.items;
// continue fetching until last is not seen
while (res.last) {
res = await db.fetch({}, { last: res.last });
allItems = allItems.concat(res.items);
}
return allItems;
}
// Function to send message to contact in database.
async function SendMessages(allItems, message, client, messagingService) {
return Promise.all(
allItems.map((contact) => {
let usersName = contact.name;
return client.messages.create({
body: `Hey ${usersName}! ${message}`,
messagingServiceSid: messagingService,
to: contact.key,
});
})
);
}
// Main Function
exports.handler = async function (context, event, callback) {
// Placeholder for users message
let message;
//Twilio and Deta, Etc Const
const client = require("twilio")(context.ACCOUNT_SID, context.AUTH_TOKEN);
const deta = Deta(context.DETA_PROJECT_KEY);
const db = deta.Base("users2");
const messagingService = context.MESSAGING_SERVICE;
// From Phone Number
const from = event.UserIdentifier;
// Parse memory
const memory = JSON.parse(event.Memory);
// Fetch all items from database and return total number of contacts.
// Update relavent variables
const allItems = await FetchDB(db);
const numberOfContacts = allItems.length;
// Figure out if message came from short circuit broadcast or normal
if (memory.triggered) {
message = memory.message;
} else {
message =
memory.twilio.collected_data.broadcast_message.answers.message_input
.answer;
}
// Check if verified and set name.
const currentUser = await db.get(from);
// Get the current users name or set a default value
let userName = currentUser.name || "friend";
// Determine if user is an authorized broadcaster
if (from === context.BROADCAST_NUMBER) {
// Decide if the sending of a message should be cancelled.
if (message.toLowerCase() === "c" || message.toLowerCase() === "cancel") {
// Return Callback and end task
callback(null, {
actions: [
{
say: `${userName}, you have canceled your request and no messages have been sent.`,
},
{
listen: false,
},
],
});
}
// Move forward with sending a message.
else {
// Send message to users in database and send success message to broadcaster.
await SendMessages(allItems, message, client, messagingService);
return callback(null, {
actions: [
{
say: `${userName}, your broadcast has successfully sent to ${numberOfContacts} contacts.`,
},
{
listen: true,
},
],
});
}
// The user is not authorized so return this.
}
return callback(null, {
actions: [
{
say: "You are not authorized to broadcast.",
},
{
listen: false,
},
],
});
};
What I did here was change FetchDB to only take the db as an argument, then to create a local allItems variable that collects all the contacts and then returns them.
async function FetchDB(db) {
let res = await db.fetch();
let allItems = res.items;
// continue fetching until last is not seen
while (res.last) {
res = await db.fetch({}, { last: res.last });
allItems = allItems.concat(res.items);
}
return allItems;
}
This is then called in the main body of the function to assign a local variable. I also replaced the ReturnNumberOfContacts function with a simple assignment.
const allItems = await FetchDB(db);
const numberOfContacts = allItems.length;
One thing you may want to consider is how many contacts you are trying to send messages to during this function. There are a few limits you need to be aware of.
Firstly, Function execution time is limited to 10 seconds so you need to make sure you can load and send all your messages within that amount of time if you want to use a Twilio Function for this.
Also, there are limits for the number of concurrent connections you can make to the Twilio API. That limit used to be 100 connections per account, but it may vary these days. When sending asynchronous API requests as you do in JavaScript, the platform will attempt to create as many connections to the API that it can in order to trigger all the requests asynchronously. If you have more than 100 contacts you are trying to send messages to here, that will quickly exhaust your available concurrent connections and you will receive 429 errors. You may choose to use a queue, like p-queue, to ensure your concurrent connections never get too high. The issue in this case is that it will then take longer to process the queue which brings me back to the original limit of 10 seconds of function execution.
So, I think the above code may work in theory now, but using it in practice may have other issues that you will need to consider.

Twilio Voice Recording

I am using the twilio JS client to make call from the web. the client call the backend to get the token. Here the backend code that returns the token. How to record the call. mean where to set the recording url. The call is successfull. But don't know where to pass the recording url.
public function newToken(Request $request)
{
$accountSid = config('services.twilio')['accountSid'];
$applicationSid = config('services.twilio')['applicationSid'];
$apiKey = config('services.twilio')['apiKey'];
$apiSecret = config('services.twilio')['apiSecret'];
$voiceGrant = new VoiceGrant();
$voiceGrant->setOutgoingApplicationSid($applicationSid);
$voiceGrant->setIncomingAllow(true);
$this->accessToken->addGrant($voiceGrant);
$token = $this->accessToken->toJWT();
return response()->json(['token' => $token]);
}
On JS side the code which uses the client side library of twillio.
const Device = Twilio.Device;
// Store some selectors for elements we'll reuse
var callStatus = $("#call-status");
var answerButton = $(".answer-button");
var callSupportButton = $(".call-support-button");
var hangUpButton = $(".hangup-button");
var callCustomerButtons = $(".call-customer-button");
var device = null;
function updateCallStatus(status) {
callStatus.attr('placeholder', status);
}
/* Get a Twilio Client token with an AJAX request */
$(document).ready(function() {
setupClient();
});
function setupHandlers(device) {
device.on('ready', function(_device) {
updateCallStatus("Ready");
});
/* Report any errors to the call status display */
device.on('error', function(error) {
updateCallStatus("ERROR: " + error.message);
});
/* Callback for when Twilio Client initiates a new connection */
device.on('connect', function(connection) {
// Enable the hang up button and disable the call buttons
hangUpButton.prop("disabled", false);
callCustomerButtons.prop("disabled", true);
callSupportButton.prop("disabled", true);
answerButton.prop("disabled", true);
// If phoneNumber is part of the connection, this is a call from a
// support agent to a customer's phone
if ("phoneNumber" in connection.message) {
updateCallStatus("In call with " + connection.message.phoneNumber);
} else {
// This is a call from a website user to a support agent
updateCallStatus("In call with support");
}
});
/* Callback for when a call ends */
device.on('disconnect', function(connection) {
// Disable the hangup button and enable the call buttons
hangUpButton.prop("disabled", true);
callCustomerButtons.prop("disabled", false);
callSupportButton.prop("disabled", false);
updateCallStatus("Ready");
});
/* Callback for when Twilio Client receives a new incoming call */
device.on('incoming', function(connection) {
updateCallStatus("Incoming support call");
// Set a callback to be executed when the connection is accepted
connection.accept(function() {
updateCallStatus("In call with customer");
});
// Set a callback on the answer button and enable it
answerButton.click(function() {
connection.accept();
});
answerButton.prop("disabled", false);
});
};
function setupClient() {
$.post("/token", {
forPage: window.location.pathname,
_token: $('meta[name="csrf-token"]').attr('content')
}).done(function(data) {
// Set up the Twilio Client device with the token
device = new Device();
device.setup(data.token);
setupHandlers(device);
}).fail(function() {
updateCallStatus("Could not get a token from server!");
});
};
/* Call a customer from a support ticket */
window.callCustomer = function(phoneNumber) {
updateCallStatus("Calling " + phoneNumber + "...");
var params = { "phoneNumber": phoneNumber };
device.connect(params);
};
/* Call the support_agent from the home page */
window.callSupport = function() {
updateCallStatus("Calling support...");
// Our backend will assume that no params means a call to support_agent
device.connect();
};
/* End a call */
window.hangUp = function() {
device.disconnectAll();
};
I'll assume that the TwiML generated by the URL you have set on your TwiML Application includes the verb in order to connect the incoming Client call to something like a PSTN number.
If that assumption is correct then you can include the record attribute on that verb:
<Response>
<Dial record="record-from-ringing-dual"
recordingStatusCallback="https://myapp.com/recording-events"
recordingStatusCallbackEvent="in-progress completed absent">
<Number>+15558675310</Number>
</Dial>
</Response>
Use record-from-answer-dual or record-from-ringing-dual record values for dual-channel recordings, with the parent and child calls in different stereo tracks.
See the support article Recording a Phone Call with Twilio for more details.

Twilio Studio Not Listing Services

I am setting up a Sync Application using Twilio's Sync Library. For some reason, none of the REST API methods seem to work. That is, I cannot get any of the sync methods to console.log() anything via the runtime functions.
I can, however, console.log() plain text.
Here is my code:
exports.handler = function(context, event, callback) {
// 0. Init
// const phoneNumber = event.phoneNumber;
const issueLimit = 3; // CHANGE IF NEEDED
const listName = 'issues';
const twilioClient = context.getTwilioClient();
// 1. List all lists
twilioClient.sync.services(context.SYNC_SERVICE_SID)
.syncLists
.list({limit: 20})
.then(syncLists => syncLists.forEach(s => console.log(s.sid)));
// 2. return true if quota reached
console.log("Got to here");
// 3. return false
callback(null, undefined);
};
The only code that appears to execute is the 'console.log("Got to here");'. I'm also not receiving any error messages.
Any guidance is sincerely appreciated.
When you see .then(), that's a promise, and you can read more about this here https://www.twilio.com/blog/2016/10/guide-to-javascript-promises.html
In other words, the JavaScript engine goes to your steps 2. and then 3. without waiting for 1. to finish. And since you're returning at step 3 with callback(null, undefined); you won't see the logs.
So, you'll have to move callback() inside the .then(), something like this:
exports.handler = function (context, event, callback) {
// 0. Init
// const phoneNumber = event.phoneNumber;
const issueLimit = 3; // CHANGE IF NEEDED
const listName = 'issues';
const twilioClient = context.getTwilioClient();
// 1. List all lists
twilioClient.sync.services(context.SYNC_SERVICE_SID)
.syncLists
.list({ limit: 20 })
.then(
function (syncLists) {
console.log("Got to here");
syncLists.forEach(s => console.log(s.sid));
callback(null, undefined);
}
);
};

Resources