Send Recorded Twilio Audio To Lex - twilio

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());
});

Related

how to display a file using react-native

So, I have those "cards" to which are attached files.
I want to be able to display the content of these files (when possible; I do not expect to show binary files obviously, but text, pdf, images,...) to the user.
Upon a longPress on an attachment, the openAttachment() function is be called. That function downloads the file from the server if necessary and then (tries to) open it:
// Opens an attachment
const openAttachment = async (attachment) => {
try {
// Download file if not already done
const fileInfo = await FileSystem.getInfoAsync(FileSystem.cacheDirectory + attachment.name)
let uri
if (!fileInfo.exists) {
console.log('Downloading attachment')
resp = await FileSystem.downloadAsync(
server.value + `/index.php/apps/deck/api/v1.0/boards/${route.params.boardId}/stacks/${route.params.stackId}/cards/${route.params.cardId}/attachments/${attachment.id}`,
FileSystem.cacheDirectory + attachment.name,
{
headers: {
'Authorization': token.value
},
},
)
console.log(resp)
uri = await FileSystem.getContentUriAsync(resp.uri)
} else {
console.log('File already in cache')
uri = await FileSystem.getContentUriAsync(fileInfo.uri)
}
console.log('Opening file ' + uri)
Sharing.shareAsync(uri);
} catch {
Toast.show({
type: 'error',
text1: i18n.t('error'),
text2: error.message,
})
console.log(error)
}
}
The issue always arrise at the Sharing.shareAsync(uri); line: Whatever I put there, it fails:
Sharing.shareAsync(uri) does not seem to be supported on my platform: https://docs.expo.dev/versions/latest/sdk/sharing/
Linking.openURL(uri) does not support the file:// scheme (the uri is in the form file:///var/mobile/Containers/Data/Application/5C1CB402-5ED1-4E17-B907-46111AE3FB7C/Library/Caches/test.pdf)
await WebBrowser.openBrowserAsync(uri) (from expo-web-browser) does not seem to be able to open local files
How am I supposed to do to display those files? Anyone has an idea?
Cyrille
I found a solution using react-native-file-viewer
// Opens an attachment
const openAttachment = async (attachment) => {
try {
// Download file if not already done
const fileInfo = await FileSystem.getInfoAsync(FileSystem.cacheDirectory + "attachment.name")
let uri
if (!fileInfo.exists) {
console.log('Downloading attachment')
const resp = await FileSystem.downloadAsync(
server.value + `/index.php/apps/deck/api/v1.0/boards/${route.params.boardId}/stacks/${route.params.stackId}/cards/${route.params.cardId}/attachments/${attachment.id}`,
FileSystem.cacheDirectory + attachment.name,
{
headers: {
'Authorization': token.value
},
},
)
console.log(resp)
uri = await FileSystem.getContentUriAsync(resp.uri)
} else {
console.log('File already in cache')
uri = await FileSystem.getContentUriAsync(fileInfo.uri)
}
console.log('opening file', uri)
FileViewer.open(uri)
} catch(error) {
Toast.show({
type: 'error',
text1: i18n.t('error'),
text2: error.message,
})
console.log(error)
}
}

Can't send readable stream with Pinata SDK to IPFS - 400 bad request and Unhandled rejection

Due to the nature of my project I have a image dataURL (NOT an actual image file) that I am trying to upload to IPFS via Pinata SDK. I have converted the image dataURL into a buffer(array) and tried 2 different methods but none of them works. Here is my code:
SAMPLE 1
var myBlob = new Blob([new Uint8Array(myBuffer)]);
var myReadableStream = myBlob.stream()
pinata.pinFileToIPFS(myReadableStream)
ERROR: Unhandled Rejection (TypeError): source.on is not a function
SAMPLE 2
var myBlob = new Blob([new Uint8Array(myBuffer)]);
var myHeaders = new Headers();
myHeaders.append("pinata_api_key", "MY_KEY");
myHeaders.append("pinata_secret_api_key", "MY_SECRET_KEY");
var formdata = new FormData();
formdata.append("test", myBlob);
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: formdata,
redirect: 'follow'
};
fetch("https://api.pinata.cloud/pinning/pinFileToIPFS", requestOptions)
.then(response => response.text())
.then(result => console.log('result',result))
.catch(error => console.log('error', error));
ERROR: 400 Bad Request, result {"error":"Unexpected field"}
with buffers things can be a little tricky. You'll need to format your request in a slightly different way.
I would take a look at this code snippet for an example of how somebody got this to work:
const pinataSDK = require("#pinata/sdk");
const pinata = pinataSDK(
"Pinata API Key",
"Pinata API Secret"
);
const { fs, vol } = require("memfs");
(async () => {
try {
const base64 = "base64 file string";
const buf = Buffer.from(base64, "base64");
memfs.writeFileSync("File Name", buf);
const read = vol.createReadStream("File Name");
const res = await pinata.pinFileToIPFS(read);
console.log(res);
} catch (error) {
console.log(error);
}
})();

Is there any same method in swift to read InpuStream using post http request

Is there any same method present in swift to read the input stream from HTTP request
InputStream in = address.openStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder result = new StringBuilder();
String line;
while((line = reader.readLine()) != null) {
result.append(line);
}
System.out.println(result.toString());
This is a local server is sending two response with 207:
var http = require('http');
var express = require('express')();
var port = process.env.PORT || 3000;
var promise = new Promise(function(resolve, reject) {
const x = "geeksforgeeks";
const y = "geeksforgeeks"
if(x === y) {
console.log('resolve');
resolve();
} else {
reject();
}
});
express.post('/', function(req, res) {
console.log('send req1')
// check if network exists and user requesting is owner of it
return promise.then(() => {
// add listener to receive response from gateway and forward it
//_addGwEmitter.addGwEmitter.addEventListener(req, res, gatewayPsn);
// send the gateway trigger instructions to coco user
res.status(207).write(JSON.stringify({
status: 200,
msg: "Waiting for authorization\n",
instructionText: "devProductInfo.instructionText",
instructionImage: "devProductInfo.instructionImageURL"
}) + "\n \r End" );
// if no event is received from gateway trigger timeout after 60 seconds
res.setTimeout(6000,()=>{
console.log('send req 2');
res.status(207).write(JSON.stringify({
status: 200,
msg: "authorization done \n",
instructionText: "devProductInfo.instructionText",
instructionImage: "devProductInfo.instructionImageURL"
}));
res.end();
});
}).catch(error => {
return res.status(400).send("error.getErrorInfo()");
});
});
http.createServer(express).listen(port);
i want to read two response one by one
i have tried
uploadtask
downloadTask
dataTask
in HTTP URLSession.
I got the answer if you want to use 207 response in iOS devices then implement URL data Task with delegate and in data delegate, you will get the response data. make sure response content type is text/json

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

get programmatically notified when processing is over

When uploading video to YouTube, the resulting video link becomes immediately available, but the video is not yet ready to be played, because Youtube is processing it. How to get programmatically notified upon the completion of the processing? I am using node.js googleapis.
/youtube/videos/list provides the API to query the video upload status.
var videoId = 'aBcdEfjhkLm'
const Youtube = require("youtube-api"), fs = require("fs"),
readJson = require("r-json"), Lien = require("lien"),
Logger = require("bug-killer"), opn = require("opn"),
prettyBytes = require("pretty-bytes");
const CREDENTIALS = readJson(`${__dirname}/credentials.json`);
let server = new Lien({ host: "localhost" , port: 5000 });
let oauth = Youtube.authenticate({
type: "oauth", client_id: CREDENTIALS.web.client_id,
client_secret: CREDENTIALS.web.client_secret,
redirect_url: CREDENTIALS.web.redirect_uris[0]
});
opn(oauth.generateAuthUrl({
access_type: "offline", scope: ["https://www.googleapis.com/auth/youtube"]
}));
server.addPage("/oauth2callback", lien => {
Logger.log("code: " + lien.query.code);
oauth.getToken(lien.query.code, (err, tokens) => {
if (err) { lien.lien(err, 400); return Logger.log(err); }
Logger.log("Got the tokens.");
oauth.setCredentials(tokens);
lien.end(
'<script>window.close()</script>'
);
var req = Youtube.videos.list({
id: 'fEJc2CwddgU',
part: "status",
},
(err, data) => {
if (err) {
console.log('err:', err)
process.exit()
}
var s = data.items[0].status.uploadStatus
if (s == 'uploaded') console.log('Video is being processed.')
if (s == 'processed') console.log('Video processing completed.')
process.exit()
});
});
});

Resources