viernes, 8 de noviembre de 2019

Amazon Alexa

Amazon Alexa

1. first test

The skill is created with Custom and Alexa-Hosted options. the other way could be with an aws account on AWS console. using lambdas.







Testing

2. Second test Collect slots turn-by-turn




Code on

The sample on documents say

const speakOutput = `Thanks, I'll remember that you were born ${month} ${day} ${year}.`;

but don't work that way, after debuging find that it has to be changed by:

const CaptureBirthdayIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'CaptureBirthdayIntent';
    },
    handle(handlerInput) {
        const year = handlerInput.requestEnvelope.request.intent.slots.year.value;
        const month = handlerInput.requestEnvelope.request.intent.slots.month.value;
        const day = handlerInput.requestEnvelope.request.intent.slots.day.value;
        const speakOutput = `Thanks, I'll remember that you were born .`;
       
        return handlerInput.responseBuilder
            .speak(speakOutput.concat(`${month} ${day} ${year}`))
            //.reprompt('add a reprompt if you want to keep the session open for the user to respond')
            .getResponse();
    }
};


note: it seem that it is not totaly necesary, after other testing rounds, the intent has to been called a second time to respond like it suppouse to be.

Testing


3. Persisting data on S3

change the code of the intent cake walk like this as the documentation say

   // This sample demonstrates handling intents from an Alexa skill using the Alexa Skills Kit SDK (v2).
// Please visit https://alexa.design/cookbook for additional examples on implementing slots, dialog management,
// session persistence, api calls, and more.
const Alexa = require('ask-sdk-core');
const persistenceAdapter = require('ask-sdk-s3-persistence-adapter');

const LaunchRequestHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
    },
    handle(handlerInput) {
        const speakOutput = 'Hello! I am Cakewalk. What is your birthday?';
        const repromptText = 'I was born Nov. 6th, 2014. When were you born?';
        return handlerInput.responseBuilder
            .speak(speakOutput)
            .reprompt(repromptText)
            .getResponse();
    }
};

const HasBirthdayLaunchRequestHandler = {
    canHandle(handlerInput) {

        const attributesManager = handlerInput.attributesManager;
        const sessionAttributes = attributesManager.getSessionAttributes() || {};

        const year = sessionAttributes.hasOwnProperty('year') ? sessionAttributes.year : 0;
        const month = sessionAttributes.hasOwnProperty('month') ? sessionAttributes.month : 0;
        const day = sessionAttributes.hasOwnProperty('day') ? sessionAttributes.day : 0;

        return handlerInput.requestEnvelope.request.type === 'LaunchRequest' && year && month && day;

    },
    handle(handlerInput) {

        const attributesManager = handlerInput.attributesManager;
        const sessionAttributes = attributesManager.getSessionAttributes() || {};

        const year = sessionAttributes.hasOwnProperty('year') ? sessionAttributes.year : 0;
        const month = sessionAttributes.hasOwnProperty('month') ? sessionAttributes.month : 0;
        const day = sessionAttributes.hasOwnProperty('day') ? sessionAttributes.day : 0;

        // TODO:: Use the settings API to get current date and then compute how many days until user's birthday
        // TODO:: Say Happy birthday on the user's birthday

        const speakOutput = `Welcome back. It looks like there are X more days until your y-th birthday.`;

        return handlerInput.responseBuilder
            .speak(speakOutput)
            .getResponse();
    }
};

const CaptureBirthdayIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'CaptureBirthdayIntent';
    },
    async handle(handlerInput) {
        const year = handlerInput.requestEnvelope.request.intent.slots.year.value;
        const month = handlerInput.requestEnvelope.request.intent.slots.month.value;
        const day = handlerInput.requestEnvelope.request.intent.slots.day.value;
        const attributesManager = handlerInput.attributesManager;
        const birthdayAttributes = {
        "year" : year,
        "month" : month,
        "day" : day
        };
        attributesManager.setPersistentAttributes(birthdayAttributes);

        await attributesManager.savePersistentAttributes();
       
        const speakOutput = `Thanks, I'll remember that you were born .${month} ${day} ${year}`;
       
        return handlerInput.responseBuilder
            //.speak(speakOutput.concat(`${month} ${day} ${year}`))
            .speak(speakOutput)
            //.reprompt('add a reprompt if you want to keep the session open for the user to respond')
            .getResponse();
    }
};
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();
    }
};

const LoadBirthdayInterceptor = {
    async process(handlerInput) {
        const attributesManager = handlerInput.attributesManager;
        const sessionAttributes = await attributesManager.getPersistentAttributes() || {};

        const year = sessionAttributes.hasOwnProperty('year') ? sessionAttributes.year : 0;
        const month = sessionAttributes.hasOwnProperty('month') ? sessionAttributes.month : 0;
        const day = sessionAttributes.hasOwnProperty('day') ? sessionAttributes.day : 0;

        if (year && month && day) {
            attributesManager.setSessionAttributes(sessionAttributes);
        }
    }
};

// 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(
        HasBirthdayLaunchRequestHandler,
        LaunchRequestHandler,
        CaptureBirthdayIntentHandler,
        HelpIntentHandler,
        CancelAndStopIntentHandler,
        SessionEndedRequestHandler,
        IntentReflectorHandler, // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
    )
    .withPersistenceAdapter(
      new persistenceAdapter.S3PersistenceAdapter({bucketName:process.env.S3_PERSISTENCE_BUCKET})
    )
    .addRequestInterceptors(
         LoadBirthdayInterceptor
    )
    .addErrorHandlers(
        ErrorHandler,
    )
    .lambda();


Testing



3.1 Deleteing Persisted data.





Correction:  has been capture the day as "two" when it has to be "second" , that why on year appears like 21970. correcting and seen the data in S3, we have:







Notes:

https://github.com/alexa/skill-sample-nodejs-first-skill

Crear un  layer en AWS console o alternativamente usar Alexa pero no contara con todas las posibilidades que se dan en AWS console

En AWS console add trigger Alexa Skills Kit. se habilita y se a add. ya tendriamos el skill y el trigger con el evento.

ARN amazon resourse name

al crear el skill teemos la opcion de Custom -> Provision your own (para AWS console) o Alexa hosted node.js

en module-1 copiar el json y index a donde depositemos el repo cloud en alexa

D:\temp\alexasdk>npm i --save ask-sdk
npm WARN saveError ENOENT: no such file or directory, open 'D:\temp\alexasdk\package.json'
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN enoent ENOENT: no such file or directory, open 'D:\temp\alexasdk\package.json'
npm WARN alexasdk No description
npm WARN alexasdk No repository field.
npm WARN alexasdk No README data
npm WARN alexasdk No license field.

+ ask-sdk@2.7.0
added 35 packages from 80 contributors and audited 52 packages in 13.635s
found 0 vulnerabilities


D:\temp\alexasdk>ls
"ls" no se reconoce como un comando interno o externo,
programa o archivo por lotes ejecutable.

D:\temp\alexasdk>dir
 El volumen de la unidad D no tiene etiqueta.
 El número de serie del volumen es: 10FB-BD7D

 Directorio de D:\temp\alexasdk

08/11/2019  10:05 a. m.    <DIR>          .
08/11/2019  10:05 a. m.    <DIR>          ..
08/11/2019  10:05 a. m.    <DIR>          node_modules
08/11/2019  10:05 a. m.            10,482 package-lock.json
               1 archivos         10,482 bytes
               3 dirs  317,245,050,880 bytes libres

D:\temp\alexasdk>mkdir nodejs


Instalar adaptador de pesisntencia
npm i --save ask-sdk-s3-persistence
D:\temp\alexasdk>npm i --save ask-sdk-s3-persistence-adapter
npm WARN saveError ENOENT: no such file or directory, open 'D:\temp\alexasdk\package.json'
npm WARN enoent ENOENT: no such file or directory, open 'D:\temp\alexasdk\package.json'
npm WARN alexasdk No description
npm WARN alexasdk No repository field.
npm WARN alexasdk No README data
npm WARN alexasdk No license field.

+ ask-sdk-s3-persistence-adapter@2.7.0
added 1 package from 2 contributors and audited 358 packages in 0.998s
found 0 vulnerabilities

No hay comentarios:

Publicar un comentario