Skip to content

Instantly share code, notes, and snippets.

@germanviscuso
Last active August 31, 2021 21:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save germanviscuso/7cbeb7d75a08b44fe0ec086072d677fd to your computer and use it in GitHub Desktop.
Save germanviscuso/7cbeb7d75a08b44fe0ec086072d677fd to your computer and use it in GitHub Desktop.
Alexa Skill Basics: Service Client API
{
"interactionModel": {
"languageModel": {
"invocationName": "datos personales",
"intents": [
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "AMAZON.NavigateHomeIntent",
"samples": []
},
{
"name": "AddressIntent",
"samples": [
"quiero saber la dirección",
"quiero saber mi dirección",
"donde vivo",
"cual es mi direccion",
"dame mi dirección",
"dime mi dirección",
"donde estoy"
]
},
{
"name": "TimeZoneIntent",
"samples": [
"quiero saber mi zona horaria",
"quiero saber la zona horaria",
"zona horaria",
"en que zona horaria estoy",
"que zona horaria tengo",
"dime la zona horaria",
"dame la zona horaria"
]
},
{
"name": "TemperatureUnitIntent",
"samples": [
"temperatura",
"unidad de temperatura",
"dime mi unidad de temperatura",
"cual es mi unidad de temperatura"
]
},
{
"name": "DistanceUnitIntent",
"samples": [
"unidad de distancia",
"dime mi unidad de distancia",
"cual es mi unidad de distancia",
"distancia"
]
},
{
"name": "EmailIntent",
"samples": [
"cual es mi email",
"dime mi email",
"email",
"cual es mi correo electrónico",
"dime mi correo electrónico",
"correo electrónico"
]
},
{
"name": "MobileIntent",
"samples": [
"móvil",
"teléfono",
"número de móvil",
"número de teléfono",
"dime mi móvil",
"dime mi número de teléfono",
"cual es mi móvil",
"cual es mi número de teléfono"
]
},
{
"name": "NameIntent",
"samples": [
"como me llamo",
"cual es mi nombre",
"dime mi nombre",
"nombre",
"nombre completo"
]
}
]
}
}
}
/* eslint-disable func-names */
/* eslint-disable no-console */
const Alexa = require('ask-sdk');
const LaunchHandler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
return request.type === 'LaunchRequest';
},
handle(handlerInput) {
const speechOutput = "¡Bienvenido! " + HELP_MESSAGE;
return handlerInput.responseBuilder
.speak(speechOutput)
.reprompt(speechOutput)
.getResponse();
},
};
//https://ask-sdk-for-nodejs.readthedocs.io/en/latest/Calling-Alexa-Service-APIs.html#deviceaddressserviceclient
const AddressIntentHandler = {
canHandle(handlerInput) {
const { request } = handlerInput.requestEnvelope;
return request.type === 'IntentRequest' && request.intent.name === 'AddressIntent';
},
async handle(handlerInput) {
const { requestEnvelope, serviceClientFactory, responseBuilder } = handlerInput;
const consentToken = requestEnvelope.context.System.user.permissions
&& requestEnvelope.context.System.user.permissions.consentToken;
if (!consentToken) {
return responseBuilder
.speak(NOTIFY_MISSING_PERMISSIONS)
.withAskForPermissionsConsentCard(ADDRESS_PERMISSION)
.getResponse();
}
console.log('INFO: we have permission!');
const { deviceId } = requestEnvelope.context.System.device;
const deviceAddressServiceClient = serviceClientFactory.getDeviceAddressServiceClient();
const address = await deviceAddressServiceClient.getFullAddress(deviceId);
//const address = await deviceAddressServiceClient.getCountryAndPostalCode(deviceId);
let response;
if (address.addressLine1 === null && address.stateOrRegion === null) {
response = responseBuilder.speak(NO_INFO).getResponse();
} else {
const ADDRESS_MESSAGE = `${ADDRESS_AVAILABLE + address.addressLine1}, ${address.city}, ${address.stateOrRegion}, ${address.postalCode}, código de país ${address.countryCode}`;
response = responseBuilder.speak(ADDRESS_MESSAGE).reprompt('¿Quieres alguna otra información?').getResponse();
}
return response;
}
};
const EmailIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'EmailIntent';
},
async handle(handlerInput) {
const { serviceClientFactory, responseBuilder } = handlerInput;
try {
const upsServiceClient = serviceClientFactory.getUpsServiceClient();
const profileEmail = await upsServiceClient.getProfileEmail();
if (!profileEmail) {
return responseBuilder
.speak(NO_INFO)
.getResponse();
}
const speechResponse = `Tu email es ${profileEmail}`;
return responseBuilder
.speak(speechResponse)
.reprompt('¿Quieres alguna otra información?')
.getResponse();
} catch (error) {
console.log(error.message);
if (error.statusCode == 403) {
return responseBuilder
.speak(NOTIFY_MISSING_PERMISSIONS)
.withAskForPermissionsConsentCard(EMAIL_PERMISSION)
.getResponse();
}
console.log(JSON.stringify(error));
const response = responseBuilder.speak(ERROR_MESSAGE).getResponse();
return response;
}
}
}
const MobileIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'MobileIntent';
},
async handle(handlerInput) {
const { serviceClientFactory, responseBuilder } = handlerInput;
try {
const upsServiceClient = serviceClientFactory.getUpsServiceClient();
const profileMobile = await upsServiceClient.getProfileMobileNumber();
if (!profileMobile) {
return responseBuilder
.speak(NO_INFO)
.getResponse();
}
const speechResponse = `Tu móvil es ${profileMobile}`;
return responseBuilder
.speak(speechResponse)
.reprompt('¿Quieres alguna otra información?')
.getResponse();
} catch (error) {
console.log(error.message);
if (error.statusCode == 403) {
return responseBuilder
.speak(NOTIFY_MISSING_PERMISSIONS)
.withAskForPermissionsConsentCard(MOBILE_PERMISSION)
.getResponse();
}
console.log(JSON.stringify(error));
const response = responseBuilder.speak(ERROR_MESSAGE).getResponse();
return response;
}
}
}
const NameIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'NameIntent';
},
async handle(handlerInput) {
const { serviceClientFactory, responseBuilder } = handlerInput;
try {
const upsServiceClient = serviceClientFactory.getUpsServiceClient();
const profileName = await upsServiceClient.getProfileName();
//const profileName = await upsServiceClient.getProfileGivenName();
if (!profileName) {
return responseBuilder
.speak(NO_INFO)
.getResponse();
}
const speechResponse = `Tu nombre es ${profileName}`;
return responseBuilder
.speak(speechResponse)
.reprompt('¿Quieres alguna otra información?')
.getResponse();
} catch (error) {
console.log(error.message);
if (error.statusCode == 403) {
return responseBuilder
.speak(NOTIFY_MISSING_PERMISSIONS)
.withAskForPermissionsConsentCard(FULL_NAME_PERMISSION)
//.withAskForPermissionsConsentCard(GIVEN_NAME_PERMISSION) //and in skill.json replace with alexa::profile:given_name:read
.getResponse();
}
console.log(JSON.stringify(error));
const response = responseBuilder.speak(ERROR_MESSAGE).getResponse();
return response;
}
}
}
const TimeZoneIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'TimeZoneIntent';
},
async handle(handlerInput) {
const { requestEnvelope, serviceClientFactory, responseBuilder } = handlerInput;
const {deviceId} = requestEnvelope.context.System.device;
const upsServiceClient = serviceClientFactory.getUpsServiceClient();
const timeZone = await upsServiceClient.getSystemTimeZone(deviceId);
if (!timeZone) {
return responseBuilder
.speak(NO_ACCESS)
.getResponse();
}
const speechResponse = `Tu zona horaria es ${timeZone}`;
return responseBuilder
.speak(speechResponse)
.reprompt('¿Quieres alguna otra información?')
.getResponse();
}
}
const TemperatureUnitIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'TemperatureUnitIntent';
},
async handle(handlerInput) {
const { requestEnvelope, serviceClientFactory, responseBuilder } = handlerInput;
const {deviceId} = requestEnvelope.context.System.device;
const upsServiceClient = serviceClientFactory.getUpsServiceClient();
const temperatureUnit = await upsServiceClient.getSystemTemperatureUnit(deviceId);
if (!temperatureUnit) {
return responseBuilder
.speak(NO_ACCESS)
.getResponse();
}
const speechResponse = `Tu unidad de temperatura es ${temperatureUnit}`;
return responseBuilder
.speak(speechResponse)
.reprompt('¿Quieres alguna otra información?')
.getResponse();
}
}
const DistanceUnitIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'DistanceUnitIntent';
},
async handle(handlerInput) {
const { requestEnvelope, serviceClientFactory, responseBuilder } = handlerInput;
const {deviceId} = requestEnvelope.context.System.device;
const upsServiceClient = serviceClientFactory.getUpsServiceClient();
const distanceUnit = await upsServiceClient.getSystemDistanceUnits(deviceId);
if (!distanceUnit) {
return responseBuilder
.speak(NO_ACCESS)
.getResponse();
}
const speechResponse = `Tu unidad de distancia es ${distanceUnit}`;
return responseBuilder
.speak(speechResponse)
.reprompt('¿Quieres alguna otra información?')
.getResponse();
}
}
const HelpHandler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
return request.type === 'IntentRequest'
&& request.intent.name === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
return handlerInput.responseBuilder
.speak(HELP_MESSAGE)
.reprompt(HELP_REPROMPT)
.getResponse();
},
};
const ExitHandler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
return request.type === 'IntentRequest'
&& (request.intent.name === 'AMAZON.CancelIntent'
|| request.intent.name === 'AMAZON.StopIntent');
},
handle(handlerInput) {
return handlerInput.responseBuilder
.speak(STOP_MESSAGE)
.getResponse();
},
};
const SessionEndedRequestHandler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
return request.type === 'SessionEndedRequest';
},
handle(handlerInput) {
console.log(`Session ended with reason: ${handlerInput.requestEnvelope.request.reason}`);
return handlerInput.responseBuilder.getResponse();
},
};
const ServiceErrorHandler = {
canHandle(handlerInput, error) {
return error.name === 'ServiceError';
},
handle(handlerInput, error) {
if (error.statusCode === 403) {
return handlerInput.responseBuilder
.speak(NOTIFY_MISSING_PERMISSIONS)
.withAskForPermissionsConsentCard(ADDRESS_PERMISSION)
.getResponse();
}
return handlerInput.responseBuilder
.speak(LOCATION_FAILURE)
.getResponse();
},
};
const SimulatorErrorHandler = {
canHandle(handlerInput, error) {
return error.message.includes('Failed trying to parse the response body');
},
handle(handlerInput, error) {
return handlerInput.responseBuilder
.speak(SIMULATOR_FAILURE)
.getResponse();
}
};
const ErrorHandler = {
canHandle() {
return true;
},
handle(handlerInput, error) {
console.log(`ERROR handler: ${error.message}`);
return handlerInput.responseBuilder
.speak(ERROR_MESSAGE)
.getResponse();
},
};
const HELP_MESSAGE = 'Puedes preguntarme por tu dirección, tu email, tu nombre, tu número de móvil, tu unidad de temperatura, tu unidad de distancia y tu zona horaria.';
const HELP_REPROMPT = '¿Cómo te puedo ayudar?';
const STOP_MESSAGE = '¡Adiós!';
const ERROR_MESSAGE = 'Perdona, ha ocurrido un error.';
const NOTIFY_MISSING_PERMISSIONS = 'Por favor habilita los permisos solicitados en tu app Alexa y vuelve a intentarlo.';
const NO_INFO = 'Parece que no lo tienes configurado. Ingresalo en la app Alexa y vuelve a intentarlo.';
const NO_ACCESS = 'Parece que no he podido recuperar el dato. Vuelve a intentarlo.';
const ADDRESS_AVAILABLE = 'Aquí tienes tu dirección: ';
const LOCATION_FAILURE = 'Ha habido un error accediendo la API para obtener la dirección. Por favor intenta otra vez.';
const SIMULATOR_FAILURE = 'Debes usar esta skill desde un dispositivo real, no desde el simulador. Por favor intenta otra vez.';
// no olvides habilitar estos permisos en la configuración de la skill en developer.amazon.com/alexa
const ADDRESS_PERMISSION = ['read::alexa:device:all:address'];
const FULL_NAME_PERMISSION = ['alexa::profile:name:read'];
const GIVEN_NAME_PERMISSION = ['alexa::profile:given_name:read'];
const EMAIL_PERMISSION = ['alexa::profile:email:read'];
const MOBILE_PERMISSION = ['alexa::profile:mobile_number:read'];
const skillBuilder = Alexa.SkillBuilders.custom();
exports.handler = skillBuilder
.addRequestHandlers(
LaunchHandler,
AddressIntentHandler,
EmailIntentHandler,
MobileIntentHandler,
NameIntentHandler,
TimeZoneIntentHandler,
TemperatureUnitIntentHandler,
DistanceUnitIntentHandler,
HelpHandler,
ExitHandler,
SessionEndedRequestHandler
)
.addErrorHandlers(SimulatorErrorHandler)
.addErrorHandlers(ServiceErrorHandler)
.addErrorHandlers(ErrorHandler)
.withApiClient(new Alexa.DefaultApiClient())
.lambda();
{
"manifest": {
"publishingInformation": {
"locales": {
"es-ES": {
"name": "datos personales"
}
}
},
"apis": {
"custom": {
"endpoint": {
"sourceDir": "lambda/custom"
}
}
},
"manifestVersion": "1.0",
"permissions": [
{
"name": "alexa::devices:all:address:full:read"
},
{
"name": "alexa::profile:email:read"
},
{
"name": "alexa::profile:mobile_number:read"
},
{
"name": "alexa::profile:name:read"
}
]
}
}
Copy link

ghost commented Jan 23, 2019

Hola German.
Llevo varias intentonas para certificar una skill que necesita la dirección del dispositivo.
Tengo el código tan similar como este ejemplo.
He logrado grabar la excepción que da cuando están realizando los test de certificación.

Que es en:
if (address.addressLine1 === null && address.stateOrRegion === null)
Error:
Cannot read property 'addressLine1' of undefined

Voy a poner un if(address) superior, pero no entiendo el error ya que todos los ejemplos que he visto nadie lo realiza.

Un saludo.

@germanviscuso
Copy link
Author

germanviscuso commented Jan 24, 2019

Voy a poner un if(address) superior, pero no entiendo el error ya que todos los ejemplos que he visto nadie lo realiza

Creo que tenemos que adaptar estos ejemplos porque puede darse el caso que el usuario nunca haya configurado una dirección e el dispositivo. Ponle el if, como en este ejemplo: https://forums.developer.amazon.com/questions/171638/how-to-get-address-alexa-device.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment