The basic premise of this skill is to be able to quickly and easily find out who represents a state in the US Congress, and to be able to find some biographical information about a specific current or former member of congress. If the user asks for information about a current congressperson, the skill also creates a card with their contact information, to make it easy to find out more about them (by checking their website or social media), and to gently nudge the user into considering how they might contribute their perspective to the political process as well.
I made use of the excellent GovTrack API*, which offers a huge amount of information without even requiring an authorization key.
StatesThis was definitely the easier of the two requests to manage. Amazon has a built in intent slot for US States - AMAZON.US_STATE - that is built to handle both full state names and state abbreviations. (You can find all of the built in slot types here.) This was helpful because I didn't have to do it myself, and because I might not have thought to include everything they have.
The built in state slot was, in a way, also unhelpful, because it allowed abbreviations, which required a bit of extra work. The getStateAbbrevFromIntent function needed to return both a state name for the voice interface, and a state abbreviation, because that was the only variable the API accepted. My STATELIST was set up for state name keys, and state abbreviation values. I could have used something like Lodash to do a reverse search by value, but the stats were suggesting that every additional require was slowing down the program (unsurprisingly), and it seemed likely that Lodash would need updating long before the US added or removed a state, so I just added ABBREVLIST instead. If the US changes it's membership I'll update the skill.
Once I had the right state name and abbreviation, it was just an https GET request to the API with the right value and I had my response. For the sake of being informative, I listed the names and descriptions of current congresspeople when fewer than 10 results were returned. For the sake of brevity, I listed only the names of the congresspeople when 10 or more were returned. You're welcome, anyone who asks about California.
PeopleThe GovTrack API allows you search a name when using the /person/ access point. It will accept a first name, a last name, or both, and the first name can be a proper or nickname. It does not have any amount of 'fuzzy search' built into it, only exactly spelled names work. It will return everyone by the name entered, which will only be one for "Mark Takai", but many more entries if you were to search only for "Chuck".
Amazon again has a handy built in slot for first names - AMAZON.US_FIRST_NAME - which does some of the very heavy name lifting. It does not have the same for last names, so I was on my own for that. And since the API has no wiggle room for almost right names, I created a custom slot that included the last names of everyone who has ever held a congressional position in the US. The total number of API results is over 12,000, so without automating it I'd still be working on it.
I made it by running a function on all of the /person/ results from the GovTrack API and then removing duplicate names because slots aren't allowed duplicates. I used PHP because I know it better, going 1000 names at a time, and using array_unique() to clean out extras. After I had the whole list together I cleaned the array again. This in-browser tool also does a nice job of unduplicating if you need it.
The slot list helped, but to improve the results even more, I added Fuse.js to help bridge the gap. By applying Fuse + a list of first and last names of congresspeople (name.js), I could radically improve the odds that the name someone said was the name of an actual congressperson. Unfortunately, when the list went beyond about 2000 names the function was much more likely to break, so I limited it to the 2000 current and most recently retired congresspeople. It's an imperfect solution, but should be enough to make names from current events work reliably.
If the API can't find anyone by that name entered, it apologizes and asks you try again.
If it finds multiple people with that name, it lists them out and asks which one you meant.
And if the result is a single person, the function grabs the id from that person, and uses it in another https GET for the /role/ endpoint, which brings up much more information than /person/ did.
From that result, it constructs a voice reply that includes the name, description, years, party, and last role including any special titles of anyone who is no longer in congress; and for current members it includes the same list, but also generates a card with contact information.
The API returns often returns a gender, but that may be missing in older entries. To manage this, I created a set of variables that printed as he/she/they, him/hers/theirs, etc, for efficiency in creating the voice output. The rest of the function just pieces together API data with spoken english.
Built in Intents - Welcome, Help, Cancel, StopWhen the Know Your Congress skill is launched, it may be invoked by something like "Alexa, ask Know Your Congress who Charles Grassley is" - which sends it into the above name (or state) function, or it may be invoked with "Alexa, open Know Your Congress." If it is opened without any specific request there's a welcome greeting in the handleWelcomeRequest function, with a reprompt that gives more information about how to make the skill work.
If the user says 'Help', it replies with additional guidance and sample phrases, contained in the handleHelpRequest function. I also added another help option - a list of supported states, territories, and districts - invoked by asking for supported states, with a response handled by the handleSupportedStateRequest function. Beyond the basic 50 US states, there are a handful of other places that are allowed to send representatives to congress, and I used this to help users find them.
Cancel and Stop are both built in, and if either is invoked, this skill just says 'Goodbye'. I personally dislike when you try to quit a program and it tries to drag things out with 'are you sure' or something meant to make you feel guilty, so I kept it simple.
*You know that excellent GovTrack API? On the day I was wrapping up my last bits of typo cleanup and adjustment, the developers page suddenly had a nice new banner at the top saying that this API would be depreciated Summer of 2017, and that users should migrate to another service, suggesting that ProPublica might be a good choice. So for now I'm done, but I'll be rebuilding it all by next summer…
Setting Up this Alexa Skill- 1. Sign up for Amazon's AWS service - aws.amazon.com.
- 2. Choose the 'Lambda' service, and be sure to choose "US East - N. Virginia" as the location in the header bar, way to the right.
- 3. Create a new function.
- 4. Choose a "Blank Function".
- 5. Click the border box and choose Alexa Skills Kit.
- 6. Name your function - I chose knowYourCongress, make sure Node.js 4.3 is selected.
- 7. Zip together everything in the "src" folder in my repository. Click "edit code inline" by "Code entry type", choose Upload a .ZIP file.
- 8. Under "Lambda function handler and role" leave "Role" as "Choose an existing role", and in the "Existing Role" blank choose "service-role/lambda_basic_execution".
- 9. Upload your zip and click "Next" at the bottom. Click "Create function" at the bottom of the next page.
- 10. Now head over to the Alexa Developer Portal. developer.amazon.com. Click "Alexa" in the header, then under "Alexa Skills Kit" click "Get Started." You will click the same thing later when you're trying to get to a skill you're already working on, even if you've already started.
- 11. Click "Add a New Skill" in the upper right.
- 12. Fill in a Name, and Invocation Name - what people will say to open it(Know Your Congress), and leave Audio Player set to 'no'.
- 13. From the repository, copy the contents of "IntentSchema.json" into the Intent Schema box, the contents of "SampleUtterances.txt" into the Sample Utterances box, then click "Add Slot Type", enter the name "LIST_OF_LAST_NAMES", then paste in the contents of "LIST_OF_LAST_NAMES", then save. Wait while the model is built, then click "Next".
- 14. In Global Fields, choose AWS Lambda ARN and North America. Go over to your Lambda function in AWS, and in the top right find ARN - arn:aws:lambda:us-east-WHOLE-BUNCH-OF-NUMBERS:function:knowYourCongress. Copy it, and paste it in the "North America" box.
At the top of your Developer Console, under where it says Know Your Congress (or whatever name you've given your skill) it says ID: amzn1.ask.skill.whole-bunch-of-numbers. Copy that, go over to your Lambda function, and paste it in next to "var APP_ID =", on line 4 or so of the function.
- 15. Now to test. Try putting in various utterances - 'who is charles grassley', 'who represents iowa', 'who is chuck', and seeing the results. This was the easiest way I found to debug code, and really handy for making sure that Alexa said things the way I wanted her to. Click "Listen" under the response to hear how your result sounds. When you're done, click "Next".
- 16. Choose a category, enter a bit about how the skill works, and then fill in the Short and Full Skill Description. Those are marketing - make people want the skill. Enter a few Example Phrases, add some keywords and logos, then click "Next".
- 17. This skill doesn't take money, doesn't collect user information, isn't targeted at the under 13 set, and meets the Export Compliance requirement. When that's all checked and suck, click "Submit for Certification".
That's it, you're done until Amazon emails you. If you're doing a different skill than this pre-approved one, it's likely they'll email you back a few mistakes they found. In my case my first round response was all dumb little typos that I had missed from staring at a screen for too long. I found the email from Amazon to be specific and helpful.
Comments