make a call from twilio subaccount(makeCall) - twilio

I am using twilio voice-quickstart-ios. Currently i am trying to make call from subaccount using twilio REST API. Here is my makeCall endpoint
exports.handler = function(context, event, callback) {
console.log("event :" + JSON.stringify(event));
var to = event.to;
var from = event.from;
let subaccountSid = event.sid;
let subaccountAuthToken = event.token;
const accountSid = context.ACCOUNT_SID;
const authToken = context.AUTH_TOKEN;
const client = require('twilio')(accountSid, authToken, {
accountSid: subaccountSid
});
client.calls
.create({
url: 'http://twimlets.com/message?Message%5B0%5D=Hello%20from%20your%20subaccount',
to: to,
from: from
}, function(err, call) {
if (err) {
console.log("err : " + err);
callback(null, JSON.stringify(err));
} else {
console.log("call Sid " + call.sid);
callback(null, call);
}
});
};
And bellow is the code to perform a voice call from my app.
func performVoiceCall(uuid: UUID, client: String?, completionHandler: #escaping (Bool) -> Swift.Void) {
guard let accessToken = fetchAccessToken() else {
completionHandler(false)
return
}
let dictionary = userdefaults.getSelectedNumbersSidAndAuth()
let sid = dictionary["sid"]!
let token = dictionary["token"]!
let from = self.dialView.selectedNumberInDropdown
let connectOptions: TVOConnectOptions = TVOConnectOptions(accessToken: accessToken) { (builder) in
builder.params = [twimlParamTo : self.outgoingValue!, "sid" : sid, "from" : from, "token": token]
builder.uuid = uuid
}
let call = TwilioVoice.connect(with: connectOptions, delegate: self)
self.activeCall = call
self.activeCalls[call.uuid.uuidString] = call
self.callKitCompletionCallback = completionHandler
}
Now the problem is none of twilio's callback function get called. (eg: if callee or caller disconnects the call, delegates are not called). If i call from master account using Twml everything works fine. but i must have to call from sub account.
How can i be able to fix this? do i need to call makeCall endpoint from performVoiceCall function? If so how can i call this? please let me know.

Related

How to retrieve variable in twilio function with Action remember?

I am creating a chat bot with Twilio Autopilot, and I am trying to retrieve a variable that I save in a function from another function with Action Remember.
This is my first function:
exports.handler = function(context, event, callback) {
console.log(Object.keys(event));
let memory = event.Memory;
let respObj = {};
console.log("Memory "+memory);
let msg = "Hi, im save the name";
respObj = {
"actions": [
{
"say": msg
},{
"remember": {
"myname": "pau"
}
}
]
};
console.log("Memory "+memory);
callback(null, respObj);
};
This is my second function:
exports.handler = function(context, event, callback) {
let responseObject = {};
let memory = JSON.parse(event.Memory);
let ret = memory.myname;
console.log(ret);
console.log("Memory "+memory);
responseObject={
"actions":[
{
"say":"your name is" + ret
}
]
};
callback(null, responseObject);
};
I get an undefined of the variable, when I call this second function.
I would appreciate if someone could help me. Thank you!!! Regards.

Can't get a Lambda function to send back information

I just got my first Lambda function written, but it does not work at this point.
I tried a number of variations in the code; partly following what I could think of and partly following what I could come across on the net; but all failed.
I want the Lambda function to listUsers in a UserPool and get an email for a given sub passed as parameter.
Here is the Swift function making the call to the Lambda function:
func getLambdaInfo() {
let lambdaInvoker = AWSLambdaInvoker.default(),
jsonObject:[String: Any] = ["sub" : "MY-USER-SUB"]
lambdaInvoker.invokeFunction("myLambdaFunc", jsonObject: jsonObject)
.continueWith(block: {
(task:AWSTask<AnyObject>) -> Any? in
if( task.error != nil) {
print("Error: \(task.error!)")
return nil
}
print("\(#function) ---- WE ARE HERE!!!!")
// Handle response in task.result:
if let JSONDictionary = task.result as? NSDictionary {
print("Result: \(JSONDictionary)")
}
return nil
})
}
Here is the Lambda function:
var AWS = require('aws-sdk/dist/aws-sdk-react-native');
exports.handler = async (event,context) => {
var params = {
UserPoolId: 'MY-POOL-ID',
AttributesToGet: ['email'],
Limit: '2'
};
var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
cognitoidentityserviceprovider.listUsers(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
// How can I get this data sent in the response is probably the issue ??
});
const response = {
inBound: event.sub,
statusCode: 200,
body: JSON.stringify('Hello from Lambda!')
};
return response;
}
Here is what can be seen in the Xcode debugging console:
getLambdaInfo() ---- WE ARE HERE!!!!
Result: {
body = "\"Hello from Lambda!\"";
inBound = "MY-USER-SUB";
statusCode = 200;
}
I hope someone with more AWSLambda than me will be able to give me some hints concerning the changes I need to make in my code to get the result (email address) I want (into my Swift getLambdaInfo()).
You need to move your return statement in the callback of listUsers:
cognitoidentityserviceprovider.listUsers(params, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
// return a 500 error ?
}
else {
console.log(data);
const response = {
inBound: event.sub,
statusCode: 200,
body: JSON.stringify(data)
}
return response;
}
});
Since you're using the async pattern you can also do:
try {
const data = await cognitoidentityserviceprovider.listUsers(params).promise() // note the await and .promise() here
const response = {
inBound: event.sub,
statusCode: 200,
body: JSON.stringify(data)
}
return response;
} catch (err) {
// do something with err
}
Otherwise your Lambda function returns before your callback gets executed (async nature of JavaScript).

Send Recorded Twilio Audio To Lex

Currently I am able to record user input, pass the recording URL to the needed function, and download the audio file locally. What I am trying to do with the audio file is either get a buffer of it to send to Lex or convert it to the format Lex needs.
Per AWS Documentation the following values are accepted for the input stream param value:
var params = {
botAlias: 'STRING_VALUE', /* required */
botName: 'STRING_VALUE', /* required */
contentType: 'STRING_VALUE', /* required */
inputStream: new Buffer('...') || 'STRING_VALUE' || streamObject, /*required */
userId: 'STRING_VALUE', /* required */
accept: 'STRING_VALUE',
requestAttributes: any /* This value will be JSON encoded on your behalf with JSON.stringify() */,
sessionAttributes: any /* This value will be JSON encoded on your behalf with JSON.stringify() */
};
lexruntime.postContent(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
Per the twilio documentation it looks like the audio file is pretty flexible...
A request to the RecordingUrl will return a recording in binary WAV audio format by default. To request the recording in MP3 format, append ".mp3" to the RecordingUrl.
What do I need to do to get the twilio recorded audio in the right format for Lex? Is it just a matter of building the correct Lex param set or do I need to do some audio conversion before hand? I am writing this application in node js if that helps and I can add more code if it will help.
I was able to figure this out by downloading the file from Twilio as a PCM and changing my parameters a bit. Also, due to the way that Twilio handles the record verb, I needed to transfer the call to a hold state while waiting for the recordingStatusCallback to POST out. I also send a text to the caller with the final status from Lex.
The code I used to download the file:
app.post('/processRecording', (request, response) => {
var https = require('https');
var fs = require('fs');
let callSID = request.body.CallSid;
let url = request.body.RecordingUrl;
var saveFile = new Promise(function(resolve, reject) {
let fileName = callSID+ ".pcm";
var file = fs.createWriteStream(fileName);
var request = https.get(url, function(response) {
response.pipe(file);
resolve();
});
});
});
const accountSid = 'YOUR ACCOUNT SID';
const authToken = 'YOUR AUTH TOKEN';
const client = require('twilio')(accountSid, authToken);
//Once the file is downloaded, I then fetch the call from the hold state using this code:
saveFile.then(function(){
client.calls(callSID)
.update({method: 'POST', url: '/updateCall'})
.then(call => console.log(call.to))
.done();
});
And my updateCall endpoint looks like this:
app.post('/updateCall', (request, response) => {
let lexruntime = new AWS.LexRuntime();
let recordedFileName = request.body.CallSid + '.pcm';
let toNumber = request.body.To;
let fromNumber = request.body.From;
let twiml = new Twilio.twiml.VoiceResponse();
let lexFileStream = fs.createReadStream(recordedFileName);
let sid = request.body.CallSid;
var params = {
botAlias: 'prod', /* required */
botName: 'OrderFlowers', /* required */
contentType: 'audio/lpcm; sample-rate=8000; sample-size-bits=16; channel-count=1; is-big-endian=false',
accept: 'text/plain; charset=utf-8',
userId: sid /* required */
};
params.inputStream = lexFileStream;
lexruntime.postContent(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
if (data.dialogState == "ElicitSlot" || data.dialogState == "ConfirmIntent" || data.dialogState == "ElicitIntent" ){
twiml.say(data.message);
twiml.redirect({
method: 'POST'
}, '/recordVoice');
response.type('text/xml');
response.send(twiml.toString());
}
else if (data.dialogState == "Fulfilled" ){
twiml.say(data.message);
response.type('text/xml');
response.send(twiml.toString());
client.messages.create({
to: toNumber,
from: fromNumber,
body: data.message
}).then(msg => {
}).catch(err => console.log(err));
}
else{
twiml.say(data.message);
response.type('text/xml');
response.send(twiml.toString());
}
});
});
The recordVoice endpoint is actually a Twilio Serverless Function But I think this is what it would look like as an express endpoint:
app.post('/recordVoice', (request, response) => {
let twiml = new Twilio.twiml.VoiceResponse();
twiml.record({
action: '/deadAir',
recordingStatusCallback: '/processRecording',
trim: true,
maxLength: 10,
finishOnKey: '*'
});
twiml.say('I did not receive a recording');
response.type('text/xml');
response.send(twiml.toString());
});
The /deadAir endpoint is also a Twilio Serverless Function but this is what it would look like:
app.post('/deadAir', (request, response) => {
let twiml = new Twilio.twiml.VoiceResponse();
twiml.pause({
length: 60
});
response.type('text/xml');
response.send(twiml.toString());
});

Twilio Functions - Blocking Spam/800#s

I am trying to block spam and 800#s in one shot using a twilio function, but it's not working...
You could modify the function as below:
exports.handler = function(context, event, callback) {
// set-up the variables that this Function will use to forward a phone call using TwiML
// REQUIRED - you must set this to the number you want calls forwarded to
let phoneNumber = event.PhoneNumber || "+16787801234";
// OPTIONAL
let callerId = event.CallerId || null;
// OPTIONAL
let timeout = event.Timeout || null;
// OPTIONAL +266696687 = ANONYMOUS Callers
let blacklistedCallers = event.blacklistedCallers || ["+14073601234","+15185001234", "+266696687"];
// generate the TwiML to tell Twilio how to forward this call
let twiml = new Twilio.twiml.VoiceResponse();
console.log(event.From.substring(0,5));
let allowedThrough = false;
if (blacklistedCallers.length > 0) {
if ((blacklistedCallers.indexOf(event.From) === -1) && (event.From.substring(0,5) != "+1800")) {
allowedThrough = true;
}
}
let dialParams = {};
if (callerId) {
dialParams.callerId = callerId;
}
if (timeout) {
dialParams.timeout = timeout;
}
if (allowedThrough) {
twiml.dial(dialParams, phoneNumber);
}
else {
twiml.reject({reason: 'rejected'});
}
// return the TwiML
callback(null, twiml);
};

Multiple login windows/keeps re-prompting for username/password on acquireToken()

Every time I make a call to acquireToken, it keeps launching the AAD login window and prompts me for a username/password, even though I've already authenticated successfully and consumed an access token to make API calls.
Here is my code
Step 1. Call the loadData function from controller
loadData = (): Rx.IPromise<Array<UserResult>> => {
var url = this.xxxApiUrl;
return Http.get<Array<UserResult>>(this._$http, url);
};
Step -2
export function get<TResult>(http: ng.IHttpService, url: string,
ignoreLoadingBar: boolean = false, retryCount = 0): Rx.IPromise<TResult> {
var req: any = {};
if (ignoreLoadingBar) {
req.ignoreLoadingBar = ignoreLoadingBar;
}
let resObservable = Rx.Observable.create(subscriber => {
acquireToken(url, (message, token) => {
req.headers.Authorization = `Bearer ${token}`;
http.get(url, req)
.then(res => {
subscriber.onNext(res.data);
subscriber.onCompleted();
}, (err) => { alert(JSON.stringify(err)); });
});
});
return resObservable.toPromise();
}
function acquireToken(apiUrl: string, callback) {
let innerCallback = (res) => callback('', res.accessToken);
let xConfig= JSON.parse(<any>sessionStorage.getItem('xConfig'));
window.AuthenticationContext = new
window.Microsoft.ADAL.AuthenticationContext
(xConfig.common.azure.authorityTenant);
window.AuthenticationContext.tokenCache.readItems().then(items => {
if (items.length > 0) {
let authority = items[0].authority;
window.AuthenticationContext = new
window.Microsoft.ADAL.AuthenticationContext(authority);
}
let resourceUri = getResourceUri(xConfig, apiUrl);
window.AuthenticationContext.acquireTokenSilentAsync(resourceUri,
xConfig.common.azure.clientId, xConfig.common.azure.redirectUri)
.then(innerCallback, (err) => {
window.AuthenticationContext.acquireTokenAsync(resourceUri,
xConfig.common.azure.clientId, xConfig.common.azure.redirectUri)
.then(innerCallback);
});
});
}
Looking at your code, it looks like that you are using acquireTokenSilentAsync using the common endpoint, this is not supported. Please make sure to use your tenant Id or name (like tenant.onmicrosoft.com) instead of common when using acquireTokenSilentAsync
For more information about the common endpoint please see here

Resources