Skip to content

Instantly share code, notes, and snippets.

@softwarechido
Last active May 28, 2019 06:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save softwarechido/0b2f79c0e2c34846422dd43531a6294c to your computer and use it in GitHub Desktop.
Save softwarechido/0b2f79c0e2c34846422dd43531a6294c to your computer and use it in GitHub Desktop.
// lambda
// Este ejemplo es una demostración de como manejar intents en una skill de Alexa utilziando el Alexa Skills Kit SDK (v2)
const Alexa = require('ask-sdk-core');
const i18n = require('i18next');
const sprintf = require('i18next-sprintf-postprocessor');
// Lista de Strings para el lenguaje esmxData
const esmxData = {
translation: {
SKILL_NAME: 'Nutrición Inteligente',
WELCOME_MESSAGE: 'Hola! bienvenido a Nutrición Inteligente. ¿Cómo te puedo ayudar?',
LAUNCH_REPROMPT: '¿Cómo te puedo ayudar?',
HELLOWORLD_MESSAGE: 'Hola mundo',
HELP_MESSAGE: 'Me puedes decir dame un consejo o calcula mi peso ideal. ¿Cómo te puedo ayudar?',
HELP_REPROMPT: 'Cómo te puedo ayudar?',
ADVICE_REPROMPT: '¿Cómo te puedo ayudar?',
IDEAL_WEIGHT_MALE: 'tu peso ideal si eres hombre es ',
IDEAL_WEIGHT_FEMALE: 'tu peso ideal si eres mujer es ',
IDEAL_WEIGHT_REPROMPT: '<break time="1s"/>Ahora, ¿Cómo te puedo ayudar?',
ERROR_MESSAGE: 'Perdona, ha ocurrido un error.',
STOP_MESSAGE: 'Adios!',
ADVICES_LIST:
[
'Aliméntate de forma balanceada entre carbohidratos, grasas y proteínas',
'Divide tus calorías en tres comidas, por ejemplo si tu plan es comer 1800 calorías, distribuye 600 en cada comida',
'Procura alimentos de bajo índice glucémico, es decir, alimentos que no suban tus niveles de azucar en la sangre'
]
}
};
// Lenguajes Soportados
const languageStrings = {
'es-MX': esmxData,
'es-US': esmxData,
'es-ES': esmxData
};
const LocalizationInterceptor = {
process(handlerInput) {
// Obtiene el local del request e inicializa i18next
const localizationClient = i18n.use(sprintf).init({
lng: handlerInput.requestEnvelope.request.locale,
resources: languageStrings,
});
// Crea una función de localización para soportar los argumentos
localizationClient.localize = function localize() {
// Toma los argumentos y los pasa a i18next usando sprintf para
//reemplazar los strings
const args = arguments;
const values = [];
for (let i = 1; i < args.length; i += 1) {
values.push(args[i]);
}
const value = i18n.t(args[0], {
returnObjects: true,
postProcess: 'sprintf',
sprintf: values,
});
// Si hay un arreglo entonces usa un valor random
if (Array.isArray(value)) {
return value[Math.floor(Math.random() * value.length)];
}
return value;
};
// Toma los atributos del request y guarda la funcion localize dentro
// para ser utilizado en un handler llamando requestAttributes.t(STRING_ID, [args...])
const attributes = handlerInput.attributesManager.getRequestAttributes();
attributes.t = function translate(...args) {
return localizationClient.localize(...args);
};
},
};
const LaunchRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
},
handle(handlerInput) {
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();
const speechText = requestAttributes.t('WELCOME_MESSAGE');
return handlerInput.responseBuilder
.speak(speechText)
.withSimpleCard(requestAttributes.t('SKILL_NAME'))
.reprompt('LAUNCH_REPROMPT')
.addDirective({
type: 'Alexa.Presentation.APL.RenderDocument',
version: '1.0',
document: require('./welcome.json'),
datasources: {}
})
.getResponse();
}
};
const HolaMundoIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'HolaMundoIntent';
},
handle(handlerInput) {
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();
const speechText = requestAttributes.t('HELLOWORLD_MESSAGE');
return handlerInput.responseBuilder
.speak(speechText)
//.reprompt('agrega un texto de reprompt si deseas dejar la sesión abierta para que el usuario responda. No olvide cerrar con una pregunta. ¿Cómo te puedo ayudar?')
.getResponse();
}
};
const ConsejoIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'ConsejoIntent';
},
handle(handlerInput) {
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();
// esto ya no es necesario porque el i18 regresa un valor aleatorio del arreglo ADVICES_LIST
// const listaConsejos = requestAttributes.t('ADVICES_LIST');
// const indiceAleatorio = Math.floor(Math.random() * listaConsejos.length);
// const consejoAleatorio = listaConsejos[indiceAleatorio];
const consejoAleatorio = requestAttributes.t('ADVICES_LIST');
const speechOutput = consejoAleatorio;
const adviceReprompt = requestAttributes.t('ADVICE_REPROMPT');
return handlerInput.responseBuilder
.speak(`${speechOutput} ${adviceReprompt}`)
.reprompt(adviceReprompt)
.addDirective({
type: 'Alexa.Presentation.APL.RenderDocument',
version: '1.0',
document: require('./main.json'),
datasources: {
"bodyTemplate7Data": {
"type": "object",
"objectId": "bt7Sample",
"title": "Consejo",
"image": {
"sources": [
{
"url": "https://s3.amazonaws.com/nutricioninteligente/screen4.png",
"size": "small",
"widthPixels": 0,
"heightPixels": 0
},
{
"url": "https://s3.amazonaws.com/nutricioninteligente/screen4.png",
"size": "large",
"widthPixels": 0,
"heightPixels": 0
}
]
},
"logoUrl": "",
"hintText": "Try, \"Alexa, dime mi peso ideal\""
}
}
})
.getResponse();
}
};
const PesoIdealIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'PesoIdealIntent';
},
handle(handlerInput) {
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();
const request = handlerInput.requestEnvelope.request;
var estatura = request.intent.slots.estatura.value;
var pesoidealHombre = (estatura-100)-((estatura-150)/4);
var pesoidealMujer = (estatura-100)-((estatura-150)/2);
const speechOutput = ` ${requestAttributes.t('IDEAL_WEIGHT_MALE')} ${pesoidealHombre}. ${requestAttributes.t('IDEAL_WEIGHT_FEMALE')} ${pesoidealMujer}`;
const pesoIdealReprompt = requestAttributes.t('IDEAL_WEIGHT_REPROMPT');
return handlerInput.responseBuilder
.speak(`${speechOutput} ${pesoIdealReprompt}`)
.reprompt(pesoIdealReprompt)
.addDirective({
type: 'Alexa.Presentation.APL.RenderDocument',
version: '1.0',
document: require('./main.json'),
datasources: {
"bodyTemplate7Data": {
"type": "object",
"objectId": "bt7Sample",
"title": "Peso ideal",
"image": {
"sources": [
{
"url": "https://s3.amazonaws.com/nutricioninteligente/screen1.png",
"size": "small",
"widthPixels": 0,
"heightPixels": 0
},
{
"url": "https://s3.amazonaws.com/nutricioninteligente/screen1.png",
"size": "large",
"widthPixels": 0,
"heightPixels": 0
}
]
},
"logoUrl": "",
"hintText": "Try, \"Alexa, dime mi peso ideal\""
}
}
})
.getResponse();
}
};
const HelpIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();
const speechText = requestAttributes.t('HELP_MESSAGE');
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.getResponse();
}
};
const CancelAndStopIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'
|| handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent');
},
handle(handlerInput) {
const requestAttributes = handlerInput.attributesManager.getRequestAttributes();
const request = handlerInput.requestEnvelope.request;
const speechText = requestAttributes.t('STOP_MESSAGE');
return handlerInput.responseBuilder
.speak(speechText)
.getResponse();
}
};
const SessionEndedRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
},
handle(handlerInput) {
// Any cleanup logic goes here.
return handlerInput.responseBuilder.getResponse();
}
};
// Este handler para que hagas tus pruebas y debugging.
// Simplemente repite el intent que dijo el usuario.
const IntentReflectorHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest';
},
handle(handlerInput) {
const intentName = handlerInput.requestEnvelope.request.intent.name;
const speechText = `Lanzaste el intent que se llama ${intentName}`;
return handlerInput.responseBuilder
.speak(speechText)
//.reprompt('agrega un texto de reprompt si deseas dejar la sesión abierta para que el usuario responda. No olvide cerrar con una pregunta. ¿Cómo te puedo ayudar?')
.getResponse();
}
};
// Error genério para capturar erroes de sintaxis o de enrutamiento.
// Si recibes un error, eso significa que no hay un handler que regrese "true" para el método canHandle()
const ErrorHandler = {
canHandle() {
return true;
},
handle(handlerInput, error) {
console.log(`~~~~ Error handled: ${error.message}`);
const speechText = `Lo siento, No puedo entender lo que has dicho. Por favor inténtalo de nuevo`;
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.getResponse();
}
};
// Este handler funciona como el punto de entrada de tu skill, se encarga de enrutar
// todas las peticiones.
// Asegúrate de tener todos los handlers e interceptores dado de alta aquí.
// El órden importa, son procesador de arriba hacia abajo.
exports.handler = Alexa.SkillBuilders.custom()
.addRequestHandlers(
LaunchRequestHandler,
HolaMundoIntentHandler,
ConsejoIntentHandler,
PesoIdealIntentHandler,
HelpIntentHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler,
IntentReflectorHandler) // Asegúrate de que el IntentReflector sea el último para evitar que maneje peticiones incorrectas
.addErrorHandlers(
ErrorHandler)
.addRequestInterceptors(
LocalizationInterceptor
)
.lambda();
{
"type": "APL",
"version": "1.0",
"theme": "dark",
"import": [
{
"name": "alexa-layouts",
"version": "1.0.0"
}
],
"resources": [
{
"description": "Stock color for the light theme",
"colors": {
"colorTextPrimary": "#151920"
}
},
{
"description": "Stock color for the dark theme",
"when": "${viewport.theme == 'dark'}",
"colors": {
"colorTextPrimary": "#f0f1ef"
}
},
{
"description": "Standard font sizes",
"dimensions": {
"textSizeBody": 48,
"textSizePrimary": 27,
"textSizeSecondary": 23,
"textSizeSecondaryHint": 25
}
},
{
"description": "Common spacing values",
"dimensions": {
"spacingThin": 6,
"spacingSmall": 12,
"spacingMedium": 24,
"spacingLarge": 48,
"spacingExtraLarge": 72
}
},
{
"description": "Common margins and padding",
"dimensions": {
"marginTop": 40,
"marginLeft": 60,
"marginRight": 60,
"marginBottom": 40
}
}
],
"styles": {
"textStyleBase": {
"description": "Base font description; set color",
"values": [
{
"color": "@colorTextPrimary"
}
]
},
"textStyleBase0": {
"description": "Thin version of basic font",
"extend": "textStyleBase",
"values": {
"fontWeight": "100"
}
},
"textStyleBase1": {
"description": "Light version of basic font",
"extend": "textStyleBase",
"values": {
"fontWeight": "300"
}
},
"mixinBody": {
"values": {
"fontSize": "@textSizeBody"
}
},
"mixinPrimary": {
"values": {
"fontSize": "@textSizePrimary"
}
},
"mixinSecondary": {
"values": {
"fontSize": "@textSizeSecondary"
}
},
"textStylePrimary": {
"extend": [
"textStyleBase1",
"mixinPrimary"
]
},
"textStyleSecondary": {
"extend": [
"textStyleBase0",
"mixinSecondary"
]
},
"textStyleBody": {
"extend": [
"textStyleBase1",
"mixinBody"
]
},
"textStyleSecondaryHint": {
"values": {
"fontFamily": "Bookerly",
"fontStyle": "italic",
"fontSize": "@textSizeSecondaryHint",
"color": "@colorTextPrimary"
}
}
},
"layouts": {},
"mainTemplate": {
"parameters": [
"payload"
],
"items": [
{
"when": "${viewport.shape == 'round'}",
"type": "Container",
"direction": "column",
"items": [
{
"type": "Image",
"source": "${payload.bodyTemplate7Data.backgroundImage.sources[0].url}",
"scale": "best-fill",
"position": "absolute",
"width": "100vw",
"height": "100vh"
},
{
"type": "AlexaHeader",
"headerTitle": "${payload.bodyTemplate7Data.title}",
"headerAttributionImage": "${payload.bodyTemplate7Data.logoUrl}"
},
{
"type": "Container",
"grow": 1,
"alignItems": "center",
"justifyContent": "center",
"items": [
{
"type": "Image",
"source": "${payload.bodyTemplate7Data.image.sources[0].url}",
"scale": "best-fill",
"width": "100vh",
"height": "70vw",
"align": "center"
}
]
}
]
},
{
"type": "Container",
"items": [
{
"type": "Image",
"source": "${payload.bodyTemplate7Data.backgroundImage.sources[0].url}",
"scale": "best-fill",
"position": "absolute",
"width": "100vw",
"height": "100vh"
},
{
"type": "AlexaHeader",
"headerTitle": "${payload.bodyTemplate7Data.title}",
"headerAttributionImage": "${payload.bodyTemplate7Data.logoUrl}"
},
{
"type": "Container",
"direction": "row",
"paddingLeft": "5vw",
"paddingRight": "5vw",
"paddingBottom": "5vh",
"alignItems": "center",
"justifyContent": "center",
"items": [
{
"type": "Image",
"height": "75vh",
"width": "90vw",
"source": "${payload.bodyTemplate7Data.image.sources[0].url}",
"scale": "best-fill",
"align": "center"
}
]
}
]
}
]
}
}
{
"interactionModel": {
"languageModel": {
"invocationName": "nutrición inteligente",
"intents": [
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "HolaMundoIntent",
"slots": [],
"samples": [
"hola",
"cómo estás",
"di hola mundo",
"di hola",
"comenta hola mundo",
"comenta hola"
]
},
{
"name": "AMAZON.NavigateHomeIntent",
"samples": []
},
{
"name": "ConsejoIntent",
"slots": [],
"samples": [
"que me de un consejo",
"consejo",
"quiero un consejo",
"dame un consejo",
"un consejo"
]
},
{
"name": "PesoIdealIntent",
"slots": [
{
"name": "estatura",
"type": "AMAZON.NUMBER",
"samples": [
"mido {estatura}",
"{estatura}"
]
}
],
"samples": [
"dame mi peso ideal",
"calcúla mi peso ideal",
"mi peso ideal",
"peso ideal",
"quiero saber mi peso ideal",
"cuanto debo de pesar",
"dime mi peso ideal",
"cual es mi peso ideal"
]
}
],
"types": []
},
"dialog": {
"intents": [
{
"name": "PesoIdealIntent",
"confirmationRequired": false,
"prompts": {},
"slots": [
{
"name": "estatura",
"type": "AMAZON.NUMBER",
"confirmationRequired": false,
"elicitationRequired": true,
"prompts": {
"elicitation": "Elicit.Slot.666209592402.794048786262"
}
}
]
}
],
"delegationStrategy": "ALWAYS"
},
"prompts": [
{
"id": "Elicit.Slot.666209592402.794048786262",
"variations": [
{
"type": "PlainText",
"value": "¿cuánto mides en centimetros?"
}
]
}
]
}
}
"type": "APL",
"version": "1.0",
"theme": "dark",
"import": [],
"resources": [],
"styles": {},
"layouts": {},
"mainTemplate": {
"items": [
{
"type": "Container",
"height": "100vh",
"width": "100vw",
"direction": "column",
"alignItems": "center",
"justifyContent": "center",
"items": [
{
"type": "Text",
"text": "Nutrición Inteligente"
},
{
"type": "Text",
"text": "¡Bienvenidos!"
}
]
}
]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment