Cómo desarrollar una skill para alexa

¡Buenas!

si no lo sabeis, os informo: Los de amazon han publicado la primera skill para alexa. Es una habilidad chorra, pero ahí está. el caso es que programarla fue sencillo. solo tuve que seguir unas instrucciones básicas y no tuve que escribir nada de código. Y ahora tengo otra en proceso de certificación. en esta sí he escrito código y ha sido un coñazo, porque casi no hay material en español y me ha tocado leer la documentación en el idioma de Shakespeare.

Como soy una persona olvidadiza, he decidido publicar esto explicando cómo configurar el entorno de desarrollo y por qué no, un código de ejemplo para que lo useis como punto de partida. al finalizar, os dejaré el enlace a un repositorio de un tal Javier Campos (abre en una nueva ventana), que tiene bastante material en castellano para que podais ampliar.

Vamos al lío.

Gatolandia

la habilidad que vamos a hacer es una tontería simplísima. Pero por algo se empieza, ¿no? No está publicada, si alguien se anima puede hacerlo por las risas y dejarlo en los comentarios.

La idea es pedirle a alexa un dato sobre gatos y que nos suelte alguna curiosidad. ¿Por qué gatos? Pues no sé, porque es lo primero que se me ha ocurrido, tú puedes hacerla sobre botijos si te apetece. De hecho, sería gracioso. Al lío.

Consiguiendo lo necesario y configurando el entorno

Una habilidad de Amazon consta de 2 partes:

  • el modelo. Es lo que gestiona (por explicarlo de forma rápida) lo que le dices a la habilidad.
  • El endpoint. Es el lugar donde va la lógica de la habilidad. La skill lo llamará cuando reciba un comando. Se puede alojar en nuestro propio servidor, pero por comodidad y para evitarnos problemas, lo haremos en AWS Lambda que es gratis al menos que tu skill se haga super famosa, así no nos complicamos.

Obteniendo lo necesario.

  • Lo primero que tenemos que hacer es registrarnos en el programa de desarrolladores de amazon. Ve a https://developer.amazon.com/ (abre en una nueva ventana) y sigue las instrucciones.
  • En segundo lugar tenemos que registrarnos en AWS. Ve a https://aws.amazon.com (abre en una nueva ventana) y regístrate. No te asustes, AWS es inmenso como el océano, pero es cuestión de pillarle el punto.
  • Siguiendo con las cosas que necesitamos, tenemos que descargar e instalar Node JS (abre en una nueva ventana).
  • Y para finalizar, necesitamos Alexa Skills Kit Command Line Interface (ASK CLI). Para ello, en una terminal tecleamos el comando (sin comillas) «npm install -g ask-cli» (en mac o linux puede requerir sudo).

Configurando el entorno de desarrollo

Ya tenemos todo lo que necesitamos. solo nos queda configurar el entorno de desarrollo y cuando terminemos podremos ponernos a programar. Para ello tenemos que seguir los siguientes pasos:

  1. Lo primero es configurar las claves para que ASK pueda acceder a AWS lambda. Nos dirigimos a https://console.aws.amazon.com/iam/home e iniciamos sesión con nuestros datos de amazon AWS.
  2. A continuación pulsamos en políticas y luego en el botón crear política.
  3. Pulsamos en json y pegamos lo siguiente:

    {
    "Version": "2012-10-17",
    "Statement": {
    "Effect": "Allow",
    "Action": [
    "iam:CreateRole",
    "iam:GetRole",
    "iam:AttachRolePolicy",
    "iam:PassRole",
    "lambda:AddPermission",
    "lambda:CreateFunction",
    "lambda:GetFunction",
    "lambda:UpdateFunctionCode",
    "lambda:ListFunctions",
    "logs:FilterLogEvents",
    "logs:getLogEvents",
    "logs:describeLogStreams"
    ],
    "Resource": "*"
    }
    }
  4. Pulsamos en revisar política y rellenamos: Un nombre descriptivo y una descripción. No necesitamos más. Pulsamos en crear política.
  5. Ahora nos dirigimos a usuarios. allí pulsamos el botón añadir usuarios y rellenamos los campos. Tenemos que marcar la casilla «Acceso mediante programación». Hecho esto pulsamos en siguiente.
  6. Seleccionamos la casilla para añadir el usuario a un grupo y pulsamos en crear grupo. Al grupo le asignamos la política que creamos anteriormente. Cuando el grupo esté creado y seleccionado, pulsamos en siguiente.
  7. Volvemos a pulsar en siguiente, revisar. si todo va bien, pulsamos en crear usuario.
  8. Importante: Pulsa en la opción para guardar las claves en formato csv. Es importantísimo que no pierdas este archivo. No son recuperables desde la web, y si pierdes dichas claves tendrás que eliminarlas y volver a crearlas.
  9. Ya estamos terminando. Con las claves a buen recaudo, es hora de terminar de configurar ask. Para ello en una consola teclearemos ask init y seguiremos las instrucciones. Si todo va bien, tendremos todo configurado y funcionando.

Creando la habilidad.

Es momento (por fin) de crear la habilidad gatolandia. Para ello partiremos de la plantilla «Hello world», que nos da casi todo hecho. Teclearemos en una consola el comando ask new y seguiremos las instrucciones. Importante: si usas lector de pantallas, a la hora de seleccionar opciones tendrás que hacerlo con flechas arriba y abajo. Con NVDA, usando la revisión de pantalla verás cuál está seleccionada porque aparecerá con signos de mayor qué. Aquí un ejemplo:

>ask new
? Please select the runtime Node.js V8
? List of templates you can choose Hello World
? Please type in your skill name: gatolandia
Skill "gatolandia" has been created based on the chosen template

Es posible que en Windows nos arroje un error relacionado con scripts deshabilitados, si no en este punto cuando despleguemos la habilidad con ask deploy: «No se puede cargar el archivo…» Para arreglarlo abriremos un power shell como administrador y teclearemos el comando «Set-ExecutionPolicy -Scope CurrentUser» (sin las comillas). Una vez hecho esto podremos continuar (solo hay que repetir el ask new o ask deploy).
Lo primero que tenemos que hacer es cambiar el nombre de la habilidad, los datos de autor y las frases de ejemplo que se usarán para abrirla. En el directorio Gatolandia que se nos ha creado encontraremos un archivo llamado skill.json. Lo editamos, recomendable con Notepad++ si estamos en windows y cambiamos su contenido por lo siguiente.

{
"manifest": {
"publishingInformation": {
"locales": {
"es-ES": {
"summary": "Una habilidad que dará información sobre gatos.",
"examplePhrases": [
"me gustan los gatos",
"gatos",
"ayuda"
],
"name": "gatolandia",
"description": "Esta habilidad dirá una curiosidad sobre gatos cuando se la pidamos."
}
},
"isAvailableWorldwide": true,
"testingInstructions": "alexa abre gatolandia.",
"category": "KNOWLEDGE_AND_TRIVIA",
"distributionCountries": []
},
"apis": {
"custom": {
"endpoint": {
"sourceDir": "lambda/custom",
"uri": "ask-custom-gatolandia-default"
}
}
},
"manifestVersion": "1.0"
}
}

como expliqué antes, el módulo gestiona las frases que alexa entenderá. La plantilla que estamos usando está en inglés, y usa un módulo en dicho idioma. Así que lo primero que tenemos que hacer es irnos al directorio Gatolandia que se nos ha creado (debemos estar ya allí), y allí al directorio models. Encontraremos un archivo llamado en-US.json. Tenemos que renombrarlo a es-ES.json (es_AR.json si estás en argentina, es_MX.json si estás en México, es.US.json si estás en Estados Unidos y quieres que tu habilidad hable español…).
A continuación lo abriremos, (recomendable usar Notepad++) y lo modificamos para que quede algo así:

{
"interactionModel": {
"languageModel": {
"invocationName": "gatolandia",
"intents": [
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "GatolandiaIntent",
"slots": [],
"samples": [
"curiosidad",
"dime una curiosidad sobre gatos",
"quiero saber algo sobre datos",
"qué sabes de los gatos",
"qué me dices de los gatos",
"quiero saber sobre gatos",
"Información sobre gatos"
]
},
{
"name": "AMAZON.NavigateHomeIntent",
"samples": []
}
],
"types": []
}
}
}

Hecho esto tendremos el modelo de nuestra habilidad. Alexa entenderá cosas como «alexa, abre gatolandia y dime algo sobre gatos» y una vez abierta la skill, entenderá cosas como «ayuda» o «información sobre gatos». Solo falta una cosa: añadir las respuestas y cambiar los intents que vienen por defecto en toda habilidad. Nos dirigimos al directorio lambda y allí a custom. Allí encontraremos un archivo llamado index.js que editaremos más adelante y un directorio llamado node_modules al que accederemos desde el terminal.
Una vez allí, teclearemos npm install para instalar las dependencias necesarias.

Volviendo ahora sí al index.js, borra todo su contenido y reemplázalo por el siguiente.

// 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 LaunchRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
},
handle(handlerInput) {
const speakOutput = 'bienvenido o bienvenida, pídeme información sobre los gatos.';
return handlerInput.responseBuilder
.speak(speakOutput)
.reprompt(speakOutput)
.getResponse();
}
};
const GatolandiaIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'GatolandiaIntent';
},
handle(handlerInput) {
random = Math.round(Math.random() * (4 - 1) + 1);
speakOutput = '¡Los gatos solo cazan ratones cuando no tienen hambre!';
if(random==1)
speakOutput = '¡Los gatos tienen cuatro patas!';
if(random==2)
speakOutput = '¡Los gatos pueden ser de muchos colores!';
if(random==3)
speakOutput = '¡Los gatos hacen miau!';
if(random==4)
speakOutput = '¡Los gatos negros traen mala suerte!';
return handlerInput.responseBuilder
.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 = 'Soy un mensaje de ayuda.';

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 = 'chao!';
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,
GatolandiaIntentHandler,
HelpIntentHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler,
IntentReflectorHandler, // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
)
.addErrorHandlers(
ErrorHandler,
)
.lambda();

¡Ya está! ahora volvemos a la raíz de la habilidad (al directorio gatolandia) y tecleamos ask deploy. si todo ha ido bien, nos dará un mensaje de confirmación diciendo que la habilidad ha sido creada y desplegada. Es momento de activarla y probarla. para ello le decimos a nuestro dispositivo Alexa (el del teléfono también vale) «alexa, abre gatolandia» y luego le decimos algo como quiero saber algo sobre gatos. Aviso: cuando nos dé la respuesta la skill se cerrará, no nos avisará y si le repetimos quiero saber algo sobre gatos, entrará en su propia información fuera de nuestra skill. si queremos probarla de nuevo para conseguir una respuesta aleatoria, tendremos que volver a abrirla. También podemos descomentar la línea
//.reprompt(‘add a reprompt if you want to keep the session open for the user to respond’)
Y cambiarle el mensaje.
en un próximo post destriparemos el código, que los intents tienen miga.

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.