Alexa skill, custom slot - date and time - amazon-dynamodb

I've created a skill and i want to be able to call upon a machine state at a certain date and time from my dynamo db table.
my first column is date and my sort key is time.
Would i need to create a custom slot for date with all 365 daysof the year or is there a quicker way to do this? Also would i need to create a custom slot for every minute of the day.
Code:
var AWSregion = 'us-east-1'; // us-east-1
var AWS = require('aws-sdk');
var dbClient = new AWS.DynamoDB.DocumentClient();
AWS.config.update({
region: "'us-east-1'"
});
let GetMachineStateIntent = (context, callback) => {
var params = {
TableName: "updatedincident",
Key: {
date: "2018-03-28",
time: "04:23",
}
};
dbClient.get(params, function (err, data) {
if (err) {
// failed to read from table for some reason..
console.log('failed to load data item:\n' + JSON.stringify(err, null, 2));
// let skill tell the user that it couldn't find the data
sendResponse(context, callback, {
output: "the data could not be loaded from your database",
endSession: false
});
} else {
console.log('loaded data item:\n' + JSON.stringify(data.Item, null, 2));
// assuming the item has an attribute called "incident"..
sendResponse(context, callback, {
output: data.Item.incident,
endSession: false
});
}
});
};
function sendResponse(context, callback, responseOptions) {
if(typeof callback === 'undefined') {
context.succeed(buildResponse(responseOptions));
} else {
callback(null, buildResponse(responseOptions));
}
}
function buildResponse(options) {
var alexaResponse = {
version: "1.0",
response: {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.output}</prosody></speak>`
},
shouldEndSession: options.endSession
}
};
if (options.repromptText) {
alexaResponse.response.reprompt = {
outputSpeech: {
"type": "SSML",
"ssml": `<speak><prosody rate="slow">${options.reprompt}</prosody></speak>`
}
};
}
return alexaResponse;
}
exports.handler = (event, context, callback) => {
try {
var request = event.request;
if (request.type === "LaunchRequest") {
sendResponse(context, callback, {
output: "welcome to my skill. what data are you looking for?",
endSession: false
});
}
else if (request.type === "IntentRequest") {
let options = {};
if (request.intent.name === "GetMachineStateIntent") {
GetMachineStateIntent(context, callback);
} else if (request.intent.name === "AMAZON.StopIntent" || request.intent.name === "AMAZON.CancelIntent") {
sendResponse(context, callback, {
output: "ok. good bye!",
endSession: true
});
}
else if (request.intent.name === "AMAZON.HelpIntent") {
sendResponse(context, callback, {
output: "you can ask me about incidents that have happened",
reprompt: "what can I help you with?",
endSession: false
});
}
else {
sendResponse(context, callback, {
output: "I don't know that one! please try again!",
endSession: false
});
}
}
else if (request.type === "SessionEndedRequest") {
sendResponse(context, callback, ""); // no response needed
}
else {
// an unexpected request type received.. just say I don't know..
sendResponse(context, callback, {
output: "I don't know that one! please try again!",
endSession: false
});
}
} catch (e) {
// handle the error by logging it and sending back an failure
console.log('Unexpected error occurred in the skill handler!', e);
if(typeof callback === 'undefined') {
context.fail("Unexpected error");
} else {
callback("Unexpected error");
}
}
};

The short answer is no
In your interaction model you can supply the following built in slot types for your date and time slots:
built-in Date slot: https://developer.amazon.com/docs/custom-skills/slot-type-reference.html#date
built-in Time slot: https://developer.amazon.com/docs/custom-skills/slot-type-reference.html#time
The docs explain what type of utterances map to each.
For instance, you could create an interaction model where you set up an intent, let's call it GetMachineStateIntent and then map the following utterances to this model:
what was the machine state at {Time} on {Date}
what was the state of the machine at {Time} on {Date}
what was the machine state at {Time} {Date}
what was the state of the machine at {Time} {Date}
what was the machine state on {Date} at {Time}
what was the state of the machine on {Date} {Time}
what was the machine state {Date} at {Time}
what was the state of the machine {Date} {Time}
In your skill, you would handle the GetMachineStateIntent and in the request you will receive the filled in values for each of the two slots.
As a first step, while building the interaction model, it would be good to have Alexa simply respond back with speech confirming that it received the date and time slot values from your request.
For example, you might include something like:
if (request.type === "IntentRequest" && request.intent.name == "GetMachineStateIntent") {
var dateSlot = request.intent.slots.Date != null ?
request.intent.slots.Date.value : "unknown date";
var timeSlot = request.intent.slots.Time != null ?
request.intent.slots.Time.value : "unknown time";
// respond with speech saying back what the skill thinks the user requested
sendResponse(context, callback, {
output: "You wanted the machine state at "
+ timeSlot + " on " + dateSlot,
endSession: true
});
}

Related

Firebase: is it good enough to check if reference exists before start a transaction

I wonder if it is good enough to test if the reference exists
BEFORE I start a transaction on this reference?
e.g: by using .once('value') and snapshot.exists()
I mean if the check is outside the transaction isn't there a risk another user to delete the reference just after the check and before the transacton executor function?
==== edited to include the minimal complete code =====
here is my data in realtime database:
activeOffers
-LKohyZ58cnzn0vCnt9p
details
direction: "city"
seatsCount: 2
timeToGo: 5
uid: "-ABSIFJ0vCnt9p8387a" ---- offering user
And here is my code flow:
===== index.js =====
entries = require('./entries');
/// cloud function
exports.TEST_askOfferSeats = functions.https.onCall((data, context) => {
console.log('data: ' + JSON.stringify(data));
return entries.askSeats(data);
});
here is my test data sent by Postman:
{
"data":
{
"uid": "-FGKKSDFGK12387sddd", ---- the requesting/asking user
"id": "-LKpCACQlL25XTWJ0OV_",
"details":
{
"direction": "city",
"seatsCount": 1,
"timeToGo": 5
}
}
}
===== entries.js =======
exports.askSeats = function(data) {
const TAG = '[askSeats]: ';
var entryRef = db.ref('activeOffers/' + data.id);
return globals.exists(entryRef)
.then((found)=>{
if (found) {
return dealSeats(entryRef, data);
} else {
return 'Offer not found [' + data.id + ']';
}
});
}
===== globals.js ======
exports.exists = (ref)=>{
return ref.once('value')
.then((snapshot)=>{
return (snapshot.exists());
});
}
===== entries.js =====
dealSeats = function(entryRef, data) {
const TAG = '[dealSeats]: ';
return entryRef.transaction((entry)=>{
if (entry) {
if ((entry.deals) && (entry.deals[data.uid])) {
throw new Error('You've already made a deal.');
} else if (entry.details.seatsCount >= data.details.seatsCount) {
entry.details.seatsCount -= data.details.seatsCount;
var deal = [];
deal.status = 'asked';
deal.details = data.details;
if (!entry.deals) {
entry.deals = {};
}
entry.deals[data.uid] = deal;
} else {
throw new Error('Not enought seats.');
}
}
return entry;
})
.then((success)=>{
return success.snapshot.val();
})
.catch((error)=>{
return Promise.reject(error);
});
}
Btw: is this 'throw new Error(......)' is the correct way to break the transaction ?
========= updated with final source ===
Thanks to Doug Stevenson.
So here is my final source that is working fine. If someone sees a potential problem please let me know. Thanks.
dealSeats = function(entryRef, data) {
const TAG = '[dealSeats]: ';
var abortReason;
return entryRef.transaction((entry)=>{
if (entry) {
if ((entry.deals) && (entry.deals[data.uid])) {
abortReason = 'You already made a reservation';
return; // abort transaction
} else if (entry.details.seatsCount >= data.details.seatsCount) {
entry.details.seatsCount -= data.details.seatsCount;
var deal = [];
deal.status = 'asked';
deal.details = data.details;
if (!entry.deals) {
entry.deals = {};
}
entry.deals[data.uid] = deal;
// Reservation is made
} else {
abortReason = 'Not enought seats';
return; // abort transaction
}
}
return entry;
})
.then((result)=>{ // resolved
if (!result.committed) { // aborted
return abortReason;
} else {
let value = result.snapshot.val();
if (value) {
return value;
} else {
return 'Offer does not exists';
}
}
})
.catch((reason)=>{ // rejected
return Promise.reject(reason);
});
}
If you read a value before a transaction, then read it again inside the transaction, you have absolutely no guarantee that the second read inside the transaction will yield the same result as the initial read outside before the transaction. It could be modified by the time the transaction is performed.
If you want a truly atomic update, only check value that participate in the transaction within the transaction itself, and make a decision about what to do in the transaction handler.

firebase reset password controller

Yesterday my app was launched, Ionic v1, and a few users entered the wrong password and can't log into the app.
The app uses firebase authentication. I have a __refs file that points to the database and have tried numerous things trying to get the reset to work.
I've tried referencing $firebaseAuth, of course my __refs, $firebase then use $firebase.auth()...
I didn't write the authentication of this app so I'm not real sure how it works. I'm hoping that someone can help me.
My reset controller
angular.module('formulaWizard').controller('ResetPasswordCtrl',
function($scope, $ionicLoading, $firebaseAuth, __Refs) {
$scope.user = {
email: ''
};
$scope.errorMessage = null;
var fbAuth = $firebaseAuth(__Refs.rootRef);
$scope.resetPassword = function() {
$scope.errorMessage = null;
$ionicLoading.show({
template: 'Please wait...'
});
fbAuth.sendPasswordResetEmail($scope.user.email)
.then(showConfirmation)
.catch(handleError);
};
function showConfirmation() {
$scope.emailSent = true;
$ionicLoading.hide();
}
function handleError(error) {
switch (error.code) {
case 'INVALID_EMAIL':
case 'INVALID_USER':
$scope.errorMessage = 'Invalid email';
break;
default:
$scope.errorMessage = 'Error: [' + error.code + ']';
}
$ionicLoading.hide();
}
});
My Refs file
angular.module('formulaWizard')
.factory('__Refs', function ($firebaseArray, $firebaseObject) {
// Might use a resource here that returns a JSON arrayf
var ref = new Firebase('https://firebasedatabase.com/');
return {
rootRef: ref,
customers: ref.child('customers'),
}
});
I can't take credit for the answer it was provide by Abimbola Idowu on HackHands.
Since I paid for the answer I thought I would share it with anyone else that might also be stumped by this.
$scope.resetPassword = function() {
$scope.errorMessage = null;
$ionicLoading.show({
template: 'Please wait...'
});
__Refs.rootRef.resetPassword({ email: $scope.user.email }, function(error) {
if (error === null) {
showConfirmation();
} else {
handleError()
}
});
};
This is the __refs service
angular.module('formulaWizard')
.factory('__Refs', function ($firebaseArray, $firebaseObject) {
// Might use a resource here that returns a JSON arrayf
var ref = new Firebase('https://firebasedatabase.com/');
return {
rootRef: ref,
}
});

botkit middleware - How to use sendToWatson to update context?

I use https://github.com/watson-developer-cloud/botkit-middleware#implementing-app-actions as my reference.
The context in my conversation does not update.
Here is my bot-facebook.js.
function checkBalance(context, callback) {
var contextDelta = {
user_name: 'Henrietta',
fname: 'Pewdiepie'
};
callback(null, context);
}
var checkBalanceAsync = Promise.promisify(checkBalance);
var processWatsonResponse = function (bot, message) {
if (message.watsonError) {
console.log(message.watsonError);
return bot.reply(message, "I'm sorry, but for technical reasons I can't respond to your message");
}
if (typeof message.watsonData.output !== 'undefined') {
//send "Please wait" to users
bot.reply(message, message.watsonData.output.text.join('\n'));
if (message.watsonData.output.action === 'check_balance') {
var newMessage = clone(message);
newMessage.text = 'check new name';
checkBalanceAsync(message.watsonData.context).then(function (contextDelta) {
console.log("contextDelta: " + JSON.stringify(contextDelta));
return watsonMiddleware.sendToWatsonAsync(bot, newMessage, contextDelta);
}).catch(function (error) {
newMessage.watsonError = error;
}).then(function () {
return processWatsonResponse(bot, newMessage);
});
}
}
};
controller.on('message_received', processWatsonResponse);
The JSON editor of welcome node in my watson conversation.
{
"context": {
"fname": "",
"user_name": ""
},
"output": {
"text": {
"values": [
"Good day :) My name is Doug and I am a chatbot."
],
"selection_policy": "random"
},
"action": "check_balance"
}
}
I have tried multiple ways I could imagine.
Do I need to do something like fname: <?contextDelta.fname?> in the json editor?
You aren't checking context in your dialog.
Context object in JSON editor is used to store captured data in context,
so your node actually empties context variable.
Probably you need to remove that context initialization from your dialog,
To see value of context variable, you have to use it in the output
"Good day, $fname :) My name is Doug and I am a chatbot."

Server methods outside of Meteor.methods

On the client I need a helper method that returns true or false depending on whether the user is eligible for a payment request.
However, I can't really use a Meteor.method for this, because they don't return a value on the client.
Instead, I have done this and would like to know if this poses any security holes or if there is a preferable approach
Server:
...
// Constants
//
_.extend(Payments, {
MINIMUM_REQUIRED_FOR_REQUEST: 100
});
// Public
//
Meteor.methods({
});
canRequestPayment = function(userId) {
var user = Meteor.users.findOne(userId, { fields: { earnings: 1 } });
if (_.isUndefined(user)) { throw new Meteor.Error('user-not-found', 'User not found'); }
return hasEnoughCreditForRequest(user) && hasNoPendingPayments(user);
};
// Private
//
var hasNoPendingPayments = function(user) {
return Payments.find({ userId: user._id, state: 'pending' }).count() === 0;
};
var hasEnoughCreditForRequest = function(user) {
var period = user.earnings.period;
return period >= Payments.MINIMUM_REQUIRED_FOR_REQUEST;
};
As can be seen, I have created two helper methods with var, to mimic private behavior, and then I have the canRequestPayment method which is accessable outside of the file, and that I call on the client instead of a Meteor.method
Client:
Template.payments.helpers({
eligibleForPaymentRequest: function() {
return canRequestPayment(Meteor.userId());
},

Mongoose pre.save() async middleware not working on record creation

I am using keystone#0.2.32. I would like to change the post category to a tree structure. The below code is running well except when I create a category, it goes into a deadlock:
var keystone = require('keystone'),
Types = keystone.Field.Types;
/**
* PostCategory Model
* ==================
*/
var PostCategory = new keystone.List('PostCategory', {
autokey: { from: 'name', path: 'key', unique: true }
});
PostCategory.add({
name: { type: String, required: true },
parent: { type: Types.Relationship, ref: 'PostCategory' },
parentTree: { type: Types.Relationship, ref: 'PostCategory', many: true }
});
PostCategory.relationship({ ref: 'Post', path: 'categories' });
PostCategory.scanTree = function(item, obj, done) {
if(item.parent){
PostCategory.model.find().where('_id', item.parent).exec(function(err, cats) {
if(cats.length){
obj.parentTree.push(cats[0]);
PostCategory.scanTree(cats[0], obj, done);
}
});
}else{
done();
}
}
PostCategory.schema.pre('save', true, function (next, done) { //Parallel middleware, waiting done to be call
if (this.isModified('parent')) {
this.parentTree = [];
if(this.parent != null){
this.parentTree.push(this.parent);
PostCategory.scanTree(this, this, done);
}else
process.nextTick(done);
}else
process.nextTick(done); //here is deadlock.
next();
});
PostCategory.defaultColumns = 'name, parentTree';
PostCategory.register();
Thanks so much.
As I explained on the issue you logged on Keystone here: https://github.com/keystonejs/keystone/issues/759
This appears to be a reproducible bug in mongoose that prevents middleware from resolving when:
Parallel middleware runs that executes a query, followed by
Serial middleware runs that executes a query
Changing Keystone's autokey middleware to run in parallel mode may cause bugs in other use cases, so cannot be done. The answer is to implement your parentTree middleware in serial mode instead of parallel mode.
Also, some other things I noticed:
There is a bug in your middleware, where the first parent is added to the array twice.
The scanTree method would be better implemented as a method on the schama
You can use the findById method for a simpler parent query
The schema method looks like this:
PostCategory.schema.methods.addParents = function(target, done) {
if (this.parent) {
PostCategory.model.findById(this.parent, function(err, parent) {
if (parent) {
target.parentTree.push(parent.id);
parent.addParents(target, done);
}
});
} else {
done();
}
}
And the fixed middleware looks like this:
PostCategory.schema.pre('save', function(done) {
if (this.isModified('parent')) {
this.parentTree = [];
if (this.parent != null) {
PostCategory.scanTree(this, this, done);
} else {
process.nextTick(done);
}
} else {
process.nextTick(done);
}
});
I think it's a bug of keystone.js. I have changed schemaPlugins.js 104 line
from
this.schema.pre('save', function(next) {
to
this.schema.pre('save', true, function(next, done) {
and change from line 124 to the following,
// if has a value and is unmodified or fixed, don't update it
if ((!modified || autokey.fixed) && this.get(autokey.path)) {
process.nextTick(done);
return next();
}
var newKey = utils.slug(values.join(' ')) || this.id;
if (autokey.unique) {
r = getUniqueKey(this, newKey, done);
next();
return r;
} else {
this.set(autokey.path, newKey);
process.nextTick(done);
return next();
}
It works.

Resources