Send Alexa on a cold-brew fueled rant on all things hipster. All the cool people are doing it. You can find the skill here. Let's get started...
AWS LambdaSince Alexa isn't quite hip enough to spit fresh out of the box she'll need to pull her hipster lexicon from an external source. Fortunately, such a thing exists in the public API: Hipster Jesus. You probably haven't heard of it, it's really underground...
- Go to the AWS Portal
- Create a Lambda function
- Type Alexa into the filter and select alexa-skills-color-expert
- For the trigger click on the dotted box on the left and choose Alexa Skills Kit
- On the next screen, name the function TalkHipster2Me, pick Node.js 4.3 for the runtime, paste in the code from the index.js file in GitHub (or copy it from below), pick lambda_basic_execution for the Execution Policy then continue onward
'use strict';
var http = require('http');
function buildSpeechletResponse(title, output, repromptText, shouldEndSession) {
return {
outputSpeech: {
type: 'PlainText',
text: output,
},
card: {
type: 'Simple',
title: `${title}`,
content: `${output}`,
},
reprompt: {
outputSpeech: {
type: 'PlainText',
text: repromptText,
},
},
shouldEndSession,
};
}
function buildResponse(sessionAttributes, speechletResponse) {
return {
version: '1.0',
sessionAttributes,
response: speechletResponse,
};
}
function startTalking(session, callback) {
getJson(
session,
function(result) {
const sessionAttributes = session.attributes;
const cardTitle = 'TalkHipster2Me';
const speechOutput = result.text + ' Shall I continue?';
const repromptText = 'Shall I continue?';
const shouldEndSession = false;
callback(sessionAttributes,
buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}
);
}
function startHelp(callback) {
const sessionAttributes = {};
const cardTitle = 'TalkHipster2Me';
const speechOutput = "Want to keep your hipster slang funky fresh? Ask me to talk hipster and I'll spit truth."
+ "Want to really impress? Ask me to mix in some Latin.";
const repromptText = 'Or not, you know, whatever';
const shouldEndSession = false;
callback(sessionAttributes,
buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}
function shutUp(session, callback) {
const sessionAttributes = session.attributes;
const cardTitle = 'TalkHipster2Me';
const speechOutput = "...zumba";
const shouldEndSession = false;
callback(sessionAttributes,
buildSpeechletResponse(cardTitle, speechOutput, null, shouldEndSession));
}
function handleSessionEndRequest(callback) {
const cardTitle = 'TalkHipster2Me';
const speechOutput = 'Alexa out!';
const shouldEndSession = true;
callback({}, buildSpeechletResponse(cardTitle, speechOutput, null, shouldEndSession));
}
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}`);
if (!session.attributes) {
session.attributes = {};
}
session.latin = false;
startTalking(session, callback);
}
function onIntent(intentRequest, session, callback) {
console.log(`onIntent requestId=${intentRequest.requestId}, sessionId=${session.sessionId}`);
const intent = intentRequest.intent;
const intentName = intentRequest.intent.name;
if (!session.attributes) {
session.attributes = {};
}
if (intentName === 'LatinIntent') {
session.attributes.latin = true;
startTalking(session, callback);
} else if (intentName === 'HipsterIntent') {
session.attributes.latin = false;
startTalking(session, callback);
} else if (intentName === 'AMAZON.YesIntent' || intentName === 'AMAZON.ResumeIntent') {
startTalking(session, callback);
} else if (intentName === 'AMAZON.PauseIntent') {
shutUp(session, callback);
} else if (intentName === 'AMAZON.HelpIntent') {
startHelp(callback);
} else if (intentName === 'AMAZON.StopIntent' || intentName === 'AMAZON.CancelIntent' || intentName === 'AMAZON.NoIntent') {
handleSessionEndRequest(callback);
} else {
throw new Error('Invalid intent');
}
}
function onSessionEnded(sessionEndedRequest, session) {
console.log(`onSessionEnded requestId=${sessionEndedRequest.requestId}, sessionId=${session.sessionId}`);
// Add cleanup logic here
}
function getJson(session, eventCallback) {
var url = "http://hipsterjesus.com/api/?paras=1&html=false";
if (session.attributes.latin !== true) {
url += "&type=hipster-centric"
}
http.get(url, function(res) {
var body = '';
res.on('data', function (chunk) {
body += chunk;
});
res.on('end', function () {
var result = JSON.parse(body);
eventCallback(result);
});
}).on('error', function (e) {
console.log("Got error: ", e);
});
}
// --------------- 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}`);
if (event.session.application.applicationId !== 'INSERT-ID-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);
}
};
- Once the function is created, find the ARN in the upper right, it should look like arn:aws:lambda:us-east-1:{UniqueID}:function:TalkHipster2Me. Save it for later
- Keep the tab open, we'll need the Skill Id in a bit
Now that we have the backend set up to handle requests, we'll need to configure the frontend. Plaid is always a good choice!
- Go to the Amazon developer portal
- Click the Alexa tab
- Choose the Alexa Skills Kit
- Click Add a New Skill
- For Skill Information set the name to TalkHipster2Me and give "talk hipster to me" for the invocation name
- Under the Interaction Model set the Intent Schema to
{
"intents": [
{
"intent": "HipsterIntent"
},
{
"intent": "LatinIntent"
},
{
"intent": "AMAZON.YesIntent"
},
{
"intent": "AMAZON.NoIntent"
},
{
"intent": "AMAZON.StopIntent"
},
{
"intent": "AMAZON.CancelIntent"
},
{
"intent": "AMAZON.HelpIntent"
},
{
"intent": "AMAZON.ResumeIntent"
},
{
"intent": "AMAZON.PauseIntent"
}
]
}
and for sample utterances put
HipsterIntent talk hipster to me
HipsterIntent talk hipster
HipsterIntent now talk hipster
HipsterIntent speak hipster
HipsterIntent be a hipster
HipsterIntent hipster
HipsterIntent do hipster
HipsterIntent now do hipster
HipsterIntent now hipster
HipsterIntent go back to hipster
HipsterIntent now go back to hipster
HipsterIntent no more latin
LatinIntent talk latin to me
LatinIntent add latin
LatinIntent now do latin
LatinIntent try latin
LatinIntent latin
LatinIntent speak latin
LatinIntent how about latin
LatinIntent can you do latin
LatinIntent do latin
LatinIntent mix in some latin
- In the Configuration tab select AWS Lambda ARN and choose North America, then paste in the ARN from your lambda function.
- Now we can go back to the Lambda function and bind it to this skill. Towards the top of the page, in small font, you should see an ID like amzn1.echo-sdk-ams.app.{UniqueID} Copy it. Back in the lamba function go to line 151 in the code and replace the placeholder id with the skill id
- For the Publishing Information tab select Novelty & Humor for the Category. After some basic testing instructions and skill descriptions put "Alexa, talk hipster to me.", "Mix in some Latin." and "Now go back to hipster." for the Example Phrases. Upload the icons from the GitHub repo or copy them from this page (I also highly recommend vectr for online vector graphics) and then it's on to Privacy.
- For Privacy and Compliance say No to everything and check the box for Export Compliance.
- Finally, you can hit that sweet, sweet Submit for Certification button. With any luck it will be listed on the store for all to enjoy.
Congratulations! Now you're Alexa enabled device is plaid to the bone!
Comments