Skip to content

Instantly share code, notes, and snippets.

@javichur
Created January 25, 2020 17:30
Show Gist options
  • Save javichur/3abbf932ea73ce1c7b6eafed9a7d8b16 to your computer and use it in GitHub Desktop.
Save javichur/3abbf932ea73ce1c7b6eafed9a7d8b16 to your computer and use it in GitHub Desktop.
const https = require('https');
const mode = 'prod'; // dev or 'prod'
const clientID = 'xxx'; // obtenido de consola web de Alexa developers.
const clientSecret = 'xxx'; // obtenido de consola web de Alexa developers.
let tokenGlobal = null; // se usa para enviar notificaciones. Dura 1h. Se rellena en notify().
// Ejemplo de envío de una notificación
const who = 'cake';
const userId1 = 'amzn1.ask.account.xxx';
const apiEndpoint = getApiEndpoint(userId1);
notify(userId1, 'ALERT', who, apiEndpoint);
// TODO: se ha simplificado este método para el ejemplo. Implementar según instrucciones antes
// de llevarlo a producción.
// Devuelve 'api.amazonalexa.com', 'api.eu.amazonalexa.com' o 'api.fe.amazonalexa.com'
function getApiEndpoint(userId) {
// Recuperar de base de datos la url base de la API, según la región del user.
// Solución posible: guardar en base de datos esta info cuando el user abre nuestra skill:
// handlerInput.requestEnvelope.context.System.apiEndpoint;
return 'api.eu.amazonalexa.com';
}
async function notify(userId, eventType, message, apiEndpoint) {
if (tokenGlobal == null) {
tokenGlobal = await getToken();
}
return sendEvent(eventType, tokenGlobal, userId, message, apiEndpoint);
}
function getProactiveOptions(token, postLength, apiEndpoint) {
return {
hostname: apiEndpoint.replace('https://', ''), // 'api.amazonalexa.com', api.eu.amazonalexa.com (Europe) api.fe.amazonalexa.com (Far East)
port: 443,
path: `/v1/proactiveEvents/${mode && mode === 'prod' ? '' : 'stages/development'}`, // mode: global var
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': postLength,
Authorization: `Bearer ${token}`,
},
};
}
function getProactivePostData(eventType, userId, message) {
switch (eventType) {
case 'ALERT':
return getAlertMessageEvent(userId, message);
}
}
function getAlertMessageEvent(userId, productNameExpired) {
const timestamp = new Date();
const expiryTime = new Date();
expiryTime.setMinutes(expiryTime.getMinutes() + 60);
const referenceId = `SampleReferenceId${new Date().getTime()}`; // cross reference to records in your existing systems
const eventJson = {
timestamp: timestamp.toISOString(),
referenceId,
expiryTime: expiryTime.toISOString(),
event: {
name: 'AMAZON.MessageAlert.Activated',
payload: {
state: {
status: 'UNREAD',
freshness: 'NEW',
},
messageGroup: {
creator: {
name: productNameExpired,
},
count: 1,
urgency: 'URGENT',
},
},
},
relevantAudience: {
type: 'Unicast',
payload: {
user: userId,
},
},
};
return eventJson;
}
// ----------------------------------------------------------------------------
function getTokenOptions(postLength) {
// const TokenPostData = getTokenPostData();
return {
hostname: 'api.amazon.com',
port: 443,
path: '/auth/O2/token',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': postLength, // TokenPostData.length
},
};
}
function getTokenPostData() {
return `grant_type=client_credentials&client_id=${clientID}&client_secret=${clientSecret}&scope=alexa::proactive_events`;
}
// const TokenPostData = 'grant_type=client_credentials&client_id=amzn1.application-oa2-client.45690dc26b6848419d11e3c3e51c4c76&client_secret=5571d04619803b123fc305f0b2a8b3ac4f91504f512c2faed7165ca54356aaff&scope=alexa::proactive_events';
async function getToken() {
console.log('Entrando en getToken()');
return new Promise((resolve) => {
const TokenPostData = getTokenPostData();
const req = https.request(getTokenOptions(TokenPostData.length), (res) => {
res.setEncoding('utf8');
let returnData = '';
res.on('data', (chunk) => { returnData += chunk; });
res.on('end', () => {
console.log('onEnd() en getToken()');
const tokenRequestId = res.headers['x-amzn-requestid'];
// console.log(`Token requestId: ${tokenRequestId}`);
resolve(JSON.parse(returnData).access_token);
});
});
console.log('Write() en getToken()');
req.write(TokenPostData);
req.end();
});
}
// ----------------------------------------------------------------------------
async function sendEvent(eventType, token, userId, message, apiEndpoint) {
return await new Promise((resolve) => {
const ProactivePostData = JSON.stringify(getProactivePostData(eventType, userId, message));
console.log(`\nProactivePostData\n${JSON.stringify(JSON.parse(ProactivePostData), null, 2)}\n-----------`);
const ProactiveOptions = getProactiveOptions(token, ProactivePostData.length, apiEndpoint);
console.log(`ProactiveOptions\n${JSON.stringify(ProactiveOptions, null, 2)}`);
const req = https.request(ProactiveOptions, (res) => {
res.setEncoding('utf8');
if ([200, 202].includes(res.statusCode)) {
// console.log('successfully sent event');
console.log(`requestId: ${res.headers['x-amzn-requestid']}`);
} else {
console.log(`Error https response: ${res.statusCode}`);
console.log(`requestId: ${res.headers['x-amzn-requestid']}`);
if ([403].includes(res.statusCode)) {
console.log(`userId ${userId}\nmay not have subscribed to this event.`);
}
}
let returnData;
res.on('data', (chunk) => { returnData += chunk; });
res.on('end', () => {
// console.log(`return headers: ${JSON.stringify(res.headers, null, 2)}`);
resolve(`sent event ${eventType}`);
});
});
req.write(ProactivePostData);
req.end();
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment