How to list call recordings in Nexmo? - audio

I'm working on a voice assistant with Nexmo.
I use Node-RED to build the NCCO object including a record node.
In the provided tutorials by Nexmo, e.g.
Build Your Own Voicemail With Node-RED and the Nexmo Voice API
the directly download the recording to the local machine.
In my case, don't want to immediately download the audio file via node-red but let Nexmo store my audios and download them all together later through e.g. a Python script.
In the docs it says that "NOTE: After your recording is complete, it is stored by Nexmo for 30 days before being automatically deleted".
Unfortunately, I can't find any reference about where the audios are stored in my Nexmo account and how to list all recordings/recording urls of a Nexmo application.
Thank you for any help.
Nina

Currently there’s no way to get the recordings as a list from Nexmo.
What you could do instead is, capture the API response from the recording webhook and log it.
Then later on when you’re ready to download them, read it back to a get recording node.
If you connect a debug node into the /recording webhook, you can see the structure of the message object.
payload: object
start_time: "2020-03-04T13:06:40Z"
recording_url: "https://api.nexmo.com/v1/files/516f74a8-abcd-4270-b553-2582650a2e5a"
size: 26478
recording_uuid: "dbd3cb68-0a3a-4c89-bc2d-7c38abd2c497"
end_time: "2020-03-04T13:06:47Z"
conversation_uuid: "CON-93ef5eef-gg92-49ae-9e01-f3c782390dd9"
timestamp: "2020-03-04T13:06:47.733Z"
You’ll need the recording_url to download a recording, but it’s good to keep the conversation_uuid handy, as you can lookup the FROM and TO numbers based on this.
I'm leaving a possible solution with Google Sheets below, as a replacement for the /recording webhook. Import it from clipboard into your editor and see the comment node for instructions :)
[{"id":"9198a326.cfb3e","type":"tab","label":"Flow 2","disabled":false,"info":""},{"id":"49dc7444.625ff4","type":"http in","z":"9198a326.cfb3e","name":"","url":"/record","method":"post","upload":false,"swaggerDoc":"","x":210,"y":380,"wires":[["21594561.f44c1a","47021216.a25afc","dc814f00.17fa7"]]},{"id":"47021216.a25afc","type":"http response","z":"9198a326.cfb3e","name":"","statusCode":"","headers":{},"x":610,"y":380,"wires":[]},{"id":"5371302c.ee3688","type":"getrecording","z":"9198a326.cfb3e","creds":"10de89c6.d1db3e","filename":"recordings/{{msg.payload.from}}_{{msg.payload.timestamp}}.mp3","x":1260,"y":580,"wires":[["6d03a7ed.2a6d7","38dd6acf.b46cae"]]},{"id":"21594561.f44c1a","type":"debug","z":"9198a326.cfb3e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":450,"y":320,"wires":[]},{"id":"bcb08110.c2f7c","type":"e-mail","z":"9198a326.cfb3e","server":"smtp.gmail.com","port":"465","secure":true,"tls":true,"name":"","dname":"","x":1690,"y":580,"wires":[]},{"id":"6d03a7ed.2a6d7","type":"debug","z":"9198a326.cfb3e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":1490,"y":500,"wires":[]},{"id":"38dd6acf.b46cae","type":"change","z":"9198a326.cfb3e","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"'Voicemail from ' & msg.req.query.from","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1500,"y":580,"wires":[["bcb08110.c2f7c"]]},{"id":"c8fadcb1.13aca","type":"inject","z":"9198a326.cfb3e","name":"Download/Send recordings in email","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":280,"y":580,"wires":[["af254a75.791888"]]},{"id":"f9706ea5.ea66","type":"change","z":"9198a326.cfb3e","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\t \"start_time\": (msg.payload)[0],\t \"recording_url\": (msg.payload)[1],\t \"size\": (msg.payload)[2],\t \"recording_uuid\": (msg.payload)[3],\t \"end_time\": (msg.payload)[4],\t \"conversation_uuid\": (msg.payload)[5],\t \"timestamp\": (msg.payload)[6],\t \"from\": (msg.payload)[7]\t}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1060,"y":580,"wires":[["5371302c.ee3688"]]},{"id":"9f017b25.db6fe8","type":"debug","z":"9198a326.cfb3e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":970,"y":440,"wires":[]},{"id":"dc814f00.17fa7","type":"change","z":"9198a326.cfb3e","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"[payload.start_time, payload.recording_url, payload.size, payload.recording_uuid, payload.end_time, payload.conversation_uuid, payload.timestamp, req.query.from]","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":480,"y":440,"wires":[["c8118876.f668e"]]},{"id":"521189f7.2f6d8","type":"debug","z":"9198a326.cfb3e","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":830,"y":760,"wires":[]},{"id":"deba2a63.a4f4d","type":"split","z":"9198a326.cfb3e","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":830,"y":580,"wires":[["f9706ea5.ea66"]]},{"id":"c8118876.f668e","type":"GSheet","z":"9198a326.cfb3e","creds":"90e07aa9.34a6","method":"append","action":"","sheet":"1mmXhj40aeSooxmtku3ma4auLyrHhJO8xCSQsklZ1_BU","cells":"Sheet4!A1","name":"","x":730,"y":440,"wires":[["9f017b25.db6fe8"]]},{"id":"af254a75.791888","type":"GSheet","z":"9198a326.cfb3e","creds":"bd7b95fd.c3dee8","method":"get","action":"","sheet":"1mmXhj40aeSooxmtku3ma4auLyrHhJO8xCSQsklZ1_BU","cells":"Sheet4!A:H","name":"","x":610,"y":580,"wires":[["521189f7.2f6d8","deba2a63.a4f4d"]]},{"id":"335f8e96.7242ba","type":"comment","z":"9198a326.cfb3e","name":"📖 Instructions","info":"1. Install `node-red-contrib-google-sheets` package and restart Node-RED.\n2. Add _creds_, _SpreadsheetID_ and _Cells_ in the **GSheet** nodes \n3. Add your Nexmo credentials in the **Get Recording** node\n4. Configure **email** node or add download functionality","x":220,"y":180,"wires":[]},{"id":"10de89c6.d1db3e","type":"nexmovoiceapp","z":"","name":"New Voice App"},{"id":"90e07aa9.34a6","type":"gauth","z":"9198a326.cfb3e"}]

Related

Can I send Hero cards to whats app by using Twilio

I have chat bot, I build it with bot framework nodejs SDK I integrate this chat bot to whatsapp and I want to display some buttons in the whatsapp chat,
So is there any one found a solution for this problem pleas help me
Note: I try to send a hero card but I run to problem
Error: TwilioWhatsAppAdapter.parseActivity():
An activity text or attachment with contentUrl must be specified.
I don't know how bot framework attempts to send WhatsApp messages with buttons, but I doubt it fits with the way Twilio wants to send them.
Currently, to send a message with buttons, you need to register and use a template message. When you send the text of a message that matches one of your templates, then Twilio will deliver the entire template with buttons as well.
For the future, look out for the Twilio Content API which will streamline templates, buttons and other rich messages across platforms.
It looks like you are using the TwilioWhatsAppAdapter from the botbuilder-community-js repo.
Looking at the adapter's code, it doesn't appear to be configured to handle any card other than a 'signin' card (see here). If you don't mind doing a little leg work on this, I don't see why you couldn't fork this repo and build in functionality that supports a hero card similar to the signin implementation.
I imagine it would look something like this:
case 'application/vnd.microsoft.card.hero':
// eslint-disable-next-line no-case-declarations
const hero = attachment.content;
message.body = `${ hero.text }\n\n`;
message.body += (hero.buttons[0].title ? `*${ hero.buttons[0].title }*\n` : '');
message.body += hero.buttons[0].value;
break;
The above may need a little massaging but it wouldn't be far off from this. Then, you just need to build the library for use.

What is the correct way to handle changes.watch webhook requests?

I'm trying to watch for google drive changes, and write down history of changes in database, also I want to store the files (all versions).
For now I have set up webhook, so I get some info in request.header, but this info looks weird, only message number differs, resourceUri or pageToken in it are the same.
Request headers don't contain info about file or folder that was changed. No change id, no file id, no actors.
I want to know, how this method is helpful and what should I do to get real-time changes with information about files that were changed.
There are some steps to handle Drive file changes.
1. Choose the change type you want to track
It could be just changes within the file, changes in the file metadata, such as moving the file to another folder or to a shared drive. You might want to use the Reports API for that one.
2. Watch for changes
You can use the Changes API to detect what data has changed or has been removed. If you want detailed information about the changes, you can also check the Activity API
3. Get the notification
Set up a webhook when changes are detected on the file
POST https://www.googleapis.com/drive/v3/files/fileId/watch
Authorization: Bearer auth_token_for_current_user
Content-Type: application/json
{
"id": "01234567-89ab-cdef-0123456789ab", // Your channel ID.
"type": "web_hook",
"address": "https://example.com/notifications", // Your receiving URL.
...
"token": "target=myApp-myFilesChannelDest", // (Optional) Your channel token.
"expiration": 1426325213000 // (Optional) Your requested channel expiration time.
}
Sample code from https://developers.google.com/drive/api/v3/push
4. Export the new version of the file
After a new change is triggered, you can download the new file version using the export method
You can check a more detailed guide on https://developers.google.com/drive/api/v3/manage-revisions

Unable to send pdf file from bot to user in ms teams

NodeJS BotBuilder SDK version: 3.15.0
My code:
var pdf = {
name: '<file_name>.pdf',
contentType: 'application/pdf',
contentUrl: '<https url to public pdf file>'
};
var reply = new builder.Message(session).addAttachment(pdf);
session.send(reply);
This code is the same in few online examples. The issue I have is that I always get error:
Error: POST to 'https://smba.trafficmanager.net/emea/v3/conversations/a%3A1TwHmhoGuZP2Mf9P0TTnjv8HkcaXzEHryv0sYCvDDUI-qrMitJtHRlAnIcedcDH_v3IfMBXtg_zo5MDVcS0-8hDCQ4sJzpJhrewBPK8uWJXYeShgmd-s7uh5o8kW4ebAP/activities/1543588440246' failed: [400] Bad Request
For image/png this code works fine.
What I want to achieve is this: (image is taken from Bot Framework Emulator)
File from the web sent from bot to user
The file is sent from bot without uploading it to users's one drive.
This works also when I tested the feature in test section of https://dev.botframework.com/bots. It doesn't work only in ms teams.
The behaviour for sending files can differ per channel. Microsoft Teams doesn't support the direct upload method, like the WebChat / Emulator does. This is due to compliance reasons, as Bill Bliss stated.
You can post messages with card attachments referencing existing SharePoint files using the Microsoft Graph APIs for OneDrive and SharePoint. Using the Graph APIs requires obtaining access to a user's OneDrive folder (for personal and groupchat files) or the files in a team's channels (for channel files) through the standard OAuth2 authorization flow. This method works in all Teams contexts.
Have a look at Send and receive files through your bot
for the full documentation and how to implement.
An alternative option would be to use an AdaptiveCard where you can use an image thumbnail of the document combined with a button to directly download the PDF file from your public accessible URL.

Is it possible to lookup a database in a Dialogflow intent?

I'm trying to make an app using DialogFlow which finds a specific object in a specific place.
This is a generic example.
The user would say something like "Where to I find Dog in Europe" and the app would reply with "Dog can be found in Europe via: breeding, finding it out in the wild or by buying it"
considering Dog as input1 and europe as input2
Ideally the app should be able to cross reference input1 and input2 to find the correct response. Can I implement a database like structure and do this?
You can't access a database from Dialogflow directly, but you can build your own fulfillment backend that can do anything you want. It communicates with Dialogflow via HTTP requests/responses in the Dialogflow Webhook format.
Here is an example fulfillment that reads data from Firebase database - https://github.com/actions-on-google/dialogflow-updates-nodejs
You can't access a database directly in Dialog flow, but you can build your own fulfillment back end. I have been using Airtable as a database and Integromat and Webhooks to query the database and parse the results back to Dialogflow. As a novice coder I found this to be the simnplest way.
KaySubb is right, you can make a fulfillment that reads data from a firebase database(or firestore).
You can do this turning on fulfillment at the bottom page of the intent page.
First go to https://console.firebase.google.com/ (login with google account) and you should be able to see your google cloud platform project.
To use firebase, you need to first install it. Get node.js as you need npm first. I'm not sure what OS you're on but go into command line or terminal and type.
npm install firebase --save
then type:
firebase login
this will authenticate your login and connect your project when you deploy.
Then use go to the directory you want to create your project in:
firebase init functions
Select your project and select javascript, install all dependencies
Now go to functions and open the index.js file. Here you can change you write code needed in js.
Write your functions and type:
firebase deploy
in the command line open in the file directory. When it completes, it will
give you a link. This as the webhook URL in dialogflow (it should start with
https://us-central). If you see only 1 link which says
console.firebase.google.com....... then open that link on a browser, click on
"functions" on the left side of the screen and get the link from there.
This should get you started with firebase, now you can link your project to firebase fulfillment. There is great firestore explanation here
https://www.youtube.com/watch?v=kdk6MhhI8oc
But I'll give you a brief explanation:
On the top of your index.js file you will need:
const functions = require('firebase-functions');
var admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
var firestore = admin.firestore();
The basic code is here:
exports.webhook = functions.https.onRequest((request, response) => {
switch(request.body.result.action){
case 'saveData':
let params = request.body.result.parameters
firestore.collection('colName').doc('docName').add({
name:params.name
age:params.age
}).then(() => {
response.send({
speech:
`this is a response for "${params.name}".`
});
})
.catch((e => {
console.log('Error getting documents', e);
response.send({
speech:
`Sorry, something has gone wrong. Try again and if the problem persists, please report it.`
});
}))
break;
default:
}
})
I'll explain what it does:
You need the switch to decide which intent to do. request.body.result.action returns the action name (write this in dialogflow just above the parameters).
Once that is decided request.body.result.parameters give you the parameters from the intent. params.______ gives you the parameter.
I would definitely recommend reading the official documentation:
https://firebase.google.com/docs/firestore/quickstart
to help understand the data structure to help create the ideal database for you. Essentially a collection is a list and within that a doc is one entry. You can name them yourself of using the entries from param.
respond.send is what the bot will reply to the user, I've also shown how to use the parameters in the response.
.catch will just store any errors in the log, you can read the log in console.firebase.google.com.... open your project and click on function. There will be a place to read logs there. You can check any errors encountered over there.
default: will output whatever default response you wrote on dialogflow at the bottom of the intent.
Hope this helps,comment any questions. I have gone through a huge amount as concisely as I could. This will take some time to get used to and become good at, follow the docs and the youtube videos if you have a lot of trouble!
If you're having even more trouble, there is a slack that helps people that I can direct you to.

Is there any way to use our own server for storage of data generated using PUBNUB api? [duplicate]

I'm looking to develop a chat application with Pubnub where I want to make sure all the chat messages that are send is been stored in the database and also want to send messages in chat.
I found out that I can use the Parse with pubnub to provide storage options, But I'm not sure how to setup those two in a way where the messages and images send in the chat are been stored in the database.
Anyone have done this before with pubnub and parse? Are there any other easy options available to use with pubnub instead of using parse?
Sutha,
What you are seeking is not a trivial solution unless you are talking about a limited number of end users. So I wouldn't say there are no "easy" solutions, but there are solutions.
The reason is your server would need to listen (subscribe) to every chat channel that is active and store the messages being sent into your database. Imagine your app scaling to 1 million users (doesn't even need to get that big, but that number should help you realize how this can get tricky to scale where several server instances are listening to channels in a non-overlapping manner or with overlap but using a server queue implementation and de-duping messages).
That said, yes, there are PubNub customers that have implemented such a solution - Parse not being the key to making this happen, by the way.
You have three basic options for implementing this:
Implement a solution that will allow many instances of your server to subscribe to all of the channels as they become active and store the messages as they come in. There are a lot of details to making this happen so if you are not up to this then this is not likely where you want to go.
There is a way to monitor all channels that become active or inactive with PubNub Presence webhooks (enable Presence on your keys). You would use this to keep a list of all channels that your server would use to pull history (enable Storage & Playback on your keys) from in an on-demand (not completely realtime) fashion.
For every channel that goes active or inactive, your server will receive these events via the REST call (and endpoint that you implement on your server - your Parse server in this case):
channel active: record "start chat" timetoken in your Parse db
channel inactive: record "end chat" timetoken in your Parse db
the inactive event is the kickoff for a process that uses start/end timetokens that you recorded for that channel to get history from for channel from PubNub: pubnub.history({channel: channelName, start:startTT, end:endTT})
you will need to iterate on this history call until you receive < 100 messages (100 is the max number of messages you can retrieve at a time)
as you retrieve these messages you will save them to your Parse db
New Presence Webhooks have been added:
We now have webhooks for all presence events: join, leave, timeout, state-change.
Finally, you could just save each message to Parse db on success of every pubnub.publish call. I am not a Parse expert and barely know all of its capabilities but I believe they have some sort or store local then sync to cloud db option (like StackMob when that was a product), but even if not, you will save msg to Parse cloud db directly.
The code would look something like this (not complete, likely errors, figure it out or ask PubNub support for details) in your JavaScript client (on the browser).
var pubnub = PUBNUB({
publish_key : your_pub_key,
subscribe_key : your_sub_key
});
var msg = ... // get the message form your UI text box or whatever
pubnub.publish({
// this is some variable you set up when you enter a chat room
channel: chat_channel,
message: msg
callback: function(event){
// DISCLAIMER: code pulled from [Parse example][4]
// but there are some object creation details
// left out here and msg object is not
// fully fleshed out in this sample code
var ChatMessage = Parse.Object.extend("ChatMessage");
var chatMsg = new ChatMessage();
chatMsg.set("message", msg);
chatMsg.set("user", uuid);
chatMsg.set("channel", chat_channel);
chatMsg.set("timetoken", event[2]);
// this ChatMessage object can be
// whatever you want it to be
chatMsg.save();
}
error: function (error) {
// Handle error here, like retry until success, for example
console.log(JSON.stringify(error));
}
});
You might even just store the entire set of publishes (on both ends of the conversation) based on time interval, number of publishes or size of total data but be careful because either user could exit the chat and the browser without notice and you will fail to save. So the per publish save is probably best practice if a bit noisy.
I hope you find one of these techniques as a means to get started in the right direction. There are details left out so I expect you will have follow up questions.
Just some other links that might be helpful:
http://blog.parse.com/learn/building-a-killer-webrtc-video-chat-app-using-pubnub-parse/
http://www.pubnub.com/blog/realtime-collaboration-sync-parse-api-pubnub/
https://www.pubnub.com/knowledge-base/discussion/293/how-do-i-publish-a-message-from-parse
And we have a PubNub Parse SDK, too. :)

Resources