question

Lisa@HeyHon avatar image
Lisa@HeyHon asked

How can I read an attribute from DynamoDB and change my skill's response accordingly?

Hey, Alexa developers!

Here's my goal: In the intent handler below, I am trying to add a question, "Were you happy with this skill today?" if the user has not already answered the question before.

How will I know a user has already answered the question? When a user answers the question, I create an item in DynamoDB. The key is their userId ("UserId"). Their response is a boolean ("happy").Though I have not shown the code here, this much is working.

In short: I will know a user has answered the question if there is a DynamoDB item with their userId and "happy" is true or false.

The problem: The response is not changing. That is, even if item does not exist in DynamoDB, or if "happy" is empty, the question is not added to var speechText.

My code for the entire intent is below. Am I getting an item from DynamoDB incorrectly? Using the wrong conditions in my if/else statement? Any ideas would be deeply appreciated.

const DoItIntentHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'IntentRequest' && request.intent.name === 'DoItIntent';
    },
    handle(handlerInput) {
            attributes.FromDoItIntent = true;
            handlerInput.attributesManager.setSessionAttributes(attributes);
            const praise = getRandomItem(praises);
            const factResults = getRandomFact(facts, factTitles, longFacts);
            const fact = factResults[0];
            var factTitle = factResults[1];
            const longFact = factResults[2];
            var speechText = praise + ' Interestingly, ' + fact + ' <break time="0.3s"/>';
            const userId = handlerInput.requestEnvelope.session.user.userId;
            
            
            const params = {
                TableName: 'HeyHonFeedback',
                    Key: {
                        'UserId': userId,  
                    }  
                };


            docClient.get(params, function (err,data) {
                if (err) {
                    console.log('Error', err);
                } else if (!data["Item"]["happy"]) {
                    speechText += 'Were you happy with this skill today?';
                }
            });  
 
            
            return handlerInput.responseBuilder
                .speak(speechText)
                .withSimpleCard(factTitle, longFact)
                .withShouldEndSession(false)
                .getResponse(); 
        
        }
    }
};

alexa skills kitdynamodb
10 |3000 characters needed characters left characters exceeded

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Rokas avatar image
Rokas answered

In your case, I would have rather used persistence attributes. I recommend reading more about them. It is much more easier to work with them and they are used for these exact situations: to save user session data and use next time he uses the skill.

Talking about this issue, most of the aws-sdk clients return Request from their functions and invoke callback function on completion. In your case I recommend promisifying this function call and awaiting its results, like this:

 try {
    const data = await docClient.get(params).promise();
  } catch (e) {
    console.log(e);
  }
if (!data["Item"]["happy"]) {
    speechText += 'Were you happy with this skill today?'
}

In this case you will also need to add word async before 'handle'.

async handle(handlerInput) {
                

I haven't tested it, but I hope it works.

1 comment
10 |3000 characters needed characters left characters exceeded

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Thanks for your help, Rokas!

0 Likes 0 ·
Albebach avatar image
Albebach answered

Lisa, I'm a python guy, so forgive me if I can't be specific, but what I would imagine seeing is this intent that would give praise and then ask "Were you happy with this skill today?" returning that response -- then a separate couple of intents: the yesIntent and noIntent (AMAZON.Yes, AMAZON.no, respectively) that would handle a yes or no answer to that question. In those intent handlers, I'd expect to see the associated boolean stored in Dynamo based on their answer. The intent would want to check the session attributes to make sure that the "yes" or "no" was coming FromDoItIntent, of course. If so, the database could be updated, and a response such as "I'm glad you're happy, that makes me happy." or "I'm sorry that I've failed you." can be returned. I hope that helps.

1 comment
10 |3000 characters needed characters left characters exceeded

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Hi, Albebach—thanks so much for your input! You're right that there are two intents after the one I posted: AMAZON.YesIntent and AMAZON.NoIntent. In the handlers for these intents, I write the userId, profile email, and appropriate boolean to DynamoDB. The skill's response changes accordingly: "I'm so glad to hear that!" or "I'm sorry to hear that."

The challenge is—and I may not have expressed this clearly—if someone uses the skill several times, I'd hate for them to hear the survey each time. That just seems like bad UX.

What I'm hoping to do in the intent handler above:

1. Retrieve user's userId from the requestEnvelope - done
2. Get item from DynamoDB whose key "UserId" matches the userId from step 1
3. If the item does not exist, or if there is no value for "happy", then add survey to response.
4. Else, if "happy" is either true or false, do not add survey to response.

0 Likes 0 ·