facebookamazon-web-servicesaws-lambdaamazon-lexfacebook-chatbot

Facebook Messenger doesn't display messages returned by bot's lambdas


I created a chatbot with Amazon Lex and integrated it with Facebook Messenger. Responses defined in Lex Console (Response section) are returned to the messenger (see upper right image on picture 1), but responses generated by lambda functions (written in Python) aren't received (see lower right image on picture 1) -- the triple dot icon is displayed for 30 seconds and it disappears.

I integrated the bot with FB using this video and AWS documentation. In Webhooks subscriptions (on FB developers panel) I chose following events: messages, messaging_postbacks, messaging_optins, message_deliveries, message_reads, messaging_payments, message_echoes, standby, messaging_handovers. The app isn't approved by FB yet but the application status is Live.

Picture 1: pic1
(source: imggmi.com)

Picture 2: pic2
(source: imggmi.com)

import json

user_invoices = ['1/2019', '2/2019', '3/2019']
invoice_status = {'1/2019' : 'Paid', '2/2019' : 'Unpaid', '3/2019' : 'Unpaid'}

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 close(session_attributes, fulfillment_state, message):
    return {
        'sessionAttributes': session_attributes,
        'dialogAction': {
            'type': 'Close',
            'fulfillmentState': fulfillment_state,
            'message': message
        }
    }

def handle(event, context):
    intent_request = event
    session_attributes = intent_request['sessionAttributes']
    slots = intent_request['currentIntent']['slots']
    
    if intent_request['currentIntent']['name'] == 'CheckInvoiceStatus':
        session_attributes['lastIntent'] = intent_request['currentIntent']['name']
        try:
            inv_no = slots['invoiceNo']
            if inv_no == None and intent_request['invocationSource'] == 'DialogCodeHook':
                return elicit_slot(
                    session_attributes,
                    intent_request['currentIntent']['name'],
                    slots,
                    'invoiceNo',
                    {
                        'contentType': 'PlainText',
                        'content': 'Please enter invoice number.'
                    }
                )
                
            session_attributes['invoiceNo'] = inv_no
            if not inv_no in user_invoices:
                return delegate(session_attributes, intent_request['currentIntent']['slots'])
            inv_status = invoice_status[inv_no]
            return close(
                session_attributes,
                'Fulfilled',
                {
                    'contentType': 'PlainText',
                    'content': 'Invoice no. {} is {}.'.format(inv_no, inv_status.lower())
                }
            )
        except KeyError as e:
            return close(
                session_attributes,
                'Fulfilled',
                {
                    'contentType': 'PlainText',
                    'content': 'error: {}, input: {}.'.format(e, event)
                }
            )
    
    elif intent_request['currentIntent']['name'] == 'AutoWelcomeMessage':
        if 'userName' in session_attributes:
            content = 'Hello ' + session_attributes['userName'] + '. How can I help you?'
        else:
            content = 'Hello. How can I help you?'
                
        return close(
                session_attributes,
                'Fulfilled',
                {
                    'contentType': 'PlainText',
                    'content': content
                }
            )

I expect Lex will be able to execute the lambda functions and return the values to the user the same as it happens while tesing the bot in Amazon Lex console.


Solution

  • Solved.

    My code used sessionAttributes from intent_request and the problem was that when there are no session attributes, the corresponding JSON element is set as empty, i.e. "sessionAttributes" : {} (as you can see on image 1), but in case of message from Facebook this element is set to None, i.e. "sessionAttributes" : None (as you can see on image 2).

    This way I got errors like:

    [ERROR] TypeError: argument of type 'NoneType' is not iterable
    Traceback (most recent call last):
      File "/var/task/lambda_function.py", line 104, in handle
        if 'userName' in session_attributes:
    

    or

    [ERROR] TypeError: 'NoneType' object does not support item assignment
    Traceback (most recent call last):
      File "/var/task/lambda_function.py", line 54, in handle
        session_attributes['lastIntent'] = intent_request['currentIntent']['name']
    

    I discovered this by looking at lambda function logs (on lambda function desing webpage click Monitoring tab and View logs in CloudWatch).

    Image 1:

    image1
    (source: imggmi.com)

    Image 2:

    image2
    (source: imggmi.com)