vincent wong
Published © GPL3+

Today in History

Ask Today In History to get a brief description of historical events on any date.

IntermediateFull instructions provided1,011
Today in History

Things used in this project

Hardware components

Amazon Echo
Amazon Alexa Amazon Echo
×1

Software apps and online services

Alexa Skills Kit
Amazon Alexa Alexa Skills Kit
AWS Lambda
Amazon Web Services AWS Lambda
This Day in History API

Story

Read more

Code

IntentSchema.json.txt

JSON
{
  "intents": [
    {
      "intent": "QueryEventIntent",
      "slots": [
        {
          "name": "day",
          "type": "AMAZON.DATE"
        }
      ]
    },
    {
      "intent": "NextEventIntent"
    },
    {
      "intent": "ChangeTypeIntent"
    },
	{
	  "intent": "SelectTypeIntent",
      "slots": [
        {
          "name": "type",
          "type": "AMAZON.NUMBER"
        }
      ]
	},
    {
      "intent": "AMAZON.HelpIntent"
    },
    {
      "intent": "AMAZON.StopIntent"
    },
    {
      "intent": "AMAZON.CancelIntent"
    }
  ]
}

Utterances v2.txt

Plain text
QueryEventIntent get events for {day}
QueryEventIntent give me events for {day}
QueryEventIntent what happened on {day}
QueryEventIntent what happened {day}
QueryEventIntent what happened
QueryEventIntent {day}
QueryEventIntent about historic events

NextEventIntent yes
NextEventIntent yup
NextEventIntent sure
NextEventIntent next
NextEventIntent yes please

AMAZON.StopIntent no
AMAZON.StopIntent nope
AMAZON.StopIntent no thanks
AMAZON.StopIntent no thank you

ChangeTypeIntent change query type
ChangeTypeIntent change type

SelectTypeIntent the type is {type}
SelectTypeIntent my type is {type}
SelectTypeIntent i select {type}
SelectTypeIntent i choose {type}
SelectTypeIntent {type}

index.js

JavaScript
'use strict';
var Alexa = require("alexa-sdk");
var appId = 'amzn1.echo-sdk-ams.app.your-skill-id';

var http = require("http");

var optionsGet = {
	host : 'api.hiztory.org', 
    port : 80,
    path : '', 
    method : 'GET'
};

var parseString = require('xml2js').parseString;
var pageSize = 5;
var pageNo = 1;
var month = '';
var day = '';

exports.handler = function(event, context, callback) {
    var alexa = Alexa.handler(event, context);
    alexa.appId = appId;
    alexa.dynamoDBTableName = 'todayInHistory';
    alexa.registerHandlers(newSessionHandlers, startHistoryHandlers, queryHistoryHandlers, queryEventHandlers, changeTypeHandlers);
    alexa.execute();
};

var states = {
    STARTMODE: '_STARTMODE',   // prompt the user to start or restart
	TYPEMODE: '_TYPEMODE',     // changing query type
	QUERYMODE: '_QUERYMODE'    // user querying history
};

var types = ['events', 'births', 'deaths', 'aviation'];

var newSessionHandlers = {
    'NewSession': function() {
        if(Object.keys(this.attributes).length === 0) {
            this.attributes['queryType'] = 'events';
        }
        this.handler.state = states.STARTMODE;
        this.emit(':ask', 'Welcome to Today In History.  ' +
			'You are querying ' + this.attributes['queryType'] + ' type history.  ' +
			'Ask Today In History to get a brief description of historical events on any date.  ' +
			'For example, you could say today, or August thirtieth, or you can say no to end the query. Now, which day do you want?',
			'Which day do you want?');
    },
	'Unhandled': function () {
		this.handler.state = states.STARTMODE;
		var message = "Say yes to continue, or no to end the query.";
		this.emit(':ask', message, message);
	}
};

var queryHistoryHandlers = Alexa.CreateStateHandler(states.QUERYMODE, {
	'LaunchRequest': function () {
		this.emit('NewSession');	
	},
    'QueryEventIntent': function () {
		pageNo = 1;
		
		var daySlot = this.event.request.intent.slots.day;		
		var date = '';
		
		// If the user provides a date, then use that, otherwise use today
		// The date is in server time, not in the user's time zone. So "today" for the user may actually be tomorrow
		if (daySlot && daySlot.value) {
			date = new Date(daySlot.value);
		} else {
			date = new Date();
		}
	
		month = ('0' + (date.getMonth()+1)).slice(-2);
		day = ('0' + date.getDate()).slice(-2);
		
		this.emit('QueryEvent', this);
	},
	'ChangeTypeIntent': function() {
        this.handler.state = states.TYPEMODE;	
		this.emit('ChangeType');
	},
    'NextEventIntent': function () {
		pageNo += 1;
		this.emit('QueryEvent', this);
    },
    'AMAZON.HelpIntent': function() {
        var message = 'Ask Today In History to get a brief description of historical events on any date.  ' +
            'For example, you could say today, or August thirtieth, or you can say no to end the query. You can change the query type by saying change type.  Now, which day do you want?';
        this.emit(':ask', message, message);
    },
    'AMAZON.StopIntent': function() {
        this.emit(':tell', 'Ok, talk to  you next time!');
    },
    'SessionEndedRequest': function () {
        console.log('session ended!');
        this.emit(':saveState', true);
    },
    'Unhandled': function() {
        var message = 'Say yes to go deeper, or say no to end the query.';
        this.emit(':ask', message, message);
    }	
});

var startHistoryHandlers = Alexa.CreateStateHandler(states.STARTMODE, {
	'LaunchRequest': function () {
		this.emit('NewSession');	
	},
    'QueryEventIntent': function () {
        this.handler.state = states.QUERYMODE;	
		pageNo = 1;

		var daySlot = this.event.request.intent.slots.day;		
		var date = '';
		
		// If the user provides a date, then use that, otherwise use today
		// The date is in server time, not in the user's time zone. So "today" for the user may actually be tomorrow
		if (daySlot && daySlot.value) {
			date = new Date(daySlot.value);
		} else {
			date = new Date();
		}
	
		month = ('0' + (date.getMonth()+1)).slice(-2);
		day = ('0' + date.getDate()).slice(-2);
		
		this.emit('QueryEvent', this);
	},
	'ChangeTypeIntent': function() {
        this.handler.state = states.TYPEMODE;	
		this.emit('ChangeType');
	},
    'AMAZON.HelpIntent': function() {
        var message = 'Ask Today In History to get a brief description of historical events on any date.  ' +
            'For example, you could say today, or August thirtieth, or you can say no to end the query. You can change the query type by saying change type.  Now, which day do you want?';
        this.emit(':ask', message, message);
    },
    'AMAZON.StopIntent': function() {
        this.emit(':tell', 'Ok, talk to you next time!');
    },
    'SessionEndedRequest': function () {
        console.log('session ended!');
        this.emit(':saveState', true);
    },
    'Unhandled': function() {
        var message = 'Which day do you want?, or say no to end the query.';
        this.emit(':ask', message, message);
    }
});

var changeTypeHandlers = Alexa.CreateStateHandler(states.TYPEMODE, {
	'SelectTypeIntent': function() {
        this.handler.state = states.STARTMODE;	
		this.emit('SelectType', this);
	},
    'AMAZON.StopIntent': function() {
        this.emit(':tell', 'Ok, talk to you next time!');
    },
    'SessionEndedRequest': function () {
        console.log('session ended!');
        this.emit(':saveState', true);
    },
    'Unhandled': function() {
        var message = 'Which type of query do you want?, Choose 1 for event, 2 for birth, 3 for death, and 4 for aviation.  or say no to end the query.';
        this.emit(':ask', message, message);
    }
});

var queryEventHandlers = {
	'ChangeType': function() {
		var message = 'You have a choice of 4 types of query: choose 1 for event, 2 for birth, 3 for death, and 4 for aviation.  Say no to end the query.';
		this.emit(':ask', message, message)		
	},
	'SelectType': function(that) {
		var typeSlotValid = isTypeSlotValid(that.event.request.intent);
		if (typeSlotValid) {
			that.attributes['queryType'] = types[parseInt(that.event.request.intent.slots.type.value)-1];
		}
		
		var message = 'Your have changed the query type to ' + that.attributes['queryType'] + '.  Which day do you want?, or say no to end the query.';
		that.emit(':ask', message, message);
	},
	'QueryEvent': function(that) {
		optionsGet.path = "/" + that.attributes['queryType'] + "/" + month + "/" + day + "/"+ pageNo + "/" + pageSize + "/api.xml";
		
		console.log(optionsGet.path);
		
		var message = '';
		
		// do the GET request
		var reqGet = http.request(optionsGet, function(res) {
			console.log("statusCode: ", res.statusCode);
			// uncomment it for header details
			// console.log("headers: ", res.headers);
		 
		 
			res.on('data', function(d) {
				//var obj = JSON.parse(d);
				parseString(d, {explicitRoot: false, mergeAttrs: true}, function (err, result) {
					console.log(JSON.stringify(result));

					console.log("\n\nstatus code -> " + result.status[0].code);
					
					if (result.status[0].code == 200) {
						var pageNumber = parseInt(result.page[0].number);
						var totalPages = parseInt(result.page[0].totalpages);
					
						//message = "You are at page " + pageNumber + ".  There are " + totalPages + " pages.  ";
						message = '';
						
						if (pageNumber < totalPages) {				
							var count = result.events[0].event.length;
							console.log("page no -> " + pageNumber + ", len -> " + count);
							
							for (var i=0; i<count; i++) {
								message += result.events[0].event[i].content + ".  ";
							}
							
							message += "Say yes to go deeper, or say no to end the query.";
							that.emit(':ask', message, message);
						} else {
							that.handler.state = states.STARTMODE;
							message += "This is the end of query.  Good-bye.";
							that.emit(':tell', message);
						}
						
					} else {
						var message = "Today In History does not response.  Please try again later.";
						that.emit(':tell', message);
					}

					console.log('Done');
				});
				
				
			});
			
			res.on('error', function(e) {
				console.error(e);

				message = "Today In History does not response.  Please try again later.";				
						that.emit(':tell', message);
			});
		 
		});
		 
		reqGet.end();
	}
};

function isTypeSlotValid(intent) {
    var typeSlotFilled = intent && intent.slots && intent.slots.type && intent.slots.type.value;
    var typeSlotIsInt = typeSlotFilled && !isNaN(parseInt(intent.slots.type.value));
    return typeSlotIsInt && parseInt(intent.slots.type.value) < 5 && parseInt(intent.slots.type.value) > 0;
}

package.json

JSON
{
  "name": "today-in-history",
  "version": "1.0.0",
  "description": "Today In History Skill",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "Alexa",
    "today",
    "history"
  ],
  "author": "vincent wong",
  "license": "ISC",
  "dependencies": {
    "alexa-sdk": "^1.0.6"
  }
}

Credits

vincent wong

vincent wong

80 projects • 203 followers

Comments