Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
132 lines (94 sloc) 4.26 KB
import yaml
from .logger import get_logger
from .request import Request
from .response import (
build_empty_response,
build_response_payload,
)
from .session import Session
_logger = get_logger()
def _load_state_machine(filename):
with open(filename, 'r') as fh:
return yaml.load(fh)
class LazySusanApp(object):
def __init__(self, state_file, session_key="LAZYSUSAN_STATE"):
self.__state_machine = _load_state_machine(state_file)
self.__session_key = session_key
@staticmethod
def get_user_id_from_event(event):
"""Lookup the userId in the request in multiple locations."""
try:
return event["session"]["user"]["userId"]
except KeyError:
# Note that session can be empty when we're playing long-form audio
pass
try:
return event["context"]["System"]["user"]["userId"]
except KeyError:
pass
_logger.error(event)
raise Exception("Could not find userId in lambda event")
def build_response(self, request, session, intent_name, context, user_id):
state = session.get_state()
_logger.info("Current state: %s", state)
# Determine what our response is by looking up our current state followed by
# the intent_name in the request. Each state must define a "default" branch.
branches = self.__state_machine[state]["branches"]
try:
branch_name = branches[intent_name]
except KeyError:
branch_name = branches["default"]
# Allow us to specifically short-circuit and *not* return a request. This is due to
# certain cases where a callback from Alexa does not accept any type of response. Typically
# this is during audio playback for long-form audio.
if branch_name is None:
session.update_audio_state(context)
# For states where alexa does not want a response, we need to exit without returning
# anything to prevent an Alexa platform error. As of this time, this has not been
# documented clearly by Amazon.
return build_empty_response()
# now branch_name is the next state
if callable(branch_name):
branch_name_or_response = branch_name(
request=request,
session=session,
intent_name=intent_name,
context=context,
user_id=user_id,
state_machine=self.__state_machine
)
if isinstance(branch_name_or_response, dict):
return branch_name_or_response
# now it's just a key
branch_name = branch_name_or_response
branch = self.__state_machine[branch_name]
if branch.get("is_state", True):
session.update_state(branch_name)
session.update_audio_state(context)
response = branch["response"]
if branch.get("get_audio_offset_from_session", False):
offset = session.get_audio_offset()
response["directives"][0]["audioItem"]["stream"]["offsetInMilliseconds"] = offset
response_payload = build_response_payload(response, session.get_state_params())
return response_payload
def handle(self, event, lambda_context=None): #pylint: disable=locally-disabled,unused-argument
"""Main handler which receives initial request from Lambda.
Route the incoming request based on type LaunchRequest, IntentRequest, etc.).
:attr event: JSON payload which is sent from the Alexa service, containing relevant
info about the request
:attr context: Lambda context
"""
# Leave this in for logging
_logger.info("Event: %s", event)
request = Request(event["request"])
context = event.get("context")
user_id = LazySusanApp.get_user_id_from_event(event)
session = Session(user_id, self.__session_key, event)
if session.is_expired:
session.clear()
session.last_request_time = request.timestamp
response = self.build_response(request, session, request.intent_name, context, user_id)
session.save()
# Leave this in for logging
_logger.info("Response: %s", response)
return response