How to know if the bot is using voice or text in Lex - aws-lex

In our Lambda function that gets called from Lex, we need to know if the requests is coming from Connect or from a text source like the console or another chat tool.
We mainly need to know this to decide if we need to respond with plain text or SSML.

You need to look into the request attribute x-amz-lex:accept-content-types. For example in a Node.js function you can do that like this:
function canUseSSML(event) {
if (event.requestAttributes) {
if(event.requestAttributes['x-amz-lex:accept-content-types'] && event.requestAttributes['x-amz-lex:accept-content-types'].indexOf('SSML') != -1) {
return true;
}
}
return false;
}

Related

How to send welcome message AND load a specific dialog automatically in Microsoft Bot Framework v.3 (Node.js)?

I'm trying both to show a welcome message when my bot starts up and also load a specific dialog. We are using version 3 in the company where I'm working (I know, it's old and not supported).
As far as the welcome message, https://learn.microsoft.com/en-us/azure/bot-service/nodejs/bot-builder-nodejs-handle-conversation-events?view=azure-bot-service-3.0 says to use on conversationUpdate, which works fine, but this seems to be contradicted by https://blog.botframework.com/2018/07/12/how-to-properly-send-a-greeting-message-and-common-issues-from-customers/, which suggests one should not use conversationUpdate, except when using DirectLine, but instead send an event. Is this the final word on the matter? Is there a better way?
I'd also like to load a dialog automatically after the welcome message. How do I do this? Can I access the session during the 'on conversationUpdate' event above and load the dialog directly there? Is there a better way?
Thanks for any help!
It is contradictory, but conversationUpdate is likely your best bet in most situations. However, because channels handle this differently, you should be aware that the result can vary. For direct line, it is a better option to utilize sending events.
An example, in case of need:
bot.on('conversationUpdate', function(message) {
if (message.membersAdded) {
message.membersAdded.forEach(function(identity) {
if (identity.id === message.address.bot.id) {
var reply = new builder.Message()
.address(message.address)
.text("Welcome");
bot.send(reply);
}
});
}
});
For immediately calling a specific dialog, do this:
bot.on('conversationUpdate', function (message) {
if (message.membersAdded) {
message.membersAdded.forEach(function (identity) {
if (identity.id === message.address.bot.id) {
bot.beginDialog(message.address, '/main');
}
});
}
});
bot.dialog('/main', [
function (session, args, next) {
session.send("Glad you could join.");
session.beginDialog('/next');
}
]);
Simply combine the two for sending the welcome message and starting up a dialog.
Hope of help!

dialogflow fullfilment and firebase response time

I am trying to build a simple chatbot with DialogFlow.
My aim is to give information from user question, like : where can I slackline above water in croatia ? I have two parameters (croatia, waterline) and a list of slackline places.
So I need a data base to retrieve information from parameters. DialogFlow allows fulfillment with Firebase. I build a database with places (name, country, type of slack) and enable webhook call for my intent.
I use Inline Editor and index.js
const parameters = request.body.queryResult.parameters;
var country = parameters.country.toString();
function show(snap) {
console.log('snap');
agent.add(JSON.stringify(snap.val(),null,2));
}
function slkplc(agent) {
var testRef;
firebase.database().ref('slackplace').once('value',show);
}
// Run the proper function handler based on the matched Dialogflow intent name
let intentMap = new Map();
intentMap.set('slack place', slkplc);
agent.handleRequest(intentMap);
But I do not get the expected result while trying it on DialogFlow or Google Assistant. The function show is asynchronously called but too late and the response is not available for DialogFlow :
I see three way to deal with this problem :
use blocking call to database : another database ?
treat asynchronous message with DialogFlow ???
response to user that an error occured.
The third that I choose, but it is always on error.
After trying several things to wait data from database response, the only thing I managed is to freeze the response, therefore the timeout of DialogFlow - 5s -and Firebase - 60s - were reached.
A workaround
Another way to do it is to separate database acquisition and request/response from DialogFlow. The data of database is collected outside of the dialogflowFirebaseFulfillment
var data;
var inidata = firebase.database().ref().on('value',function(snap) {
console.log('snap');
data = snap.val();
});
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
...
function slkplc(agent) {
agent.add(JSON.stringify(data,null,2));
}
// Run the proper function handler based on the matched Dialogflow intent name
let intentMap = new Map();
intentMap.set('slack place', slkplc);
agent.handleRequest(intentMap);
}
Now I can do what I want with data, and I am able to find the place where I can practice waterline in croatia. But there is always something weird, the data of the database is duplicated ...
The "right" solution is option 2 that you suggest: since you're doing an asynchronous call, you need to handle this correctly when working with the dialogflow-fulfillment library.
Basically, if your handler makes an asynchronous call, it needs to be asynchronous as well. To indicate to the handleRequest() method that your handler is async, you need to return a Promise object.
Firebase's once() method returns a Promise if you don't pass it a callback function. You can take advantage of this, return that Promise, and also handle what you want it to do as part of a .then() clause. It might look something like this:
function slkplc(agent) {
var testRef;
return firebase.database().ref('slackplace').once('value')
.then( snap => {
var val = snap.val();
return agent.add( JSON.stringify( val, null, 2 ) );
});
}
The important part isn't just that you use a Promise, but also that you return that Promise.

Retrieve ms-rest-azure authentication code with interactiveLogin method

I'm trying to push one inputless TV screen dashboard (using chromecast) with azure authentication in nodejs (working fine without auth so far)
My best move (?) is using ms-rest-azure package allowing to perform initial authentication from another device with https://aka.ms/devicelogin & a code
However, is there a clean way to retrieve this code and make it available outside the console ? I can't find reference or callback.
My fallback scenario would be to intercept process.stdout.write but feels like dirty.
There is an options object you can pass to interactiveLogin. One option is "userCodeResponseLogger", which should be a function, eg
let options = {"userCodeResponseLogger":(msg)=>{
console.log("I have the message",msg)
}
}
msRestAzure.interactiveLogin(options).then((credentials) => {
// doing authentication stuff
});
Note you'll still need to parse the msg to extract the code.
had to go forward on this issue and finally ends up by intercepting process.stdout.write with a better implementation then mine : https://gist.github.com/pguillory/729616/32aa9dd5b5881f6f2719db835424a7cb96dfdfd6
function auth() {
hook_stdout(function(std) {
var matches = / the code (.*) to /.exec(std);
if(matches !== null && matches.length >=2) {
var code = matches[1];
// doing something with the code
unhook();
}
});
msRestAzure.interactiveLogin().then((credentials) => {
// doing authentication stuff
});
}

Wrapping JavaScript FFI in Either

I am very new to JavascriptFFI and will very much appreciate help here.
I have a working javascript code to grab image as FILE URI from camera (via cordova camera plugin). Now, it can return either error or file uri on success. We want to map them to Left Int Text and Right GHCJS.DOM.Types.File (not sure if I got the type of FILE URI right).
Here is the javascript code (untested since I modified it off the tested one to return just fileuri or error, instead of displaying it in the browser).
<script type="text/javascript" charset="utf-8">
var destinationType; // sets the format of returned value
// Wait for device API libraries to load
document.addEventListener("deviceready",onDeviceReady,false);
// device APIs are available
function onDeviceReady() {
pictureSource=navigator.camera.PictureSourceType;
destinationType=navigator.camera.DestinationType;
}
// Called when a photo is successfully retrieved
//
function onPhotoURISuccess(imageURI) {
console.log("success");
return imageURI;
}
// A button will call this function for testing
function capturePhotoEdit() {
// Take picture using device camera, allow edit, and retrieve image as binary data
navigator.camera.getPicture(onPhotoURISuccess, onFail, { quality: 20, allowEdit: true,
destinationType: destinationType.FILE_URI });
}
// Called if something bad happens.
//
function onFail(message) {
console.log("failure");
return[1, message];
}
</script>
I will appreciate pointers on how to do the FFI to navigator.camera.getPicture with ghcjs-dom-0.2.3.1 (I am using it with Reflex) such that returned result is in Either.
I can then wrap the File URI in ByteString (I think through Cordova file api to convert it first to arraybuffer) and send it off to remove server for persistence.

Can't publish options with RabbitMQ message?

I'm using ampq.node for my RabbitMQ access in my Node code. I'm trying to use either the publish or sendToQueue methods to include some metadata with my published message (namely timestamp and content type), using the options parameter.
But whatever I'm passing to options is completely ignored. I think I'm missing some formatting, or a field name, but I cannot find any reliable documentation (beyond the one provided here which does not seem to do the job).
Below is my publish function code:
var publish = function(queueName, message) {
let content;
let options = {
persistent: true,
noAck: false,
timestamp: Date.now(),
contentEncoding: 'utf-8'
};
if(typeof message === 'object') {
content = new Buffer(JSON.stringify(message));
options.contentType = 'application/json';
}
else if(typeof message === 'string') {
content = new Buffer(message);
options.contentType = 'text/plain';
}
else { //message is already a buffer?
content = message;
}
return Channel.sendToQueue(queueName, content, options); //Channel defined and opened elsewhere
};
What am I missing?
Update:
Turns out if you choose to use a ConfirmChannel, you must provide the callback function as the last parameter, or else, the options object is ignored. So once I changed the code to the following, I started seeing the options correctly:
Channel.sendToQueue(queueName, content, options, (err, result) => {...});
Somehow, I can't seem to get your example publish to work... though I don't see anything particularly wrong with it. I'm not sure why I wasn't able to get your example code working.
But I was able to modify a version of my own amqplib intro code, and got it working with your options just fine.
Here is the complete code for my example:
// test.js file
var amqplib = require("amqplib");
var server = "amqp://test:password#localhost/test-app";
var connection, channel;
function reportError(err){
console.log("Error happened!! OH NOES!!!!");
console.log(err.stack);
process.exit(1);
}
function createChannel(conn){
console.log("creating channel");
connection = conn;
return connection.createChannel();
}
function sendMessage(ch){
channel = ch;
console.log("sending message");
var msg = process.argv[2];
var message = new Buffer(msg);
var options = {
persistent: true,
noAck: false,
timestamp: Date.now(),
contentEncoding: "utf-8",
contentType: "text/plain"
};
channel.sendToQueue("test.q", message, options);
return channel.close();
}
console.log("connecting");
amqplib.connect(server)
.then(createChannel)
.then(sendMessage)
.then(process.exit, reportError);
to run this, open a command line and do:
node test.js "example text message"
After running that, you'll see the message show up in your "test.q" queue (assuming you have that queue created) in your "test-app" vhost.
Here's a screenshot of the resulting message from the RMQ Management plugin:
side notes:
I recommend not using sendToQueue. As I say in my RabbitMQ Patterns email course / ebook:
It took a while for me to realize this, but I now see the "send to queue" feature of RabbitMQ as an anti-pattern.
Sure, it's built in to the library and protocol. And it's convenient, right? But that doesn't mean you should use it. It's one of those features that exists to make demos simple and to handle some specific scenarios. But generally speaking, "send to queue" is an anti-pattern.
When you're a message producer, you only care about sending the message to the right exchange with the right routing key. When you're a message consumer, you care about the message destination - the queue to which you are subscribed. A message may be sent to the same exchange, with the same routing key, every day, thousands of times per day. But, that doesn't mean it will arrive in the same queue every time.
As message consumers come online and go offline, they can create new queues and bindings and remove old queues and bindings. This perspective of message producers and consumers informs the nature of queues: postal boxes that can change when they need to.
I also recommend not using amqplib directly. It's a great library, but it lacks a lot of usability. Instead, look for a good library on top of amqplib.
I prefer wascally, by LeanKit. It's a much easier abstraction on top of amqplib and provides a lot of great features and functionality.
Lastly, if you're struggling with other details in getting RMQ up and running with Node.js, designing your app to work with it, etc., check out my RabbitMQ For Devs course - it goes from zero to hero, fast. :)
this may help others, but the key name to use for content type is contentType in the javascript code. Using the web Gui for rabbitMQ, they use content_type as the key name. different key names to declare options, so make sure to use the right one in the right context.

Resources