wit.ai Bot Engine Stories connected to hubot - javascript

I am trying to get the new wit.ai Bot Engine connected to hubot with Javascript.
Unfortunately I am not a JS Developer so I'm struggling.
Here's the code I have:
'use strict';
const Wit = require('../../../node-wit').Wit;
const firstEntityValue = (entities, entity) => {
const val = entities && entities[entity] &&
Array.isArray(entities[entity]) &&
entities[entity].length > 0 &&
entities[entity][0].value
;
if (!val) {
return null;
}
return typeof val === 'object' ? val.value : val;
};
const actions = {
say: (sessionId, msg, cb) => {
console.log(msg);
cb();
},
merge: (context, entities, cb) => {
const loc = firstEntityValue(entities, "location");
if (loc) context.loc = loc;
cb(context);
},
error: (sessionId, msg) => {
console.log('Oops, I don\'t know what to do.');
},
'fetch-weather': (context, cb) => {
// Here should go the api call, e.g.:
// context.forecast = apiCall(context.loc)
context.forecast = 'sunny';
cb(context);
},
};
const client = new Wit('MY_TOKEN_HERE', actions);
client.interactive();
module.exports = function(robot) {
robot.respond(/hey\s+(.+$)/i, function(msg){
var match = msg.match[1];
msg.send("I've heared: " + match);
console.log(match)
process.stdout.write(match);
});
}
The script listens to "hey botname" and outputs what was written after this. My Problem is I have no clue how to get this input sent to the wit client. Using this script in bash without the hubot stuff works fine towards wit as this is based on the quick start example from wit.ai.
The other issue I'm facing is that I would like to have hubot listen in a private channel with every user and have it respond to every message without prefix. Just as the node example does in the console.
Help is highly apprechiated!

Ok, after fiddling a while I made this work.
Here's what my hubot script looks like now:
'use strict';
const Wit = require('../../../node-wit').Wit;
var room;
const firstEntityValue = (entities, entity) => {
const val = entities && entities[entity] &&
Array.isArray(entities[entity]) &&
entities[entity].length > 0 &&
entities[entity][0].value
;
if (!val) {
return null;
}
return typeof val === 'object' ? val.value : val;
};
const actions = {
say: (sessionId, msg, cb) => {
console.log(msg);
room.send(msg)
cb();
},
merge: (context, entities, cb) => {
const loc = firstEntityValue(entities, "location");
if (loc) context.loc = loc;
cb(context);
},
error: (sessionId, msg) => {
console.log('Oops, I don\'t know what to do.');
room.send('Oops, I don\'t know what to do.')
},
};
const client = new Wit('MY_TOKEN_HERE', actions);
//client.interactive();
module.exports = function(robot) {
robot.listen(function(msg) {
var userID = msg.user.id;
var roomID = msg.user.room;
// is this a direct chat(private room)?
if(roomID.indexOf(userID) >= 0){
if(typeof msg.text == "string"){
client.pxMessage(msg.text);
}
}
return true;
},
function(response){
// save room for replys
room = response;
});
}
additionally I made an awful hack to wit.js to get this work. I added the following function as I was not able to use the available methods to get this working. Basically callbacks and session were holding me back:
this.pxMessage = (message) => {
const sessionId = uuid.v1();
const context = {};
const steps = 5;
this.runActions(
sessionId,
message,
context,
(error, context) => {
if (error) {
l.error(error);
}
rl.prompt();
},
steps
);
}
If someone would take this further and implement it properly I would love to see the result. This hack works and we now have a really smart bot in our rocket.chat who understands natural language and learns every day.

You can directly use this module, it seems to be working well: https://www.npmjs.com/package/hubot-wit
I have just now finished integration. You just need to provide the environment variable WIT_TOKEN and it works beautifully!

Related

Problems getting slot in AWS Lambda function

I am trying to build a simple Alexa Skill. Now, I want to access some of my slot values. This is my code. When I uncomment even the line that I define Alexa in, my skill will not work. Also, if I only uncomment the line defining var text, I still get "there was a problem with the skills response". Const gives the same output. I am using custom slots called recipe. How can I access the slots in my lambda function? Thanks.
const breakfast = {
"bacon and eggs":["bacon","egg"],
"buttered toast":["bread", "butter"]
};
const lunch = {
"ham sandwich":["ham","cheese"]
};
const dinner = { "Steak and eggs": ['steak','eggs']};
//const Alexa = require('ask-sdk-core');
exports.handler = (event, context, callback) => {
try {
if (event.request.type === 'LaunchRequest') {
callback(null, buildResponse('Hello from Lambda'));
} else if (event.request.type === 'IntentRequest') {
const intentName = event.request.intent.name;
if (intentName === 'breakfast') {
callback(null, buildResponse(Object.keys(breakfast)+"") );
}
else if (intentName === 'lunch') {
callback(null, buildResponse(Object.keys(lunch)+"") );
}
else if (intentName === 'dinner') {
callback(null, buildResponse(Object.keys(dinner)+"") );
}
else if (intentName ==='requestRecipe'){
//var text = this.event.request.intent.slots.recipe.value;
//const meal = Alexa.getSlotValue(intentName, "meal")
callback(null, buildResponse("Recipe requested") );
}
else {
callback(null, buildResponse("Sorry, i don't understand"));
}
} else if (event.request.type === 'SessionEndedRequest') {
callback(null, buildResponse('Session Ended'));
}
} catch (e) {
context.fail(`Exception: ${e}`);
}
};
function buildResponse(response) {
return {
version: '1.0',
response: {
outputSpeech: {
type: 'PlainText',
text: response,
},
shouldEndSession: false,
},
sessionAttributes: {},
};
}
for a bit of context: my lambda has the endpoint of what my alexa hosted skill was, and the alexa skill has the endpoint of the lambda. when I say const gives the same output, i mean instead of using var, when I use const, it does the same thing. The JSON file that i get as a reply is empty brackets.
I found the issue behind my problem. Instead of using
//var text = this.event.request.intent.slots.recipe.value;
i simply did var text = event.request.intent.slots.recipe.value; I am now able to use text in building a response or something like that.

multiple http.post in Angular

I am trying to send an http.post request for each element of an array, my method works well, but when I subscribe, it does it for each of the requests, if someone could help me optimize this, I will I would really appreciate it, here I leave the snippets of my code.
component.ts
saveExclusion() {
this.indForm.value.Centers.forEach(element => {
for (const days of this.exclusionDays) {
delete days.horadesde;
delete days.horahasta;
delete days.id;
for (const key in days) {
if (days[key] === true) {
days[key] = true;
}else if (days[key] === false) {
delete days[key];
}
}
}
const valueForm = this.indForm.value;
valueForm.ResourceId = this.idResource;
valueForm.TimeZoneId = 'America/Santiago';
valueForm.CenterId = element;
this.exclusionFunc = false;
this.apiFca.saveNew(valueForm, this.exclusionDays)
.pipe(last()).subscribe((res: any) => {
console.log(res)
if (res === '200') {
this.successMessage = true;
this.exclusionDays = [];
this.indForm.reset();
this.ngOnInit();
setTimeout(() => {
this.successMessage = false;
}, 3000);
}
}, err => {
console.log('error', err);
});
});
}
service.ts
saveNew(exclusionData, daysBlock) {
let reason = '';
const dt = new Date();
const n = dt.getTimezoneOffset();
const tz = new Date(n * 1000).toISOString().substr(14, 5);
if (exclusionData.OtherReason) {
reason = exclusionData.ExclusionReason + ' ' + exclusionData.OtherReason;
} else {
reason = exclusionData.ExclusionReason;
}
if (exclusionData.ExclusionType !== 'Partial' ) {
daysBlock = [];
}
const data = {Exclusion: new ExclusionClass(
[],
reason,
exclusionData.ExclusionType,
exclusionData.Repetition,
exclusionData.CenterId,
exclusionData.ProfessionalName,
exclusionData.ResourceId,
daysBlock,
exclusionData.TimeZoneId,
'Exclude',
exclusionData.Unit,
exclusionData.ValidFrom + 'T' + exclusionData.ValidTimeFrom + ':00-' + tz,
exclusionData.ValidTo + 'T' + exclusionData.ValidTimeUntil + ':59.999-' + tz
)};
if (exclusionData.CenterId === '') {
delete data.Exclusion.CenterId;
}
return this.http
.post("url", data)
.pipe(
map((res: any) => {
return res.code;
})
);
}
greetings, and I look forward to your comments, thanks.
I'm not fully confident in my rxjs knowledge but it looks like, because of .pipe(last()), you are only watching the last request? I'd recommend you only set success if all completed without error, like
this.apiFca.saveNew(valueForm, this.exclusionDelays)
.subscribe(
res => {
console.log(res);
},
err => {
console.log(err);
},
() => {
this.successMessage = true;
// etc. etc. etc.
});
or maybe instead of using this.successMessage use something like this.saveState$ that would be the a BehaviorSubject object initialized with 'idle' (or some enum thereof) that your saveExclusion() function manages. That way, the beginning of your saveExclusion() function could
set const saveState$ = this.saveState$
assert that saveState$.getValue() === 'in process' or if not, do something about it,
saveState$.next('in process');
and you could change your subscribe line to
this.apiFca.saveNew(valueForm, this.exclusionDelays)
.subscribe(
res => {
if (res !== '200') {
saveState$.next('unexpected result')
} },
err => {
console.log(err);
saveState$.next('error');
},
() => {
if (saveState$.getValue() === 'in process') {
saveState$.next('success');
} }
);
And then you can subscribe to your component's saveState$ as well (though outside of the component you'd want to provide saveState$.asObservable() so values can't be injected by outside code). This affords some elegant event-driven code in your component initialization:
saveState$.pipe(filter(val => val === 'error'))
.subscribe(functionToTellYourUserThereWasAnError);
// if successful, we want other code to know, but immediately change it back to 'idle' even if other code errors
saveState$.pipe(filter(val => val === 'success')
.subscribe(val => saveState$.next('idle'));
// upon success, reset me
saveState$.pipe(filter(val => val === 'success'))
.subscribe(
val => {
this.exclusionDays = [];
// etc. etc.
// setTimeout not needed because set to 'idle' in a different thread.
}
)
Plus, I think your template could reflect and change the UI in response to changes in saveState$ as well, so your save button can be enabled/disabled based on whether or not saveState is 'idle', etc.

(node:13) UnhandledPromiseRejectionWarning: DiscordAPIError: Unknown Channel

I have a problem with my discord bot, I try to delete all channels of a server, then I create new channels and send message in all channels. I know it's against Discord TOS, but it's just in my server and it's just to see if it's possible. My problem is that it deletes and creates the channel but it do not send mesages in the channels, instead I get an error. I hope that you could help me.
Here's the error :
at /home/container/node_modules/discord.js/src/client/rest/RequestHandlers/Sequential.js:85:15
at /home/container/node_modules/snekfetch/src/index.js:215:21
Here's the code :
const bot = new Discord.Client()
const PREFIX = "$"
bot.on('ready', () => {
console.log('bot is online')
})
bot.on('message', async message => {
const taille = message.guild.channels.filter((c) => c.type === "text" || c.type === "voice").size;
let args = message.content.substring(PREFIX.length).split(" ");
if (!message.content.startsWith(PREFIX)) {
return
}
if (message.author == bot) { return }
switch (args[0]) {
case 'raid':
var attackembed = new Discord.RichEmbed()
.setColor('#FF0000 ')
.setTitle(`test`)
.setDescription(`123`)
.setFooter('test BOT')
if (message.member.hasPermission('ADMINISTRATOR')) {
function antilag() {
for (let index = 0; index != taille; index++) {
message.guild.channels.forEach(channel => channel.delete())
}
for (let x = 0; x != args[1]; x++) {
message.guild.createChannel(`hacked by ${message.member.user.tag} `, { type: 'text' })
}
}
function msg() {
for (let x = 0; x != args[1]; x++) {
message.guild.channels.forEach(
function(channel, index) {
channel.send(`#everyone The server ${message.guild.name} was hacked by ${message.member.user.tag}`)
})
}
}
message.guild.setName(`HACKED BY ${message.member.user.tag}`)
message.guild.setIcon(message.author.avatarURL)
message.guild.members.forEach(member => member.sendEmbed(embed));
antilag()
msg()
}
break;
}
})
bot.login("my token");
PS : Note that I am new to javascript and discord.js
createChannel is a Promise, which means that by the time this function has finished executing, your code has already moved on other parts, and whenever that channel was requested, it wasn't available yet so it was undefined. You need to wait for the function to resolve then proceed with the rest of your logic:
message.guild.createChannel("whatever channel", { type: "text" }).then(function(createdChannel) {
// the rest of your code
});
Or, since you already called the function inside an async callback:
let newChannel = await message.guild.createChannel("whatever channel", { type: "text" });
// rest of code
Take care.

How do I specify which API data I want to use?

I am developing an Alexa Skill (using v2 and javascript) and I'm attempting to make an API GET call to the USDA API.
At this point I've copy/pasted from here and I'm using the USDA API example from here (with some minor changes). as an attempt just to get the connection to work and return anything that proves it worked.
The error I currently receive is: Error handled: TypeError: Cannot read property 'generalSearchInput' of undefined at Object.handle (/var/task/index.js:39:32)
at process._tickCallback (internal/process/next_tick.js:68:7)
Directly before the error I get the return of: every entry about Cottage Cheese that is in the USDA API, I.E. WAY too much from the console.log(response). I just want the first one, or even just the name.
Console logs have told me that I am receiving data back from the call, so I know it is working. My question is: How do I set the speechOutput to the specific piece of information I need, rather than the entire API object that is returned?
The speechOutput that I'm looking for should say: Cottage Cheese
The response that I am receiving is: Output from Alexa saying:
Sorry, I had trouble doing what you asked. Please try again
index.js
const Alexa = require('ask-sdk-core');
const LaunchRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
},
handle(handlerInput) {
const speakOutput = 'Welcome to food points! What food would you like to know about?';
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
const FoodPointsIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'FoodPointsIntent';
},
async handle(handlerInput) {
console.log("THIS.EVENT = " + JSON.stringify(this.event));
var speakOutput = 'Sorry, there was an error';
//var https = require('https');
const { requestEnvelope } = handlerInput;
const userInput = Alexa.getSlotValue(requestEnvelope, 'FoodQuery');
const response = await httpGet();
console.log(response);
/*
const food = userInput;
speakOutput = food;
*/
speakOutput = response.value.generalSearchInput;
return handlerInput.responseBuilder
.speak(speakOutput)
//.reprompt('add a reprompt if you want to keep the session open for the user to respond')
.getResponse();
}
};
const TestIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'TestIntent';
},
handle(handlerInput) {
console.log("THIS.EVENT = " + JSON.stringify(this.event));
var speakOutput = 'Sorry, there was an error';
const { requestEnvelope } = handlerInput;
const userInput = Alexa.getSlotValue(requestEnvelope, 'TestQuery');
const food = userInput;
speakOutput = food;
return handlerInput.responseBuilder
.speak(speakOutput)
//.reprompt('add a reprompt if you want to keep the session open for the user to respond')
.getResponse();
}
};
function httpGet() {
return new Promise(((resolve, reject) => {
var options = {
host: 'api.nal.usda.gov',
port: 443,
path: '/fdc/v1/foods/search?api_key=DEMO_KEY&query=Cheddar%20Cheese',
method: 'GET',
};
var https = require('https');
const request = https.request(options, (response) => {
response.setEncoding('utf8');
let returnData = '';
response.on('data', (chunk) => {
returnData += chunk;
});
response.on('end', () => {
resolve(JSON.parse(returnData));
});
response.on('error', (error) => {
reject(error);
});
});
request.end();
}));
}
/****************************REMEMBER TO UPDATE THIS*************************/
const HelpIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
const speakOutput = 'You can say hello to me! How can I help?';
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
const CancelAndStopIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& (Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.CancelIntent'
|| Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.StopIntent');
},
handle(handlerInput) {
const speakOutput = 'Goodbye!';
return handlerInput.responseBuilder
.speak(speakOutput)
.getResponse();
}
};
const SessionEndedRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'SessionEndedRequest';
},
handle(handlerInput) {
// Any cleanup logic goes here.
return handlerInput.responseBuilder.getResponse();
}
};
// The intent reflector is used for interaction model testing and debugging.
// It will simply repeat the intent the user said. You can create custom handlers
// for your intents by defining them above, then also adding them to the request
// handler chain below.
const IntentReflectorHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest';
},
handle(handlerInput) {
const intentName = Alexa.getIntentName(handlerInput.requestEnvelope);
const speakOutput = `You just triggered ${intentName}`;
return handlerInput.responseBuilder
.speak(speakOutput)
//.reprompt('add a reprompt if you want to keep the session open for the user to respond')
.getResponse();
}
};
// Generic error handling to capture any syntax or routing errors. If you receive an error
// stating the request handler chain is not found, you have not implemented a handler for
// the intent being invoked or included it in the skill builder below.
const ErrorHandler = {
canHandle() {
return true;
},
handle(handlerInput, error) {
console.log(`~~~~ Error handled: ${error.stack}`);
const speakOutput = `Sorry, I had trouble doing what you asked. Please try again.`;
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
// The SkillBuilder acts as the entry point for your skill, routing all request and response
// payloads to the handlers above. Make sure any new handlers or interceptors you've
// defined are included below. The order matters - they're processed top to bottom.
exports.handler = Alexa.SkillBuilders.custom()
.addRequestHandlers(
LaunchRequestHandler,
FoodPointsIntentHandler,
TestIntentHandler,
HelpIntentHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler,
IntentReflectorHandler, // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
)
.addErrorHandlers(
ErrorHandler,
)
.lambda();
I figured it out. Hopefully this can help somebody who may be stuck.
I was calling the API data incorrectly.
The code that did what I needed
var nvArray = [];
for(var i = 0; i < response.foods[0].foodNutrients.length; i++) {
var nvArrayString = [response.foods[0].foodNutrients[i].nutrientName] + '';
var nvShortString = nvArrayString.split(',', 1);
nvArray.push(nvShortString);
}
var nvArraySpeakOutputString = nvArray.join();
The actual piece of code that fixed my errors
response.foods[0].foodNutrients[i].nutrientName
I was looking at the wrong part of the data being given to me, and thus calling something that didn't exist, i.e. response.value.generalSearchInput.
Additional information that may be helpful
nvArray is used so that I can put multiple variables from the data into one place (using push and pop).
This allowed me to split the data into shorter pieces each time the loop runs (used to keep the output from being a wall of text for each item).
nvArraySpeakOutputString is the nvArray joined together, allowing Alexa to speak it all as one variable, preventing me from having to type
varA + '. next ' + varB
and so on for each item.
Anyway, hope this helps somebody at some point!

Firebase onWrite cloud function does not run intermittently

I have been using firebase-queue for years to invoke jobs for years, but recently it started failing with transaction errors competing on resources, and had serious scaling issue.
So I migrated firebase-queue to cloud function and it seemed to be fast and very scalable initially. I wrote a simple script that is hooked up by onWrite on queue/task/{taskId}. But it seems like sometimes onWrite is never triggered randomly. I can not see the specific job triggered in the logs at all.
We are using blaze plan, promise is returned on the function, timelimit is set as maximum, 99.9% of the jobs are taking less than 5000ms.
We have like 50K invocations per day, and around 100 jobs are not triggered. This is so intermittent and have no clue on that. One strange thing is I don't see any errors on log viewer, but on the invocations graph, I can see some load error and connection error, and still no idea what are they. Any insight to resolve the issue would be appreciated.
const functions = require(`firebase-functions`);
const admin = require(`firebase-admin`);
const _ = require(`lodash`);
const specs = require(`./specs.json`);
const queueSpecs = require(`./queue-specs.js`);
const SERVER_TIMESTAMP = { ".sv": `timestamp` };
admin.initializeApp();
const workers = {};
queueSpecs.forEach((spec) => {
let specString = spec;
if (typeof spec === `object`) {
specString = spec.id;
}
workers[specString] = require(`./lib/workers/${specString}`);
});
const taskHandler = (change, context) => {
const data = change.after.val();
const ref = change.after.ref;
const { taskID } = context.params;
if (!data) {
return null;
}
const worker = workers[data._state];
if (!worker) {
console.error(`Worker not found for task ${taskID} - ${data._state}`);
return null;
}
const specID = data._state.replace(`/`, `_`);
const spec = specs[specID];
if (!spec) {
console.error(`Spec not found for task ${taskID} - ${specID}`);
return null;
}
console.log(
`Running ${taskID} - ${data._state} - ${JSON.stringify(data, null, 2)}`
);
return new Promise((resolve, reject) => {
const taskResolve = newTask => {
const updates = _.clone(newTask);
let newState = _.get(newTask, `_new_state`, ``);
newState = newState || spec.finished_state;
if (!updates || !newState) {
console.log(`Exiting task - ${taskID}`);
return ref.set(null).then(resolve, reject);
}
updates._state = newState || spec.finished_state;
updates._state_changed = SERVER_TIMESTAMP;
updates._owner = null;
updates._progress = null;
updates._error_details = null;
console.log(`Resolving`);
return ref.update(updates).then(resolve, reject);
};
const taskReject = error => {
const updates = {};
let errorString;
if (_.isError(error)) {
errorString = error.message;
} else if (_.isString(error)) {
errorString = error;
} else if (!_.isUndefined(error) && !_.isNull(error)) {
errorString = error.toString();
}
if (updates._state === `error`) {
console.log(`Exiting task on reject - ${taskID}`);
return ref.set(null).then(resolve, reject);
}
updates._state = spec.error_state || `error`;
updates._state_changed = SERVER_TIMESTAMP;
updates._owner = null;
updates._progress = null;
updates._error_details = {
previous_state: spec.in_progress_state,
error: errorString,
errorStack: _.get(error, `stack`, null),
attempts: 1
};
console.log(`Rejecting`);
return ref.update(updates).then(resolve, reject);
};
const taskProgress = () => {
// eslint-disable-line
// progress is never used, thus just resolving
const updates = {};
console.log(`Progress ????`);
return ref.update(updates).then(resolve, reject);
};
worker(data, taskProgress, taskResolve, taskReject);
});
};
exports.taskRunner = functions.database
.ref(`/queue/tasks/{taskID}`)
.onWrite(taskHandler);

Categories

Resources