Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
vincent wong
Published

Alexa, ask the Good Luck Guru

The Good Luck Guru will tell you how your day would be like!

BeginnerFull instructions provided875

Things used in this project

Story

Read more

Code

index

JavaScript
/**
 *
 * Examples:
 * One-shot model
 * User: Alexa, get today horoscope for Leo.
 * Alexa: You will attempt to reschedule your daily routine to be able to make the most of your day. While it is easy to make plans, implementing 
 * them and sticking to them depends on your own discipline and determination. You will become more aware about health issues and pay closer 
 * attention to your diet. You will spend an enjoyable time with your family, says Ganesha.
 *
 * Dialog model:
 * User: Alexa, ask Good Luck Guru.
 * Alexa: Welcome to the Good Luck Guru.  What is your horoscope?
 * User: Leo
 * Alexa: You will attempt to reschedule your daily routine to be able to make the most of your day. While it is easy to make plans, implementing 
 * them and sticking to them depends on your own discipline and determination. You will become more aware about health issues and pay closer 
 * attention to your diet. You will spend an enjoyable time with your family, says Ganesha.
 *
 */

'use strict';
/**
 * App ID for the skill
 */
var APP_ID = "your_app_id"; 

/**
 * The key to find the current index from the session attributes
 */
var KEY_CURRENT_INDEX = "current";


var HOROSCOPES = [
	"aries",
	"taurus",
	"geminiemini",
	"cancer",
	"leo",
	"virgo",
	"libra",
	"scorpio",
	"sagittarius",
	"capricorn",
	"aquarius",
	"pisces"
];

var http = require("http");

var optionsGet = {
	host : 'horoscope-api.herokuapp.com', 
    port : 80,
    path : '', 
    method : 'GET'
};

/**
 * The AlexaSkill prototype and helper functions
 */
var AlexaSkill = require('./AlexaSkill');


/**
 * GoodLuckGuru is a child of AlexaSkill.
 * To read more about inheritance in JavaScript, see the link below.
 *
 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript#Inheritance
 */
var GoodLuckGuru = function () {
    AlexaSkill.call(this, APP_ID);
};

// Extend AlexaSkill
GoodLuckGuru.prototype = Object.create(AlexaSkill.prototype);
GoodLuckGuru.prototype.constructor = GoodLuckGuru;

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

    // any session init logic would go here
};

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

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

    // any session cleanup logic would go here
};

GoodLuckGuru.prototype.intentHandlers = {
    "DailyHoroscope": function (intent, session, response) {
        getHoroscope(intent, session, response);
    },

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

    "AMAZON.StopIntent": function (intent, session, response) {
        var speechOutput = "Goodbye";
        response.tell(speechOutput);
    },

    "AMAZON.CancelIntent": function (intent, session, response) {
        var speechOutput = "Goodbye";
        response.tell(speechOutput);
    }
};

/**
 * Returns the welcome response for when a user invokes this skill.
 */
function getWelcomeResponse(response) {
    // If we wanted to initialize the session to have some attributes we could add those here.
    var speechText = "Welcome to the Good Luck Guru. What is your horoscope?";
    var repromptText = "<speak>Please choose a horoscope by saying, " +
        "leo <break time=\"0.2s\" /> " +
        "libra <break time=\"0.2s\" /> " +
        "scorpio <break time=\"0.2s\" /> " +
        "pisces</speak>";

    var speechOutput = {
        speech: speechText,
        type: AlexaSkill.speechOutputType.PLAIN_TEXT
    };
    var repromptOutput = {
        speech: repromptText,
        type: AlexaSkill.speechOutputType.SSML
    };
    response.ask(speechOutput, repromptOutput);
}

function getHoroscope(intent, session, response) {
    var speechText = "",
        repromptText = "",
        speechOutput,
        repromptOutput;

    var horoscopeSlot = intent.slots.Horoscope;

    if (isHoroscope(horoscopeSlot)) {
        // Remove the periods to fix things like d. v. d.s to dvds
        var horoscope = horoscopeSlot.value.replace(/\.\s*/g, '');

		optionsGet.path = "/horoscope/today/" + horoscope;
		
		// 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) {
				console.info('GET result:\n');
				process.stdout.write(d);
				console.info('\n\nCall completed');

				var obj = JSON.parse(d);
				speechText = obj.horoscope;
				repromptText = "<speak>" + obj.horoscope + "</speak>";
				
				speechOutput = {
					speech: speechText,
					type: AlexaSkill.speechOutputType.PLAIN_TEXT
				};
				repromptOutput = {
					speech: repromptText,
					type: AlexaSkill.speechOutputType.SSML
				};
				response.ask(speechOutput, repromptOutput);
			});
		 
		});
		 
		reqGet.end();
		reqGet.on('error', function(e) {
			console.error(e);

			speechText = "speech text err";
			repromptText = "reprompt text err";
			
			speechOutput = {
				speech: speechText,
				type: AlexaSkill.speechOutputType.PLAIN_TEXT
			};
			repromptOutput = {
				speech: repromptText,
				type: AlexaSkill.speechOutputType.SSML
			};
			response.ask(speechOutput, repromptOutput);

		});
    } else {

        // The horoscope didn't match one of our predefined horoscopes. Reprompt the user.
        speechText = "I'm not sure what the horoscope is, please try again";
        repromptText = "<speak>I'm not sure what the horoscope is, you can say, " +
            "leo <break time=\"0.2s\" /> " +
            "libra <break time=\"0.2s\" /> " +
            "scorpio <break time=\"0.2s\" /> " +
            "pisces.</speak>";
        speechOutput = {
            speech: speechText,
            type: AlexaSkill.speechOutputType.PLAIN_TEXT
        };
        repromptOutput = {
            speech: repromptText,
            type: AlexaSkill.speechOutputType.SSML
        };
        response.ask(speechOutput, repromptOutput);
    }
}

/**
 * Check if horoscopeSlot has a valid input value.
 */
function isHoroscope(horoscopeSlot) {
	var isHoroscope = false;
	if (horoscopeSlot && horoscopeSlot.value) {
		// lower case and remove spaces
		var horoscope = horoscopeSlot.value.toLowerCase().replace(/ /g, '').replace(/\./g, '');
		
		HOROSCOPES.forEach( function(item) {
			if (item === horoscope) {
				isHoroscope = true;
				return;
			}
		});
	}
	
	return isHoroscope;
}

/**
 * Instructs the user on how to interact with this skill.
 */
function helpTheUser(intent, session, response) {
    var speechText = "You can ask for the daily horoscope. " +
        "For example, get daily horoscope for leo, or you can say exit. " +
        "Now, what can I help you with?";
    var repromptText = "<speak> I'm sorry I didn't understand that. You can say things like, " +
        "leo <break time=\"0.2s\" /> " +
        "libra <break time=\"0.2s\" /> " +
        "scorpio. Or you can say exit. " +
        "Now, what can I help you with? </speak>";

    var speechOutput = {
        speech: speechText,
        type: AlexaSkill.speechOutputType.PLAIN_TEXT
    };
    var repromptOutput = {
        speech: repromptText,
        type: AlexaSkill.speechOutputType.SSML
    };
    response.ask(speechOutput, repromptOutput);
}

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

Intent Schema

JSON
{
  "intents": [
    {
      "intent": "DailyHoroscope",
      "slots": [
        {
          "name": "Horoscope",
          "type": "LIST_OF_HOROSCOPES"
        }
      ]
    },
    {
      "intent": "AMAZON.HelpIntent"
    },
    {
      "intent": "AMAZON.StopIntent"
    },
    {
      "intent": "AMAZON.CancelIntent"
    }
  ]
}

List of Horoscopes

AsciiDoc
aries
taurus
geminiemini
cancer
leo
virgo
libra
scorpio
sagittarius
capricorn
aquarius
pisces

Sample Utterances

AsciiDoc
DailyHoroscope get daily horoscope for {Horoscope}
DailyHoroscope get today horoscope for {Horoscope}
DailyHoroscope get me {Horoscope}
DailyHoroscope daily {Horoscope}
DailyHoroscope daily horoscope in {Horoscope}
DailyHoroscope today horoscope in {Horoscope}
DailyHoroscope {Horoscope}

AlexaSkill

JavaScript
/**
    Copyright 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.

    Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at

        http://aws.amazon.com/apache2.0/

    or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/

'use strict';

function AlexaSkill(appId) {
    this._appId = appId;
}

AlexaSkill.speechOutputType = {
    PLAIN_TEXT: 'PlainText',
    SSML: 'SSML'
}

AlexaSkill.prototype.requestHandlers = {
    LaunchRequest: function (event, context, response) {
        this.eventHandlers.onLaunch.call(this, event.request, event.session, response);
    },

    IntentRequest: function (event, context, response) {
        this.eventHandlers.onIntent.call(this, event.request, event.session, response);
    },

    SessionEndedRequest: function (event, context) {
        this.eventHandlers.onSessionEnded(event.request, event.session);
        context.succeed();
    }
};

/**
 * Override any of the eventHandlers as needed
 */
AlexaSkill.prototype.eventHandlers = {
    /**
     * Called when the session starts.
     * Subclasses could have overriden this function to open any necessary resources.
     */
    onSessionStarted: function (sessionStartedRequest, session) {
    },

    /**
     * Called when the user invokes the skill without specifying what they want.
     * The subclass must override this function and provide feedback to the user.
     */
    onLaunch: function (launchRequest, session, response) {
        throw "onLaunch should be overriden by subclass";
    },

    /**
     * Called when the user specifies an intent.
     */
    onIntent: function (intentRequest, session, response) {
        var intent = intentRequest.intent,
            intentName = intentRequest.intent.name,
            intentHandler = this.intentHandlers[intentName];
        if (intentHandler) {
            console.log('dispatch intent = ' + intentName);
            intentHandler.call(this, intent, session, response);
        } else {
            throw 'Unsupported intent = ' + intentName;
        }
    },

    /**
     * Called when the user ends the session.
     * Subclasses could have overriden this function to close any open resources.
     */
    onSessionEnded: function (sessionEndedRequest, session) {
    }
};

/**
 * Subclasses should override the intentHandlers with the functions to handle specific intents.
 */
AlexaSkill.prototype.intentHandlers = {};

AlexaSkill.prototype.execute = function (event, context) {
    try {
        console.log("session applicationId: " + event.session.application.applicationId);

        // Validate that this request originated from authorized source.
        if (this._appId && event.session.application.applicationId !== this._appId) {
            console.log("The applicationIds don't match : " + event.session.application.applicationId + " and "
                + this._appId);
            throw "Invalid applicationId";
        }

        if (!event.session.attributes) {
            event.session.attributes = {};
        }

        if (event.session.new) {
            this.eventHandlers.onSessionStarted(event.request, event.session);
        }

        // Route the request to the proper handler which may have been overriden.
        var requestHandler = this.requestHandlers[event.request.type];
        requestHandler.call(this, event, context, new Response(context, event.session));
    } catch (e) {
        console.log("Unexpected exception " + e);
        context.fail(e);
    }
};

var Response = function (context, session) {
    this._context = context;
    this._session = session;
};

function createSpeechObject(optionsParam) {
    if (optionsParam && optionsParam.type === 'SSML') {
        return {
            type: optionsParam.type,
            ssml: optionsParam.speech
        };
    } else {
        return {
            type: optionsParam.type || 'PlainText',
            text: optionsParam.speech || optionsParam
        }
    }
}

Response.prototype = (function () {
    var buildSpeechletResponse = function (options) {
        var alexaResponse = {
            outputSpeech: createSpeechObject(options.output),
            shouldEndSession: options.shouldEndSession
        };
        if (options.reprompt) {
            alexaResponse.reprompt = {
                outputSpeech: createSpeechObject(options.reprompt)
            };
        }
        if (options.cardTitle && options.cardContent) {
            alexaResponse.card = {
                type: "Simple",
                title: options.cardTitle,
                content: options.cardContent
            };
        }
        var returnResult = {
                version: '1.0',
                response: alexaResponse
        };
        if (options.session && options.session.attributes) {
            returnResult.sessionAttributes = options.session.attributes;
        }
        return returnResult;
    };

    return {
        tell: function (speechOutput) {
            this._context.succeed(buildSpeechletResponse({
                session: this._session,
                output: speechOutput,
                shouldEndSession: true
            }));
        },
        tellWithCard: function (speechOutput, cardTitle, cardContent) {
            this._context.succeed(buildSpeechletResponse({
                session: this._session,
                output: speechOutput,
                cardTitle: cardTitle,
                cardContent: cardContent,
                shouldEndSession: true
            }));
        },
        ask: function (speechOutput, repromptSpeech) {
            this._context.succeed(buildSpeechletResponse({
                session: this._session,
                output: speechOutput,
                reprompt: repromptSpeech,
                shouldEndSession: false
            }));
        },
        askWithCard: function (speechOutput, repromptSpeech, cardTitle, cardContent) {
            this._context.succeed(buildSpeechletResponse({
                session: this._session,
                output: speechOutput,
                reprompt: repromptSpeech,
                cardTitle: cardTitle,
                cardContent: cardContent,
                shouldEndSession: false
            }));
        }
    };
})();

module.exports = AlexaSkill;

Credits

vincent wong

vincent wong

81 projects • 205 followers

Comments