Alessio Barducci
Created December 5, 2016 © GPL3+

Event Master

Event Master is an Alexa Custom Skill that allows you to search for events in the city you want all around the world.

IntermediateShowcase (no instructions)8 hours90

Things used in this project

Hardware components

Echo Dot
Amazon Alexa Echo Dot
×1

Software apps and online services

AWS Lambda
Amazon Web Services AWS Lambda
Alexa Skills Kit
Amazon Alexa Alexa Skills Kit

Story

Read more

Schematics

Interaction Model Example 1

Interaction Model Example 2

Skill Architecture

Code

Intent Schema

JSON
{
  "intents": [
    {
      "intent": "SearchEvents",
      "slots": [
        {
          "name": "City",
          "type": "CITY"
        },
        {
          "name": "Date",
          "type": "AMAZON.DATE"
        }
      ]
    },
    {
      "intent": "SpecifyCity",
      "slots": [
        {
          "name": "City",
          "type": "CITY"
        }
      ]
    },
    {
      "intent": "ShouldGetMoreInfo",
      "slots": [
        {
          "name": "YesNo",
          "type": "YES_NO"
        }
      ]
    },
    {
      "intent": "SpecifyEvent",
      "slots": [
        {
          "name": "EventNumber",
          "type": "AMAZON.NUMBER"
        }
      ]
    },
    {
      "intent": "AMAZON.HelpIntent"
    },
    {
      "intent": "AMAZON.StopIntent"
    },
    {
      "intent": "AMAZON.CancelIntent"
    }
  ]
}

Sample Utterances

snippets
SearchEvents what to do in {City}
SearchEvents what's' going on in {City}
SearchEvents what events are in {City}
SearchEvents about events in {City}
SearchEvents what events in {City}
SearchEvents what to do in {City}
SearchEvents search events in {City}
SearchEvents what's' going on in {City} on {Date}
SearchEvents what's' going on in {City} {Date}
SearchEvents what events are in {City} on {Date}
SearchEvents what events are in {City} {Date}
SearchEvents about events in {City} on {Date}
SearchEvents about events in {City} {Date}
SearchEvents what events in {City} on {Date}
SearchEvents what events in {City} {Date}
SearchEvents what to do in {City} on {Date}
SearchEvents what to do in {City} {Date}
SearchEvents search events in {City} on {Date}
SearchEvents search events in {City} {Date}
SearchEvents what's' going on {Date} in {City}
SearchEvents what events are on {Date} in {City}
SearchEvents about events in {Date} {City}
SearchEvents what to do on {Date} in {City}
SearchEvents what to do {Date} in {City}
SearchEvents search events on {Date} in {City}
SpecifyCity {City}
SpecifyCity in {City}
SpecifyCity about {City}
ShouldGetMoreInfo {YesNo}
SpecifyEvent {EventNumber}
SpecifyEvent event number {EventNumber}
SpecifyEvent number {EventNumber}

Lambda Fuction Code

JavaScript
The URL of the external API has been removed from the script
var APP_ID = /* removed */;

var AlexaSkill = require('./AlexaSkill');
var http = require("http");
var CryptoJS = require("crypto-js");

var GOODBYE_SENTENCES = ["Have a nice day!", "Goodbye!", "Bye!", "Cheers!"];
var EVENTS_KEY = "eventsJson";
var CARDTEXT_KEY = "cardText";

var EventMaster = function () {
    AlexaSkill.call(this, APP_ID);
};

EventMaster.prototype = Object.create(AlexaSkill.prototype);
EventMaster.prototype.constructor = EventMaster;

EventMaster.prototype.eventHandlers.onSessionStarted = function (sessionStartedRequest, session) {
    console.log("EventMaster onSessionStarted requestId: " + sessionStartedRequest.requestId
        + ", sessionId: " + session.sessionId);

    // any session init logic would go here
};

EventMaster.prototype.eventHandlers.onLaunch = function (launchRequest, session, response) {
    console.log("EventMaster onLaunch requestId: " + launchRequest.requestId + ", sessionId: " + session.sessionId);
    getWelcomeResponse(session, response);
};

EventMaster.prototype.eventHandlers.onSessionEnded = function (sessionEndedRequest, session) {
    console.log("EventMaster onSessionEnded requestId: " + sessionEndedRequest.requestId
        + ", sessionId: " + session.sessionId);

    // any session cleanup logic would go here
};

EventMaster.prototype.intentHandlers = {
    "SearchEvents": function (intent, session, response) {
        getEventsResponse(intent, session, response);
    },
	
	"SpecifyCity": function (intent, session, response) {
        getEventsResponse(intent, session, response);
    },
	
	"ShouldGetMoreInfo": function (intent, session, response) {
        shouldGetMoreInfo(intent, session, response);
    },
	
	"SpecifyEvent": function (intent, session, response) {
        getMoreEventInfo(intent, session, response);
    },

    "AMAZON.HelpIntent": function (intent, session, response) {
        getWelcomeResponse(session, response);
    },

    "AMAZON.StopIntent": function (intent, session, response) {
        response.tell(GOODBYE_SENTENCES[Math.floor(Math.random() * GOODBYE_SENTENCES.length)]);
    },

    "AMAZON.CancelIntent": function (intent, session, response) {
        response.tell(GOODBYE_SENTENCES[Math.floor(Math.random() * GOODBYE_SENTENCES.length)]);
    }
};

function getEventsResponse(intent, session, response) {
	var outText = '';
	
	var city = intent.slots.City;
	if (typeof city === 'undefined' || !('value' in city))
	{
		getWelcomeResponse(session, response);
	}
	
	var url = /* removed */ + "?location=" + city.value;
	
	var date = intent.slots.Date;
	if (typeof date !== 'undefined' && 'value' in date)
	{
	    url = url + "&date=" + date.value;
	}
	
	console.log("Calling URL: " + url)
	
	http.get(url, function (result) {
		console.log('Response code: ' + result.statusCode);
		
		if (result.statusCode == 200) {
			outText = 'Done!';
			var body = '';
			result.on('data', function (chunk) {
				body += chunk;
			});
			result.on('end', function () {
				
				var responseObject = JSON.parse(body);
				session.attributes[EVENTS_KEY] = body;

				if (responseObject['status'] === 'CITY_NOT_FOUND' || responseObject['status'] === 'EVENTS_NOT_FOUND') {
					console.log("city or events not found");
					outText = responseObject['textToSay'];
					var responseType = AlexaSkill.speechOutputType.PLAIN_TEXT;
					console.log(responseObject['responseType']);
					if (responseObject['responseType'] === 'SSML') {
						responseType = AlexaSkill.speechOutputType.SSML;
					}
					var speechOutput = {
						speech: outText,
						type: responseType
					};
					response.tell(speechOutput);
				} else {
					console.log("city found");
					outText = responseObject['textToSay'];
					var responseType = AlexaSkill.speechOutputType.PLAIN_TEXT;
					console.log(responseObject['responseType']);
					if (responseObject['responseType'] === 'SSML') {
						responseType = AlexaSkill.speechOutputType.SSML;
					}
					console.log(responseType);
					var speechOutput = {
						speech: outText,
						type: responseType
					};
					
					var repromptOutput = {
						speech: "Would you like to hear more? Say yes or no.",
						type: AlexaSkill.speechOutputType.PLAIN_TEXT
					};
					
					session.attributes[CARDTEXT_KEY] = responseObject['cardContent'];
					var cardTitle = responseObject['cardTitle'];
					var cardContent = responseObject['cardContent'];
					response.askWithCard(speechOutput, repromptOutput, cardTitle, cardContent);
				}
			});
		}
		else
		{
			outText = 'There was a problem, I am not able to retrieve information from Internet. Sorry!';
			var speechOutput = {
				speech: outText,
				type: AlexaSkill.speechOutputType.PLAIN_TEXT
			};
			response.tell(speechOutput);
		}
		
	});
}

function shouldGetMoreInfo(intent, session, response) {
	if (typeof session.attributes[EVENTS_KEY] === "undefined") {
		getWelcomeResponse(session, response)
	} else {
		var answer = intent.slots.YesNo;
		if (typeof answer !== 'undefined' && ('value' in answer) &&
			answer.value.toLowerCase() === 'yes') 
		{
			var events = JSON.parse(session.attributes[EVENTS_KEY]);
			
			if (events['events'].length == 1)
			{
				getMoreEventInfo(intent, session, response);
				return;
			}
			
			var speechText = "Good. Tell me the number of the event on which you are interested about: ";
			var numberList = "";
			for (var i = 0; i < events['events'].length; i++) {
				if (i == (events['events'].length - 1))
				{
					numberList = numberList + " or " + (i + 1);
				}
				else if (i == 0)
				{
					numberList = numberList + (i + 1);
				}
				else
				{
					numberList = numberList + ", " + (i + 1);
				}
			}
			
			var speechOutput = {
				speech: speechText + numberList,
				type: AlexaSkill.speechOutputType.PLAIN_TEXT
			};
			
			var repromptOutput = {
				speech: "Please specify the number of the event: " + numberList,
				type: AlexaSkill.speechOutputType.PLAIN_TEXT
			};
			
			var cardTitle = "Event Choice";
			var cardContent = session.attributes[CARDTEXT_KEY];
			
			response.askWithCard(speechOutput, repromptOutput, cardTitle, cardContent);
		}
		else
		{
			response.tell("Ok fine. " + GOODBYE_SENTENCES[Math.floor(Math.random() * GOODBYE_SENTENCES.length)]);
		}
	}
}

function getMoreEventInfo(intent, session, response) {
	if (typeof session.attributes[EVENTS_KEY] === "undefined") {
		getWelcomeResponse(session, response)
	} else {
		var events = JSON.parse(session.attributes[EVENTS_KEY]);
		var eventNumber = intent.slots.EventNumber;
		
		if (events['events'].length == 1 || (typeof eventNumber !== 'undefined' && ('value' in eventNumber) &&
			eventNumber.value > 0 && eventNumber.value <= events['events'].length))
		{
			var index = 0;
			if (events['events'].length == 1)
			{
				index = 0;
			}
			else
			{
				index = parseInt(eventNumber.value) - 1;
			}
			
			console.log(index);
			var speech = "";
			speech += events['events'][index]['name'] + ".\n ";
			speech += events['events'][index]['infoDescriptionHeader'] + ".\n ";
			speech += events['events'][index]['description'];
			
			var cardText = events['events'][index]['name'] + "\n";
			cardText += events['events'][index]['address'] + "\n";
			cardText += events['events'][index]['description'] + "\n\n";
			cardText += events['events'][index]['url'] + "\n\n";
			
			var cardTitle = events['events'][index]['name'];
			
			response.tellWithCard(speech, cardTitle, cardText);
		}
		else
		{
			response.tell("Sorry, the event number is not valid.");
		}
	}
}

function getWelcomeResponse(session, response) {
	var speechText = "Welcome to the Event Master. Tell me the city, or city plus the date, and I will tell you what events are on the schedule. Which city are you interested about?";
	var repromptText = "Please tell me a city.";
	var speechOutput = {
		speech: speechText,
		type: AlexaSkill.speechOutputType.PLAIN_TEXT
	};
	var repromptOutput = {
		speech: repromptText,
		type: AlexaSkill.speechOutputType.PLAIN_TEXT
	};
	response.ask(speechOutput, repromptOutput);
}

// Create the handler that responds to the Alexa Request.
exports.handler = function (event, context) {
    var eventMaster = new EventMaster();
    eventMaster.execute(event, context);
};

Credits

Alessio Barducci

Alessio Barducci

0 projects • 1 follower

Comments