""" This sample demonstrates an implementation of an fulfillment lambda for a fallback intent in Lex. To use this Lambda function, you will need a Dynamo DB table with the name 'tickets'. Steps to create the table: 1. Go to Dynamo DB in AWS console and and click on Create table 2. Put the Table name as 'tickets' and primary key as 'ticket_number' 3. Click on create """ import json import os import logging import boto3 import random logger = logging.getLogger() logger.setLevel(logging.DEBUG) # Get the service resource. dynamodb = boto3.resource('dynamodb') # Instantiate a table resource object tickets_table = dynamodb.Table('tickets') # --- Helpers that build all of the responses to Lex --- def elicit_slot(session_attributes, intent_name, slots, slot_to_elicit, message): return { 'sessionAttributes': session_attributes, 'dialogAction': { 'type': 'ElicitSlot', 'intentName': intent_name, 'slots': slots, 'slotToElicit': slot_to_elicit, 'message': message } } def confirm_intent(session_attributes, intent_name, slots, message): return { 'sessionAttributes': session_attributes, 'dialogAction': { 'type': 'ConfirmIntent', 'intentName': intent_name, 'slots': slots, 'message': message } } def close(session_attributes, fulfillment_state, message): response = { 'sessionAttributes': session_attributes, 'dialogAction': { 'type': 'Close', 'fulfillmentState': fulfillment_state, 'message': message } } return response def delegate(session_attributes, slots): return { 'sessionAttributes': session_attributes, 'dialogAction': { 'type': 'Delegate', 'slots': slots } } # --- Helper Functions --- def get_session_attributes(intent_request): """ Get session attributes from intent request """ return intent_request['sessionAttributes'] if intent_request['sessionAttributes'] is not None else {} def get_user_id(intent_request): """ Get user id from intent request """ return intent_request['userId'] def get_input_transcript(intent_request): """ Get input transcript from intent request """ return intent_request['inputTranscript'] def build_ticket_details(intent_request): """ Build ticket detail with user_id and input transcript """ return json.dumps({ 'user_id': get_user_id(intent_request), 'input_transcript': get_input_transcript(intent_request) }) def read_table_item(partition_key_name, partition_key_value): """ Return item read by primary key. """ response = tickets_table.get_item(Key={partition_key_name: partition_key_value}) return response def add_item(ticket_number, details, email_id='NA'): """ Add item to table. """ response = tickets_table.put_item(Item={ 'ticket_number': ticket_number, 'details': details, 'email_id': email_id }) return response # --- DynamoDB functions --- def create_ticket_entry(intent_request): ticket_number = str(random.randint(1,9999)) add_item(ticket_number, build_ticket_details(intent_request)) return ticket_number def update_ticket_entry(ticket_number, email_id): ticket = read_table_item('ticket_number', ticket_number) #Update the table add_item(ticket_number, ticket['Item']['details'], email_id) # --- Intent handler --- def handle_fallback(intent_request): """ Performs fulfillment for the fallback intent after persisting the missed utterance in a DynamoDB table. """ logger.debug('event.bot.name={}'.format(intent_request)) ticket_number = create_ticket_entry(intent_request) return elicit_slot({}, 'ContactDetails', {'ticketNumber': ticket_number}, 'emailID', { 'contentType': 'PlainText', 'content': "Sorry, I can only help with regional and quarterly revenue for now. Can you share your email ID and I'll have someone pull up that report for you?" } ) def handle_contact_details(intent_request): """ Performs fulfillment for the RegionDetails """ logger.debug('event.bot.name={}'.format(intent_request)) ticket_number = intent_request['currentIntent']['slots']['ticketNumber'] email_id = intent_request['currentIntent']['slots']['emailID'] update_ticket_entry(ticket_number, email_id) return close(get_session_attributes(intent_request), 'Fulfilled', { 'contentType': 'PlainText', 'content': 'Thank you. You should get a response soon.' } ) def handle_region_details(intent_request): """ Performs fulfillment for the RegionDetails """ logger.debug('event.bot.name={}'.format(intent_request)) regional_revenue = { 'southern': '5', 'eastern': '10', 'western': '8', 'northern': '2' } region = intent_request["currentIntent"]["slots"]["regionName"] if region in regional_revenue.keys(): return close(get_session_attributes(intent_request), 'Fulfilled', { 'contentType': 'PlainText', 'content': 'Revenue for {reg} region is ${rev}M'.format( reg = region, rev = regional_revenue[region]) } ) else: return close(get_session_attributes(intent_request), 'Fulfilled', { 'contentType': 'PlainText', 'content': 'Total revenue across all regions is $25M' } ) def handle_quarter_details(intent_request): """ Performs fulfillment for the RevenueDetails """ logger.debug('event.bot.name={}'.format(intent_request)) quarter_revenue = { 'Q1': '3', 'Q2': '5', 'Q3': '5', 'Q4': '12' } quarter = intent_request["currentIntent"]["slots"]["period"] if quarter in quarter_revenue.keys(): return close(get_session_attributes(intent_request), 'Fulfilled', { 'contentType': 'PlainText', 'content': 'Revenue for {q} is ${rev}M'.format( q = quarter, rev = quarter_revenue[quarter]) } ) else: return close(get_session_attributes(intent_request), 'Fulfilled', { 'contentType': 'PlainText', 'content': 'Total revenue across all quarters is $25M' } ) # --- Intents --- def dispatch(intent_request): """ Called when the user specifies an intent for this bot. """ logger.debug('dispatch userId={}, intentName={}'.format(intent_request['userId'], intent_request['currentIntent']['name'])) intent_name = intent_request['currentIntent']['name'] # Dispatch to your bot's intent handlers if intent_name == 'BusinessMetricsFallback': return handle_fallback(intent_request) elif intent_name == 'RegionDetails': return handle_region_details(intent_request) elif intent_name == 'QuarterDetails': return handle_quarter_details(intent_request) elif intent_name == 'ContactDetails': return handle_contact_details(intent_request) else: raise Exception('Intent with name ' + intent_name + ' not supported') # --- Main handler --- def lambda_handler(event, context): """ Route the incoming request based on intent. The JSON body of the request is provided in the event slot. """ logger.debug('event.bot.name={}'.format(event['bot']['name'])) return dispatch(event)