Since New Year’s Eve I’ve been developing a custom Alexa skill as my “weekend project”. It’s a very simple app that keeps track of the list of people in the house.
At the moment I have a very simple list of names stored in DynamoDB, but if I’m going to make this app available to anyone else, I need to maintain a separate list for each user.
Every time my Lambda function is invoked, it’s passed some JSON describing the request. This includes a session object containing, amongst other things, three useful identifiers
Get the Application ID for your skill from the developer console
The Application ID identifies the Alexa skill making the request. The Lambda function should check that it’s sent the Application ID that it expects to make sure it’s not somehow being called by, well, something unexpected. You get the application ID you should expect from the Skill information in the Amazon developer console.
When you invoke an Alexa skill, you start a Session, identified by…you guessed it, a Session ID. If you want to you can include information in the form of Session Attributes in the response to your Lambda function, and when it gets invoked again within the same session you’ll get the same session ID and attributes included in the request JSON.
And finally there is a User ID. I’m going to use the User ID as a key to identify the different lists of people in each user’s house.
I’ll use the User ID as the Partition key in my Dynamo table, and the names of the people in the house as the Sort key.
At least for now, it’s not going to support having two people with the same name in the same house, but I think that will do for now. Minimal Viable Product and all that.
You can’t modify the keys for an existing Dynamo table, so I’m creating a new one, called “WhoIsInTheHouses”.
I need to refer to this new table in my Lambda function. (If you’re new to this series of posts — yes, dear reader, I am using Python rather than Node.js for my Lambda code. It’s allowed!)
table = dynamodb.Table('WhoIsInTheHouses')
My functions for adding and deleting names should take the User ID as well as the name.
def add_name(userID, name):try:response = table.put_item(Item={'UserID': userID,'Name': name})except ClientError as e:print(e.response)return e.response['Error']['Code']
return None
def delete_name(userID, name):try:response = table.delete_item(Key={'UserID': userID,'Name': name})except ClientError as e:print(e.response)return e.response['Error']['Code']
return None
And when we get a list of names, we only want to get the ones for this particular user. Previously I was doing a scan to get the whole table, but now I want a query that only selects items with a matching User ID.
response = table.query(KeyConditionExpression=Key('UserID').eq(userID))
As mentioned, the User ID is amongst the JSON passed in every time the Lambda function is invoked, so it’s easy to get this out.
userID = event['session']['user']['userId']
There are some additional straightforward changes that I won’t bore you with now, to pass userID to wherever it’s needed.
I can check that the code is working simply by invoking my Alexa skill, but that always passes in my own user ID. To check that it will work when it’s exposed to others, I can use the test events for the Lambda function, making up a user ID to pass in. Here are the interesting bits of a test event for adding a name:
{"session": {..."user": {"userId": "amzn1.ask.account.a-test-user-ID"},"application": {"applicationId": "amzn1.ask.skill.26c...}},"version": "1.0","request": {"locale": "en-UK","timestamp": "2016-10-27T21:06:28Z","type": "IntentRequest","requestId": "amzn1.echo-api.request.[unique-value-here]","intent": {"name": "ArrivedInTheHouse","slots": {"Name": {"name": "Name","value": "Gemma"}}}},"context": {...}}
Checking the DynamoDB table I can see that this has successfully added an item for Gemma with the test User ID.
And, if I ask Alexa who is in the house, she tells me
Liz and Phil are here in the house
but correctly doesn’t mention Gemma. So we successfully have separate lists for each Alexa user!
Please hit that lovely green heart to help other people find this post if they’re interested in developing an Alexa custom skill!
I’m seriously considering writing a book about writing Alexa apps. If you think that’s a good idea, your encouragement would mean a lot.