Creating dialogs programmatically from JSON file - node.js

I am using the Microsoft Bot Framework with Node.js. I have a config file that looks like the following.
{
"server": {
"port": 3978
},
"dialogs": {
"default": {
"text": "This is some sample text.",
"actions": [
{
"title": "Button 1",
"value": "Action 1"
},
{
"title": "Button 2",
"value": "Action 2"
}
]
},
"hello": {
"text": "hello",
"matches": "^hello$"
},
"asdf": {
"text": "asdf",
"matches": "^asdf$"
},
"goodbye": {
"text": "goodbye",
"matches": "^goodbye$"
}
}
}
I want to use a for loop to read through the dialogs and create them so that they respond with the text value and have the trigger action of the matches value.
For example, the bot responds hello to the input of hello, asdf to the input of asdf, and goodbye to the input of goodbye.
The function I have written in an attempt to solve this looks like this.
var create = function(bot, _config) {
var config = JSON.parse(JSON.stringify(_config));
// Create dialogs from config
var keys = Object.keys(config.dialogs);
for(var i = 0; i < keys.length; i++) {
var dialogName = keys[i];
var dialog = config.dialogs[dialogName];
// Skip over default dialog
if(dialogName == "default") continue;
// Create other dialogs
bot.dialog(dialogName, function(session) {
var text = dialog.text;
session.endDialog(text);
}).triggerAction({
matches: new RegExp(dialog.matches, "i")
});
}
}
When I run this, the bot responds with goodbye to the inputs of hello, asdf, and goodbye. However, the console shows that the correct dialogs are being called when they are supposed to. Even when I call the hello dialog by using session.beginDialog('hello');, the bot returns goodbye.
What seems to be causing the problem here?

It's a common “gotchas” of var in javascript. replace var to let should fix your issue.
The issue similar with
for (var i = 0; i < 10; i++) {
setTimeout(function() { console.log(i); }, 100 * i);
}
The root cause is var is function-scoped and let is block-scoped. You can refer to https://www.typescriptlang.org/docs/handbook/variable-declarations.html for details.

Related

Can chrome extension context menu (for links) extract the link content before it popups up?

I want to create a context menu item depending on what the context link is.
For instance, I want to display "AAA" for a link that starts with "https://www.google.com" and "BBB" for a link that starts with "https://twitter.com".
var context_twitter_search = chrome.contextMenus.create({
"title":'AAA', // This is what I want to change depending on the info.linkUrl.
"contexts":["link"],
"onclick":function(info, tab) {
var target_content = '';
target_content = info.linkUrl; // I can ONLY get the content after right-click.
chrome.tabs.create({url:"https://twitter.com/search?q="+target_content});
}
});
Try this in your background script:
function onClickHandler(info, tab) {
let tp = info.menuItemId;
let url = info.linkUrl;
switch (tp) {
case 'AAA':
alert('hi google user');
break;
case 'BBB':
alert('hi Tweeter user');
break;
}
}
function handleInstalled(details) {
var contextMenuItem = [
{
"id": "AAA",
"title": "AAA",
"contexts": ["link"],
"targetUrlPatterns": ["https://*.google.com/*"]
}, {
"id": "BBB",
"title": "BBB",
"contexts": ["link"],
"targetUrlPatterns": ["https://*.twitter.com/*"]
}
];
let cmi;
for (let k = 0; k < contextMenuItem.length; k++) {
cmi = contextMenuItem[k];
chrome.contextMenus.create({
"id": cmi.id,
"title": cmi.title,
"contexts": cmi.contexts,
"enabled": true,
"targetUrlPatterns": cmi.targetUrlPatterns
}, () => {
if (chrome.runtime.lastError) {}
else {}
})
}
chrome.contextMenus.onClicked.addListener(onClickHandler)
}
chrome.runtime.onInstalled.addListener(handleInstalled);

Manipulate values in a JSON in nodeJS [duplicate]

This question already has answers here:
How to update a value in a json file and save it through node.js
(8 answers)
Closed 2 years ago.
I am new to Nodejs and JSON manipulations. I have a jSON that looks like
"ent": [
{
"employee": [
{
"emp_name": "",
"column": "employee",
"emp_id": 123456,
"first name": "Joe",
"last name": "Bloggs",
"email": "",
"ldapid":
} ,
{
"emp_name": "",
"column": "employee",
"emp_id": 123456,
"first name": "Foo",
"last name": "Bars",
"email": "",
"ldapid":
}
]
}
]
I need to fill the email, ldapid and emp_name based on the firstname and last name
The desired output is
"ent": [
{
"employee": [
{
"emp_name": "Joe Bloggs",
"column": "employee",
"emp_id": 123456,
"first name": "Joe",
"last name": "Bloggs",
"email": "jbloggs#mycompemail.com",
"ldapid": "jbloggs"
} ,
{
"emp_name": "Foo Bars",
"column": "employee",
"emp_id": 567891,
"first name": "Foo",
"last name": "Bars",
"email": "fbars#mycompemail.com",
"ldapid": "fbars"
}
]
}
]
Since I am super new to the nodeJS world , I am making some initial steps to get to where I want..
The following is what I have done..
EDITED my POST
Hi All, Thanks for all your responses.
I was hoping to get an answer that did something similar to the below. this may not be a code with best practices, but does what I want, may be experts in this group can make it better.
const fs = require('fs');
/** Method to start
*
*
*/
const main = async () => {
const myJSONObject = require('./people.json');
try {
for (var i = 0; i < myJSONObject.entities.length; i++) {
var entity = myJSONObject.entities[i];
if (entity.People) {
for (var j = 0; j < entity.People.length; j++) {
var people = entity.People[j];
var fn = people["first name"];
var ln = people["last name"];
var email = `${fn.substring(0, 3)}${ln.substring(0, 5)}#mycompmail.com`;
var ldapid = `${fn.substring(0, 3)}${ln.substring(0, 5)}`;
myJSONObject.entities[i].People[j]["email"] = email.toLowerCase();
myJSONObject.entities[i].People[j]["ldap id"] = ldapid.toLowerCase();
myJSONObject.entities[i].People[j]["preferred first name"] = fn;
myJSONObject.entities[i].People[j]["preferred last name"] = ln;
// console.log(`${fn}.${ln}`)
}
}
}
fs.writeFileSync('./new_people.json', JSON.stringify(myJSONObject, 0, 4));
}
catch (error) {
console.log(error);
}
};
(async () => {
await main();
})();
Any help in this is highly appreciated.
Vakichak
From your code snipped I assume, that the JSON is a string in a file.
So the first step you need to do is to import the file contents into a variable. You can do that with fs.readFileSync(). Now you have the string in a variable.
Next you need to do is to convert the string into an object. You can do that with JSON.parse(). Now you have an object that you can manipulate.
To write it the object back into a file, you can use JSON.stringify() to make it a string again and then fs.writeFileSync() to write it to the file.
Full script:
const jsonString = fs.readFileSync('./people.json')
const jsonObject = JSON.parse(jsonString)
// do stuff with jsonObject that is written into newJsonObject
const newJsonString = JSON.stringify(newJsonObject)
fs.writeFileSync('./new_people.json', newJsonObject)
Note: there's also async functions for writing and reading files. The sync functions are okay if you load a config or something like this at the beginning of a script. If you read/write many files during runtime, you should use the async functions.

chrome.browserAction.onClicked cannot be triggered

I am working on some experiments that requires to auto generate IDs for elements, I have decided to go with chrome extension.
However, the content script only got triggered when I land on the homepage of the site (e.g. https://secure.checkout.visa.com/). Once I navigate to other pages (e.g. tap on "Create Account"), the script didn't get triggered, I tried to set a break point on the content script but it only got hit on the home page, but not on create account page.
Any idea what went wrong here? Below is the code:
// manifest.json
{
"manifest_version": 2,
"name": "Chrome Auto Gen ID",
"description": "Generate IDs",
"version": "1.0",
"permissions": ["activeTab"],
"background": {
"scripts": ["event_page.js"],
"persistent": false
},
"browser_action": {
"default_title": "Auto generate ID"
},
"content_scripts": [
{
"matches": ["https://secure.checkout.visa.com/*"],
"js": ["jquery-3.2.1.min.js", "auto_gen_id.js"],
"all_frames": true,
"run_at": "document_idle"
}
]
}
The content script:
// auto_gen_id.js
var divs = document.getElementsByTagName("div");
var inputs = document.getElementsByTagName("input");
var sections = document.getElementsByTagName("section");
var count = 0;
function genId(list) {
for (var i = 0; i < list.length; ++i) {
var element = list[i];
if (element.hasAttribute("id")) {
continue;
}
element.id = "gen-id-" + count++;
}
}
genId(divs);
genId(inputs);
The background script:
// event_page.js
chrome.browserAction.onClicked.addListener(function(tab) {
console.log('Generating IDs on ' + tab.url);
chrome.tabs.executeScript(null, {file: "auto_gen_id.js"});
});
Thanks #makyen and #wOxxOm, followed the suggestions below to solve the problem:
Is there a JavaScript/jQuery DOM change listener

Issue with Null SpeechletResponse (Alexa)

Something is messed up with my AMAZON.StopIntent. No matter what I put there (I've tried everything from every tutorial), whenever it's called, I get "There was a problem with the requested skill's response" and the Alexa app shows the error as "speechletresponse cannot be null". My project is in the JSON, not Java format.
If anyone can help, I'd very much appreciate it!
Thanks!
As requested here's what is being sent to Lambda
{
"session": {
"sessionId": "SessionId.XXXXX",
"application": {
"applicationId": "amzn1.ask.skill.XXXXXXX"
},
"attributes": {},
"user": {
"userId": "amzn1.ask.account.XXXXXXX"
},
"new": true
},
"request": {
"type": "IntentRequest",
"requestId": "EdwRequestId.XXXXXX",
"locale": "en-US",
"timestamp": "2017-01-18T22:38:53Z",
"intent": {
"name": "AMAZON.StopIntent",
"slots": {}
}
},
"version": "1.0"
}
And here's the relevent code:
var handlers = {
'LaunchRequest': function () {
this.emit('AMAZON.HelpIntent');
},
'GetNewDogThoughtIntent': function () {
this.emit('GetDogThought');
},
'GetNewCatThoughtIntent': function () {
this.emit('GetCatThought');
},
'GetDogThought': function () {
var dogthoughtIndex = Math.floor(Math.random() * DOGTHOUGHTS.length);
var randomDogThought = DOGTHOUGHTS[dogthoughtIndex];
// Create speech output
var speechOutput = "Your dog is thinking, " + randomDogThought;
this.emit(':tellWithCard', speechOutput, "Your dog was thinking... ", randomDogThought);
},
'GetCatThought': function () {
var catthoughtIndex = Math.floor(Math.random() * CATTHOUGHTS.length);
var randomCatThought = CATTHOUGHTS[catthoughtIndex];
// Create speech output
var speechOutput = "Your cat is thinking, " + randomCatThought;
this.emit(':tellWithCard', speechOutput, "Your cat was thinking... ", randomCatThought);
},
'AMAZON.HelpIntent': function () {
var speechOutput = "You can ask me for what your cat or dog is thinking, or you can say exit... Right now I can only provide thoughts for one cat or dog at a time... What can I help you with?";
var reprompt = "What can I help you with? Make sure to say if your pet is a cat or dog when you ask!";
this.emit(':ask', speechOutput, reprompt);
},
'SessionEndedRequest': function (sessionEndedRequest, session) {
},
"AMAZON.StopIntent": function (shouldEndSession) {
}
I finally got it after consulting the SpaceGeek tutorial again and making some tweaks to it. Basically, here's what worked:
'AMAZON.StopIntent': function () {
this.emit(':tell', "Goodbye!");
}
The key was the ':tell', which I didn't have before. Thanks to everyone who answered and helped!
Can you post your code for the StopIntent? You should be calling a speechlet response in it. For example:
'AMAZON.StopIntent': function (shouldEndSession, response) {
var speechOutput = "Goodbye";
response.tell(speechOutput);
},
Are you building that response properly and passing it?
I have found this link on alexa developer forum. This might help in your issue..
https://forums.developer.amazon.com/questions/49211/system-error-speechletresponse-was-null.html
I am writing this code in php, if that helps
$data = file_get_contents("php://input");
$jsonData = json_decode($data);
if($jsonData->request->type === "IntentRequest"){
$IntentName = $jsonData->request->intent->name;
if($IntentName === "AMAZON.StopIntent"){
$response = '{
"version" : "1.0",
"response" : {
"outputSpeech": {
"type": "PlainText",
"text": ""
},
"shouldEndSession" : false
}
}';
echo $response;
}
}

node/Axios log promises in a loop

I tried to adapt from code that I found on stack overflow but to no avail. I am trying to log what happens on each iteration of my post call. The promises are always logging as pending and I can't seem to get an end result on them. Any help would be greatly appreciated.
var axios = require('axios')
url2 = `https://api.hubapi.com/properties/v1/contacts/properties?hapikey=xxx`
var mainObject = {},
promises = [];
for(var i = 0; i < 10; i++){
//console.log("The number i is: ",i)
promises.push(axios.post(url2,
{
"name": "newcustomproperty" + i,
"label": "A New Custom Property",
"description": "A new property for you",
"groupName": "contactinformation",
"type": "string",
"fieldType": "text",
"formField": true,
"displayOrder": 6,
"options": []
}
))
//console.log(promises)
}
//console.log(promises)
axios.all(promises).then(function(results) {
results.forEach(function(response) {
console.log(response)
mainObject[response.identifier] = response.value;
}).catch(function(response) {
mainObject[response.identifier] = response.value
})
});

Resources