Skip to content

Instantly share code, notes, and snippets.

@davidknipe
Last active June 19, 2018 19:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davidknipe/dbe18e3f78df6b34e971fbd0f85b0119 to your computer and use it in GitHub Desktop.
Save davidknipe/dbe18e3f78df6b34e971fbd0f85b0119 to your computer and use it in GitHub Desktop.
Alexa skill for Episerver Music Festival to show of the Content Delivery API
'use strict';
// --------------- Helpers that build all of the responses -----------------------
var http = require('http');
var axios = require('axios');
var striptags = require('striptags');
function buildSpeechletResponse(title, output, repromptText, shouldEndSession) {
return {
outputSpeech: {
type: 'PlainText',
text: output,
},
card: {
type: 'Simple',
title: `SessionSpeechlet - ${title}`,
content: `SessionSpeechlet - ${output}`,
},
reprompt: {
outputSpeech: {
type: 'PlainText',
text: repromptText,
},
},
shouldEndSession,
};
}
function buildResponse(sessionAttributes, speechletResponse) {
return {
version: '1.0',
sessionAttributes,
response: speechletResponse,
};
}
// --------------- Functions that control the skill's behavior -----------------------
function getWelcomeResponse(callback) {
// If we wanted to initialize the session to have some attributes we could add those here.
const sessionAttributes = {};
const cardTitle = 'Welcome';
const speechOutput = 'Welcome to Music Festival by Epi server. ' +
'You can ask me to search for your favorite band and find out when they are playing or just say tell me the headliners';
// If the user either does not reply to the welcome message or says something that is not
// understood, they will be prompted again with this text.
const repromptText = 'Tell me to search for your favourite band is or say headliners to hear the headliners';
const shouldEndSession = false;
callback(sessionAttributes,
buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}
function handleSessionEndRequest(callback) {
const cardTitle = 'Session Ended';
const speechOutput = 'Thank you for using Music Festival! Have a nice day!';
const shouldEndSession = true;
callback({}, buildSpeechletResponse(cardTitle, speechOutput, null, shouldEndSession));
}
function createFavoriteBandAttributes(favoriteBand) {
return {
favoriteBand,
};
}
function searchBand(intent, session, callback) {
const cardTitle = intent.name;
const favoriteBandSlot = intent.slots.bandname;
let favoriteBand;
let repromptText = null;
let sessionAttributes = {};
let shouldEndSession = false;
let speechOutput = '';
repromptText = "You can ask me to search for your favourite band";
if (favoriteBandSlot) {
favoriteBand = favoriteBandSlot.value;
sessionAttributes = createFavoriteBandAttributes(favoriteBand);
axios.get('https://root.url.for.content.delivery.api/api/episerver/search/content/', {
params: {
'query' : favoriteBand,
'skip' : 0,
'top' : 1,
'expand' : '*',
'filter' : 'ContentType/any(t:t eq \'ArtistPage\')',
'orderby' : 'Changed desc',
'personalize' : 'true'
},
headers: {
'Accept': 'application/json',
'Accept-Language': 'en'
}
})
.then(function (response) {
speechOutput = favoriteBand + " are a " + response.data.Results[0].ArtistGenre.Value + " band. They are described as a " + striptags(response.data.Results[0].ArtistDescription.Value).replace(" ", "");
callback(sessionAttributes,
buildSpeechletResponse(intent.name, speechOutput, repromptText, shouldEndSession));
})
.catch(function (error) {
speechOutput = "I had an error " + error;
console.log(error);
callback(sessionAttributes,
buildSpeechletResponse(intent.name, speechOutput, repromptText, shouldEndSession));
});
}
}
function bandPlaying(intent, session, callback) {
let favoriteBand;
const repromptText = null;
const sessionAttributes = {};
let shouldEndSession = false;
let speechOutput = '';
if (session.attributes) {
favoriteBand = session.attributes.favoriteBand;
}
if (favoriteBand) {
axios.get('https://root.url.for.content.delivery.api/api/episerver/search/content/', {
params: {
'query' : favoriteBand,
'skip' : 0,
'top' : 1,
'expand' : '*',
'filter' : 'ContentType/any(t:t eq \'ArtistPage\')',
'orderby' : 'Changed desc',
'personalize' : 'true'
},
headers: {
'Accept': 'application/json',
'Accept-Language': 'en'
}
})
.then(function (response) {
let perfTime = new Date(response.data.Results[0].PerformanceStartTime.Value);
let perfHours = parseInt(perfTime.getHours());
if (perfHours > 12)
perfHours = perfHours - 11;
if (perfHours == 0)
perfHours = 10;
console.log("==============");
console.log(perfTime);
console.log(perfHours);
console.log("==============");
speechOutput = favoriteBand + " are on the " + response.data.Results[0].StageName.Value + " stage at " + perfHours + "PM";
shouldEndSession = false;
callback(sessionAttributes,
buildSpeechletResponse(intent.name, speechOutput, repromptText, shouldEndSession));
})
.catch(function (error) {
speechOutput = "Sorry I didn't quite understand.";
console.log(error);
shouldEndSession = false;
callback(sessionAttributes,
buildSpeechletResponse(intent.name, speechOutput, repromptText, shouldEndSession));
});
}
}
function listHeadliners(intent, session, callback) {
const repromptText = null;
const sessionAttributes = {};
let shouldEndSession = false;
var speechOutput = '';
shouldEndSession = false;
axios.get('https://root.url.for.content.delivery.api/api/episerver/search/content/', {
params: {
'query' : '',
'skip' : 0,
'top' : 10,
'expand' : '*',
'filter' : 'ContentType/any(t:t eq \'ArtistPage\') and ArtistIsHeadliner/Value eq true',
'orderby' : 'Changed desc',
'personalize' : 'true'
},
headers: {
'Accept': 'application/json',
'Accept-Language': 'en'
}
})
.then(function (response) {
speechOutput = "";
let count = 0;
response.data.Results.forEach(function(element) {
if (count == response.data.Results.length - 1)
{
let speech = element.ArtistName.Value;
speechOutput = speechOutput + speech;
count++;
}
else if (count == response.data.Results.length - 2)
{
let speech = element.ArtistName.Value + " and ";
speechOutput = speechOutput + speech;
count++;
}
else
{
let speech = element.ArtistName.Value + ", ";
speechOutput = speechOutput + speech;
count++;
}
});
speechOutput = "The headliners are: " + speechOutput;
console.log(speechOutput);
callback(sessionAttributes,
buildSpeechletResponse(intent.name, speechOutput, repromptText, shouldEndSession));
})
.catch(function (error) {
speechOutput = "I had an error " + error;
console.log(error);
callback(sessionAttributes,
buildSpeechletResponse(intent.name, speechOutput, repromptText, shouldEndSession));
});
}
// --------------- Events -----------------------
function onSessionStarted(sessionStartedRequest, session) {
console.log(`onSessionStarted requestId=${sessionStartedRequest.requestId}, sessionId=${session.sessionId}`);
}
function onLaunch(launchRequest, session, callback) {
console.log(`onLaunch requestId=${launchRequest.requestId}, sessionId=${session.sessionId}`);
// Dispatch to your skill's launch.
getWelcomeResponse(callback);
}
function onIntent(intentRequest, session, callback) {
console.log(`onIntent requestId=${intentRequest.requestId}, sessionId=${session.sessionId}`);
const intent = intentRequest.intent;
const intentName = intentRequest.intent.name;
// Dispatch to your skill's intent handlers
if (intentName === 'WhosPlaying') {
listHeadliners(intent, session, callback);
} else if (intentName === 'BandPlaying') {
bandPlaying(intent, session, callback);
} else if (intentName === 'SearchBand') {
searchBand(intent, session, callback);
} else if (intentName === 'AMAZON.HelpIntent') {
getWelcomeResponse(callback);
} else if (intentName === 'AMAZON.StopIntent' || intentName === 'AMAZON.CancelIntent') {
handleSessionEndRequest(callback);
} else {
throw new Error('Invalid intent');
}
}
function onSessionEnded(sessionEndedRequest, session) {
console.log(`onSessionEnded requestId=${sessionEndedRequest.requestId}, sessionId=${session.sessionId}`);
}
// --------------- Main handler -----------------------
// Route the incoming request based on type (LaunchRequest, IntentRequest,
// etc.) The JSON body of the request is provided in the event parameter.
exports.handler = (event, context, callback) => {
try {
console.log(`event.session.application.applicationId=${event.session.application.applicationId}`);
/**
* Uncomment this if statement and populate with your skill's application ID to
* prevent someone else from configuring a skill that sends requests to this function.
*/
/*
if (event.session.application.applicationId !== 'amzn1.echo-sdk-ams.app.[unique-value-here]') {
callback('Invalid Application ID');
}
*/
if (event.session.new) {
onSessionStarted({ requestId: event.request.requestId }, event.session);
}
if (event.request.type === 'LaunchRequest') {
onLaunch(event.request,
event.session,
(sessionAttributes, speechletResponse) => {
callback(null, buildResponse(sessionAttributes, speechletResponse));
});
} else if (event.request.type === 'IntentRequest') {
onIntent(event.request,
event.session,
(sessionAttributes, speechletResponse) => {
callback(null, buildResponse(sessionAttributes, speechletResponse));
});
} else if (event.request.type === 'SessionEndedRequest') {
onSessionEnded(event.request, event.session);
callback();
}
} catch (err) {
callback(err);
}
};
{
"interactionModel": {
"languageModel": {
"invocationName": "music festival",
"intents": [
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "SearchBand",
"slots": [
{
"name": "bandname",
"type": "BAND_NAME",
"samples": [
"Tell me when {bandname} are playing"
]
}
],
"samples": [
"{bandname} sound cool",
"What are {bandname} like",
"{bandname} sound interesting",
"{bandname} tell me more",
"Tell me about {bandname}",
"Tell me more about {bandname}",
"Search for {bandname}"
]
},
{
"name": "BandPlaying",
"slots": [],
"samples": [
"BandPlaying When are they on",
"BandPlaying When are they playing"
]
},
{
"name": "WhosPlaying",
"slots": [],
"samples": [
"headlining",
"headliners",
"WhosPlaying Who is headlining",
"WhosPlaying Who is head lining",
"WhosPlaying Who's headlining",
"WhosPlaying Who's on the main stage",
"WhosPlaying Who's on the mainstage",
"WhosPlaying Tell me who's on the mainstage",
"WhosPlaying Who is playing",
"WhosPlaying Who's playing",
"WhosPlaying Tell me who's on the main stage",
"WhosPlaying Who on are the main stage",
"WhosPlaying Who are the head liners",
"WhosPlaying Who's head lining"
]
}
],
"types": [
{
"name": "BAND_NAME",
"values": [
{
"name": {
"value": "Air and Firewoodx"
}
},
{
"name": {
"value": "Alexa Everywhere"
}
},
{
"name": {
"value": "Almost Up Today"
}
},
{
"name": {
"value": "Arkansas Flask"
}
},
{
"name": {
"value": "Ash Ravine"
}
},
{
"name": {
"value": "Athena"
}
},
{
"name": {
"value": "Boring Thrill"
}
},
{
"name": {
"value": "Brain Operation"
}
},
{
"name": {
"value": "Brave Minor"
}
},
{
"name": {
"value": "Cranky Babe"
}
},
{
"name": {
"value": "Crispy Meridian"
}
},
{
"name": {
"value": "Despite Yu"
}
},
{
"name": {
"value": "During Rewind"
}
},
{
"name": {
"value": "Felicia Gourd"
}
},
{
"name": {
"value": "Green Fabric and the Major Lark"
}
},
{
"name": {
"value": "Greg Rabbit"
}
},
{
"name": {
"value": "Indecent of the Surviving"
}
},
{
"name": {
"value": "La Tangerine"
}
},
{
"name": {
"value": "Mister Farmer"
}
},
{
"name": {
"value": "Open Devotion"
}
},
{
"name": {
"value": "Over Lingo"
}
},
{
"name": {
"value": "Pigment of Dawn"
}
},
{
"name": {
"value": "Polly Pollution"
}
},
{
"name": {
"value": "Poser and the Shroud"
}
},
{
"name": {
"value": "Reggie Heritage"
}
},
{
"name": {
"value": "Sleuth of the Patriarchy"
}
},
{
"name": {
"value": "The Bad Metaphors"
}
},
{
"name": {
"value": "Thoughts of the Mouth"
}
},
{
"name": {
"value": "Top Reindeer"
}
},
{
"name": {
"value": "Volatile of the Upstream"
}
}
]
}
]
},
"dialog": {
"intents": [
{
"name": "SearchBand",
"confirmationRequired": false,
"prompts": {},
"slots": [
{
"name": "bandname",
"type": "BAND_NAME",
"confirmationRequired": false,
"elicitationRequired": true,
"prompts": {
"elicitation": "Elicit.Slot.1032201435510.771767280049"
}
}
]
}
]
},
"prompts": [
{
"id": "Elicit.Slot.1032201435510.771767280049",
"variations": [
{
"type": "PlainText",
"value": "Thanks looking up information on {bandname}"
}
]
}
]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment