Saturday, June 2, 2018

Alexa Skill Shopping List- Python

The following Python code asks for user consent to access the Alexa Shopping List.
Once permission is granted in the companion app, the skill can get the items in customer's Shopping List and add items to the Shopping List.
def build_speechlet_response(title, output, reprompt_text, should_end_session):
    return {
        'outputSpeech': {
            'type': 'PlainText',
            'text': output
        },
        'card': {
            'type': 'Simple',
            'title': "SessionSpeechlet - " + title,
            'content': "SessionSpeechlet - " + output
        },
        'reprompt': {
            'outputSpeech': {
                'type': 'PlainText',
                'text': reprompt_text
            }
        },
        'shouldEndSession': should_end_session
    }

def build_permissions_consent_response(title, output, reprompt_text, should_end_session):
    return {
        'outputSpeech': {
            'type': 'PlainText',
            'text': output
        },
        'card': {
            'type': 'AskForPermissionsConsent',
            'permissions': [
           "read::alexa:household:list",
     "write::alexa:household:list"
     ]
        },
        'reprompt': {
            'outputSpeech': {
                'type': 'PlainText',
                'text': reprompt_text
            }
        },
        'shouldEndSession': should_end_session
    }

def build_response(session_attributes, speechlet_response):
    return {
        'version': '1.0',
        'sessionAttributes': session_attributes,
        'response': speechlet_response
    }

def handle_get_shopping_list_intent(intent, session, context):
    session_attributes = {}
    if "attributes" in session and "userInfo" in session["attributes"]:
        session_attributes["userInfo"] = session["attributes"]["userInfo"]
    card_title = "Get Shopping List Intent"
    reprompt_text = "Please ask me product question"
    should_end_session = False
    try:
        url = "https://api.amazonalexa.com:443/v2/householdlists/"
        request = urllib2.Request(url, \
                  headers = {"Authorization": "Bearer " + context["System"]["apiAccessToken"]})
        response = json.loads(urllib2.urlopen(request).read())
        alexaList = response["lists"]
        shoppingList = {}
        for item in response["lists"]:
            print(item)
            if item["name"] == "Alexa shopping list":
                shoppingList = item
        print(shoppingList)
        url = "https://api.amazonalexa.com:443"
        for item in shoppingList["statusMap"]:
            if item["status"] == "active":
                url += item["href"]
        request = urllib2.Request(url, \
                  headers = {"Authorization": "Bearer " + context["System"]["apiAccessToken"]})
        response = json.loads(urllib2.urlopen(request).read())
        print(response)
        if len(response["items"]) == 0:
            speech_output = "Currently there are no items in your Alexa Shopping List"
        else:
            item_set = Set([])
            speech_output = "Currently the following items are in your Alexa Shopping List: "
            for i in range(0, len(response["items"])):
                item_set.add(response["items"][i]["value"])
            for item in item_set:
                speech_output += item + ", "
    except urllib2.HTTPError as err:
        if err.code == 403:
            speech_output = "Please grant shopping list permissions to this skill in your Alexa app."
            return build_response({}, build_permissions_consent_response(
        card_title, speech_output, reprompt_text, should_end_session))
        else:
            speech_output = "Something went wrong while getting your shopping list"
    except:
        speech_output = "Something went wrong while getting your shopping list"
    return build_response({}, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))

def handle_add_shopping_list_intent(intent, session, context):
    session_attributes = {}
    if "attributes" in session and "userInfo" in session["attributes"]:
        session_attributes["userInfo"] = session["attributes"]["userInfo"]
    card_title = "Add to Shopping List Intent"
    reprompt_text = "Please ask me product question"
    should_end_session = False
    if "value" not in intent["slots"]["product"]:
        return build_response(session_attributes, build_speechlet_response_with_directive_nointent())
    product_name = intent["slots"]["product"]["value"]
    product_name = spell_check(product_name)
    try:
        url = "https://api.amazonalexa.com:443/v2/householdlists/"
        request = urllib2.Request(url, \
                  headers = {"Authorization": "Bearer " + context["System"]["apiAccessToken"]})
        response = json.loads(urllib2.urlopen(request).read())
        alexaList = response["lists"]
        shoppingList = {}
        for item in response["lists"]:
            print(item)
            if item["name"] == "Alexa shopping list":
                shoppingList = item
        print(shoppingList)
        url = "https://api.amazonalexa.com:443"
        for item in shoppingList["statusMap"]:
            if item["status"] == "active":
                href = item["href"].replace("active", "items")
                url += href
        print(url)
        data = {"value": product_name, "status": "active"}
        #data = urllib.urlencode(data)
        request = urllib2.Request(url)
        headers = {"Authorization": "Bearer " + context["System"]["apiAccessToken"], "Content-Type": "application/json"}
        request.add_header("Content-Type", "application/json")
        request.add_header("Authorization", "Bearer " + context["System"]["apiAccessToken"])
        result = urllib2.urlopen(request, json.dumps(data))
        response = json.loads(result.read())
        print(response)
        if result.getcode() >= 200 and result.getcode() < 300:
            speech_output = product_name + " is added to your Alexa Shopping List"
        else:
            speech_output = "Something went wrong while adding " + product_name + " to your Shopping List"
    except urllib2.HTTPError as err:
        if err.code == 403:
            speech_output = "Please grant shopping list permissions to this skill in your Alexa app."
            return build_response({}, build_permissions_consent_response(
        card_title, speech_output, reprompt_text, should_end_session))
        else:
            speech_output = "Something went wrong while adding " + product_name + " to your shopping list"
    except:
        speech_output = "Something went wrong while adding " + product_name + " to your Shopping List"
    return build_response({}, build_speechlet_response(
        card_title, speech_output, reprompt_text, should_end_session))

def on_intent(intent_request, session, context):
    """ Called when the user specifies an intent for this skill """

    print("on_intent requestId=" + intent_request['requestId'] +
          ", sessionId=" + session['sessionId'])

    intent = intent_request['intent']
    intent_name = intent_request['intent']['name']

    # Dispatch to your skill's intent handlers
    if intent_name == "GetShoppingListIntent":
        return handle_get_shopping_list_intent(intent, session, context)
    elif intent_name == "AddToShoppingList":
        return handle_add_shopping_list_intent(intent, session, context)


# --------------- Main handler ------------------

def lambda_handler(event, context):
    """ Route the incoming request based on type (LaunchRequest, IntentRequest,
    etc.) The JSON body of the request is provided in the event parameter.
    """
    print("event.session.application.applicationId=" + 
          event['session']['application']['applicationId'])

    """
    Uncomment this if statement and populate with your skill's application ID to
    prevent someone else from configuring a skill that sends requests to this
    function.
    """
    # if (event['session']['application']['applicationId'] !=
    #         "amzn1.echo-sdk-ams.app.[unique-value-here]"):
    #     raise ValueError("Invalid Application ID")

    

    if event['request']['type'] == "LaunchRequest":
        return on_launch(event['request'], event['session'])
    elif event['request']['type'] == "IntentRequest":
        return on_intent(event['request'], event['session'], event['context'])
    elif event['request']['type'] == "SessionEndedRequest":
        return on_session_ended(event['request'], event['session'])


Alexa Skill Account Linking- Python

For setting up account linking in Alexa Skill, follow the steps here.
Once setup is done, and intent handler needs to be implemented in the Python code: 

# --------------- Helpers that build all of the responses ----------------------

def build_user_authentication_response():
    return {
        "version": "1.0",
        "response": {
            "outputSpeech": {
                "type": "PlainText",
                "text": " Please use the companion app to authenticate on Amazon to start using this skill"
            },
            "card": {
                "type": "LinkAccount"
            },
            "shouldEndSession": false
        },
        "sessionAttributes": {}
    }

# --------------- Method that gets called when there is an intent request ----------------------

def on_intent(intent_request, session, context):
    """ Called when the user specifies an intent for this skill """

    print("on_intent requestId=" + intent_request['requestId'] +
          ", sessionId=" + session['sessionId'])

    intent = intent_request['intent']
    intent_name = intent_request['intent']['name']
    if intent_name == "SayHello":
        return build_user_authentication_response()


# --------------- Main handler ------------------

def lambda_handler(event, context):
    """ Route the incoming request based on type (LaunchRequest, IntentRequest,
    etc.) The JSON body of the request is provided in the event parameter.
    """
    print("event.session.application.applicationId=" + 
          event['session']['application']['applicationId'])

    """
    Uncomment this if statement and populate with your skill's application ID to
    prevent someone else from configuring a skill that sends requests to this
    function.
    """
    # if (event['session']['application']['applicationId'] !=
    #         "amzn1.echo-sdk-ams.app.[unique-value-here]"):
    #     raise ValueError("Invalid Application ID")

    if event['request']['type'] == "LaunchRequest":
        return on_launch(event['request'], event['session'])
    elif event['request']['type'] == "IntentRequest":
        return on_intent(event['request'], event['session'], event['context'])
    elif event['request']['type'] == "SessionEndedRequest":
        return on_session_ended(event['request'], event['session'])

This will open a Login with Amazon screen where customers can sign in using their Amazon Account Credentials. Once logged-in successfully, the account is linked and next time onwards the user token comes as part of the session request.

Sample Request after Account Linking:

{
    "version": "1.0",
    "session": {
        "new": true,
        "sessionId": "session-id",
        "application": {
            "applicationId": "application-id"
        },
        "user": {
            "userId": "user-id",
            "accessToken": "access-token",
            "permissions": {
                "consentToken": "consent-token"
            }
        }
    },
    "context": {
        "AudioPlayer": {
            "playerActivity": "IDLE"
        },
        "Display": {},
        "System": {
            "application": {
                "applicationId": "application-id"
            },
            "user": {
                "userId": "user-id",
                "accessToken": "access-token",
                "permissions": {
                    "consentToken": "consent-token"
                }
            },
            "device": {
                "deviceId": "device-id",
                "supportedInterfaces": {
                    "AudioPlayer": {},
                    "Display": {
                        "templateVersion": "1.0",
                        "markupVersion": "1.0"
                    }
                }
            },
            "apiEndpoint": "https://api.eu.amazonalexa.com",
            "apiAccessToken": "api-access-token"
        }
    },
    "request": {
        "type": "LaunchRequest",
        "requestId": "request-id",
        "timestamp": "2018-06-03T05:22:36Z",
        "locale": "en-US",
        "shouldLinkResultBeReturned": false
    }
}