claudiajs/claudia-bot-builder

Unable to send responses to FB messenger after a database call

lan585 opened this issue · 2 comments

Hi, we're currently building a bot which writes the user's responses into a database (for future reference later in the conversation). However, when we execute a mysql command and return any form of textual response to the user (even simple text, non related to database return data) an error is seen in Amazon CloudWatch.

Bot Engine Used: Facebook
Database Engine + node.js library: mysql
Database location: Amazon RDS (made the database accessible in the public internet to rule out permission issues).

Code: main.js

'use strict';
const builder = require('claudia-bot-builder'),
    fbTemplate = builder.fbTemplate,
  db = require('./src/db');

module.exports = builder((request, apiReq) => {
  apiReq.lambdaContext.callbackWaitsForEmptyEventLoop = false;

db.getUserDetail('hello world', "SUBSTEP", function(err, returnData){
        if(err) return err;
        else
        {
            console.log(returnData); //this writes the expected output inside CloudWatch
            return "text to send to FB messenger"; //this yields an error inside CloudWatch
        }
    });
});

Code: db.js

const mysql = require('mysql');

var connection = mysql.createConnection({
        host: <host>,
        user: <user>,
        password: <password>,
        database: <database>,
        debug: false
    });

module.exports = {
    getUserDetail(userID, type, callback) {
    try{
        switch (type){
            case "SUBSTEP": queryString = "<actual query>";
                break;
        }

        connection.connect(); //establishes the connection.
        connection.query(queryString, function(err, data, fields) {
            if (err){
                callback(err, null);
            }
            else{
                callback(null, data[0]);
            }
        });

    }catch(ex){
        callback(ex, null);
    }
    finally{
        connection.end();
    }
  }
}

Error from CloudWatch:
{ headers: { 'www-authenticate': 'OAuth "Facebook Platform" "invalid_request" "(#100) Must send either message or state"', 'access-control-allow-origin': '*', pragma: 'no-cache', 'cache-control': 'no-store', 'facebook-api-version': 'v2.7', expires: 'Sat, 01 Jan 2000 00:00:00 GMT', 'content-type': 'text/javascript; charset=UTF-8', 'x-fb-trace-id': 'CB/lulmfQSL', 'x-fb-rev': '2503827', vary: 'Accept-Encoding', 'x-fb-debug': 'ufWokB+8KDSDGmLHKtdSmanvhD7yVHWD8JO8Mjv+8lmr7KNqLMw82XduFseKU4ZooE+3QpXgSwDCrdqSbCIbnQ==', date: 'Sun, 14 Aug 2016 18:18:44 GMT', connection: 'close', 'content-length': '127' }, body: '{"error":{"message":"(#100) Must send either message or state","type":"OAuthException","code":100,"fbtrace_id":"CB\\/lulmfQSL"}}', statusCode: 400, statusMessage: 'Bad Request' }

What has been tested (does not work sadly):

  1. Updating timeout from 3 seconds to 10 seconds. Based from the logs - Amazon Lambda responds within 1 - 3 seconds currently.
  2. Shifting the function call from a callback based to a return based function.

Any help would be much appreciated. Thanks!

gojko commented

async bots need to return a Promise object. (Check out Asynchronous replies for more info). Unless your function actually returns a Promise object, Lambda will not wait for the callback. (so the function(err,data) part of how you call the getUserDetail never gets called). I'd suggest switching the call to promises, and returning the promise out of the bot, so your bot should look something like:

module.exports = builder((request, apiReq) => {
  return db.getUserDetail('hello world', "SUBSTEP").then(result=>{
     console.log(result);
  }).then(()=> {
    return "text to send to FB messenger";
  }):
});

Thanks! Tried it and it worked. :)