It is Election year. I must admit I don't pay attention much to politics but since we have to vote this year, I want to know who represents me. First, I asked Alexa. Who is my senator? Who is my representative? Guess what her reply - "Hmm.. I don't know the answer to your question". Alexa, you know my shipping address, you gotta know?
Well, there goes an idea. I can create an Amazon Alexa skills for this. Who represents me? Very simple app yet powerful concepts I learned.
1. Storing user data, state to dynamoDB. In this example, I want to store the zip code.
2. Retrieving user data, so when I asked again after session is done, it remembers my zip code.
3. Calling a web api service and ingesting data and passing it to Amazon Echo.
This project is certified skill now. You can find the skill "Who represents me in my government?" in the Alexa skills directory.
If this project made you interested into developing Amazon Echo Skill or reminded you to VOTE this election year, please add respect and follow me. Thank you.
Step 0: PreparationI found this site http://whoismyrepresentative.com/
by submitting a zip code, it will tell me information about my representatives.
Then looking further, they setup a simple api
http://whoismyrepresentative.com/api
By sending a GET command to
http://whoismyrepresentative.com/getall_mems.php?zip=75034&output=json
it will give me the names of all my representative like this
{
"results": [
{
"name": "Sam Johnson",
"party": "R",
"state": "TX",
"district": "3",
"phone": "202-225-4201",
"office": "2304 Rayburn House Office Building",
"link": "http://samjohnson.house.gov"
},
{
"name": "Michael Burgess",
"party": "R",
"state": "TX",
"district": "26",
"phone": "202-225-7772",
"office": "2336 Rayburn House Office Building",
"link": "http://burgess.house.gov"
},
{
"name": "John Cornyn",
"party": "R",
"state": "TX",
"district": "Senior Seat",
"phone": "202-224-2934",
"office": "517 Hart Senate Office Building",
"link": "http://www.cornyn.senate.gov"
},
{
"name": "Ted Cruz",
"party": "R",
"state": "TX",
"district": "Junior Seat",
"phone": "202-224-5922",
"office": "404 Russell Senate Office Building",
"link": "http://www.cruz.senate.gov"
}
]
}
Cool! Then I can just get their names from the list.
So I started learning how to create an Amazon Alexa Skill.
The easiest tutorial I can find is this one from Amazon
I followed the steps. Then started modifying them.
The tutorial is asking for favorite color... Well, I know I need to ask for the zip code. And save the Zip code during session.
Step 1: Create Lambda SkillAfter I was able to replace favorite color to zip code question. I started to figure out how to do an HTTP GET to this whoismyrepresentative.com/api
I ended up creating a function to contact the website and build the path based from zip code. Get the resulting data and do JSON.parse
function getRepresentative(zipcode, callback) {
return http.get({
host: 'whoismyrepresentative.com',
path: '/getall_mems.php?zip=' + zipcode + '&output=json'
}, function(response) {
// Continuously update stream with data
var body = '';
response.on('data', function(d) {
body += d;
});
response.on('end', function() {
// Data reception is done, do whatever with it!
var parsed = JSON.parse(body);
callback(parsed);
});
});
}
After that, it's a matter of looping through the result and filtering the data. If I'm looking at senators, the district should have the word "Seat".
function getSenatorNames(reps) {
var totalReps = Object.keys(reps.results).length;
var repNames = "";
var totalSenators = 0;
for ( var i = 0; i < totalReps; i++)
{
var rep = reps.results[i];
var repName = rep.name;
if (rep.district.indexOf('Seat') > 0)
{
{ console.log("Name: " + repName);
"session": {
"new": false,
"sessionId": "session1234",
"attributes": {
"zipcode" : 76040
},
"user": {
"userId": "Ron"
},
"application": {
"applicationId": "amzn1.echo-sdk-ams.app.[unique-value-here]"
}
},
"version": "1.0",
"request": {
"intent": {
"slots": {
"Location": {
"name": "Zip",
"value": 76040
}
},
"name": "RepresentativeIntent"
},
"type": "IntentRequest",
"requestId": "request5678"
}
}
totalSenators+=1
if (i == totalReps - 1)
repNames = repNames + ' and ' + repName
else
repNames = repNames + repName + ', '
}
}
return {
total : totalSenators,
names : repNames
}
}
After I got this working, I started looking into saving the zip code to DynamoDB. I don't have much experience with AWS. I found good examples from
https://github.com/amzn/alexa-skills-kit-js/blob/master/samples/scoreKeeper/src/storage.js
I created a DynamoDB table called "EchoLocation".
After asking for Zip Code, I will store it in Dynamo DB. It looked something like this, I used putItem, passed the table Name, (UserID which is unique and can be used to identify an Amazon Echo), then the zip code provided. Put Item will create and update the Item in your dynamodb table. To enforce the data to be string, you have to Identify { 'S' : zipcode }, this will make sure data stored is a string.
dynamodb.putItem({
TableName: 'EchoLocation',
Item: {
UserId: {
S: session.user.userId
},
ZipCode: {
S: zipcode
}
}
}, function (err, data) {
if (err) {
console.log(err, err.stack);
}
if (callback) {
callback(sessionAttributes,
buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession))
}
});
make sure that these is included in your project.
var AWS = require("aws-sdk");
var dynamodb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
To read the Items from dynamodb, the easiest way I can find is this. Use Get Item function, pass in the UserId, it will return back the data.
Data.Item has the information that was stored previously.
Take Note: to get the correct data, you have to use data.Item.ZipCode.S, with 'S' being the string data.
dynamodb.getItem({
TableName: 'EchoLocation',
Key: {
UserId: {
S: session.user.userId
}
}
}, function (err, data) {
console.log(err)
if (err) {
speechOutput = "I'm not sure what your zip code is, you can say, my zip code is 9 0 2 1 0";
callback(sessionAttributes,
buildSpeechletResponse(intent.name, speechOutput, repromptText, shouldEndSession));
} else if (data.Item === undefined) {
speechOutput = "I'm not sure what your zip code is, you can say, my zip code is 9 0 2 1 0";
callback(sessionAttributes,
buildSpeechletResponse(intent.name, speechOutput, repromptText, shouldEndSession));
} else {
zipcode = data.Item.ZipCode.S
sessionAttributes = createZipCodeAttributes(zipcode);
getRepresentative(zipcode, function(myreps){
//list all the names of state representative
var repList = getRepresentativeNames(myreps);
repNames = repList.names;
totalReps = repList.total;
if (totalReps > 1)
speechOutput = "Your representatives are " + repNames + '.';
else
speechOutput = "Your representative is " + repNames + ".";
callback(sessionAttributes,
buildSpeechletResponse(intent.name, speechOutput, repromptText, shouldEndSession));
});
}
});
Other than that, It's about the same structure as the What's your Favorite Color project described.
Here's the github repo:
https://github.com/rondagdag/WhoRepresentsMe
Step 2: Create an Alexa SkillFill in Skill Information
Name: Who is my representative?
Invocation Name: my government
Fill in Interaction Model
The Intent Schema looks like this.
MyZipCodeIsIntent: To specifiy Zip Code
RepresentativeIntent: To ask who is my representative?
RepresentMeIntent: To ask who represents me? (Both senate and representatives)
SenatorIntent: To ask who are my senators?
{
"intents": [
{
"intent": "MyZipCodeIsIntent",
"slots": [
{
"name": "Location",
"type": "AMAZON.NUMBER"
}
]
},
{
"intent": "RepresentativeIntent",
"slots": [
{
"name": "Location",
"type": "AMAZON.NUMBER"
}
]
},
{
"intent": "RepresentMeIntent",
"slots": [
{
"name": "Location",
"type": "AMAZON.NUMBER"
}
]
},
{
"intent": "SenatorIntent",
"slots": [
{
"name": "Location",
"type": "AMAZON.NUMBER"
}
]
},
{
"intent": "AMAZON.HelpIntent"
}
]
}
Then on sample utterances, it looks like this.
RepresentativeIntent Who is my representative
RepresentativeIntent Who are my representatives
RepresentativeIntent Name my representatives
RepresentativeIntent Name my representative
RepresentativeIntent Who represents me in the U.S. Congress
RepresentativeIntent Who represents me in Congress
RepresentativeIntent Who is the representative for {Location}
RepresentativeIntent Who are the representatives for {Location}
SenatorIntent Who is my senator
SenatorIntent Who are my senators
SenatorIntent Name my senators
SenatorIntent Name my senator
SenatorIntent Who represents me in the U.S. Senate
SenatorIntent Who represents me in Senate
SenatorIntent Who is the senator for {Location}
SenatorIntent Who are the senators for {Location}
RepresentMeIntent Who represents me
MyZipCodeIsIntent my zip code is {Location}
MyZipCodeIsIntent zip is {Location}
It maps what Amazon Echo would accept to call on my lambda.
After this, I was able to test manually.
Step 3: Test on a real Echo device and DebuggingI started testing it on an Amazon Echo device, I've noticed few things.
1. I have the skill reconfirm my zip code, but I had it as 75034, so it said Seventy-Five thousand and thirty-four. So I ended up fixing this by splitting the zip code and add space in between
zipcode.split('').join(' ');
very clean, I like it.
2. If you speak slow, sometimes Alexa wouldn't catch the all digits of zip code. No Problem, just tell it again.
3. Debugging with Dynamo DB is a little bit tricky. I had to do a lot of console.log just to make sure data is being passed properly.
4. Debugging with AWS Lambda for Alexa Skills Kit is easier by creating a template message like this.
This is to change the zip code
{
"session": {
"new": false,
"sessionId": "session1234",
"attributes": {},
"user": {
"userId": null
},
"application": {
"applicationId": "amzn1.echo-sdk-ams.app.[unique-value-here]"
}
},
"version": "1.0",
"request": {
"intent": {
"slots": {
"Location": {
"name": "ZipCode",
"value": 75034
}
},
"name": "MyZipCodeIsIntent"
},
"type": "IntentRequest",
"requestId": "request5678"
}
}
This is to send the RepresentativeIntent
{
"session": {
"new": false,
"sessionId": "session1234",
"attributes": {
"zipcode" : 76040
},
"user": {
"userId": "Ron"
},
"application": {
"applicationId": "amzn1.echo-sdk-ams.app.[unique-value-here]"
}
},
"version": "1.0",
"request": {
"intent": {
"slots": {
"Location": {
"name": "Zip",
"value": 76040
}
},
"name": "RepresentativeIntent"
},
"type": "IntentRequest",
"requestId": "request5678"
}
}
Step 4: PublishingThis is my first time to publish and Amazon Alexa Skill, One thing that tricked me is the example Phrases. Make sure that you specify the wake word and the invocation name in the first phrase. Very critical.
On privacy and compliance, since I am collecting users' personal information which is Zip Code, I have to create a Privacy Policy URL. I ended up using github pages for this. http://rondagdag.github.io/WhoRepresentsMe/
I followed the instructions from https://pages.github.com/
1. Storing user data, state to dynamoDB. In this example, I want to store the zip code.
2. Retrieving user data, so when I asked again after session is done, it remembers my zip code.
3. Calling a web api service and ingesting data and passing it to Amazon Echo.
If this project made you interested into developing Amazon Echo Skill or reminded you to VOTE this election year, please add respect and follow me. Thank you.
Know your representatives. Your Vote Counts
If this project made you interested into developing Amazon Echo Skill or reminded you to VOTE this election year, please add respect and follow me. Thank you.
Comments