get json data from realtime database to Dialogflow

2019-07-08 23:32发布

问题:

Beginner here on programming, I'm working on my free time on a project which is related to Google Assistant, for the first time i'm using the Firebase Realtime Database and don't know how to get the data from there, the code below is in Dialogflow's inline editor, where is person1 2 and 3 (groups) inside groups are students with credits. I have made some changes and put these three (person1 2 and 3) in the database picture below, i want to remove these three groups from code and replace it with the ones in realtime database.

Because this is my first time using that database, I don't know how to get that data from there with nodejs.

'use strict';
const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements 
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
  const agent = new WebhookClient({ request, response });
  console.log('Dialogflow Request headers: ' + JSON.stringify(request.headers));
  console.log('Dialogflow Request body: ' + JSON.stringify(request.body)); 
  function welcome(agent) {
    agent.add(`Welcome to my agent!`);
  } 
  function fallback(agent) {
    agent.add(`I didn't understand`);
    agent.add(`I'm sorry, can you try again?`);
  }
      function personFacts(agent) {
        const persons =  {
          person1: {"Alex" : 25, "Jennifer" : 45, "Justin" : 35, "Peter" : 89},
          person2: {"Alex" : 95, "Jennifer" : 75, "Justin" : 85, "Peter" : 59},
          person3: {"Alex" : 67, "Jennifer" : 55, "Justin" : 45, "Peter" : 15},
        };
        const personId = agent.parameters["personId"];
        const personMeasurement = agent.parameters["personMeasurement"];        
        const person = persons[personId];
        const result = person[personMeasurement];       
        agent.add(`${personId}'s ${personMeasurement} is ${result}`); 
      }

      let intentMap = new Map();
      intentMap.set('Default Welcome Intent', welcome);
      intentMap.set('Default Fallback Intent', fallback);
      intentMap.set('person', personFacts);
      agent.handleRequest(intentMap);
    });

UPDATE I used the code below, like this:

'use strict';
const functions = require( 'firebase-functions' );
const {WebhookClient} = require( 'dialogflow-fulfillment' );
const {Card, Suggestion} = require( 'dialogflow-fulfillment' );
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest( ( request, response ) =>{
  const agent = new WebhookClient( {request, response} );
  console.log( 'Dialogflow Request headers: ' + JSON.stringify( request.headers ) );
  console.log( 'Dialogflow Request body: ' + JSON.stringify( request.body ) );

  function welcome( agent ){
    agent.add( `Welcome to my agent!` );
  }

  function fallback( agent ){
    agent.add( `I didn't understand` );
    agent.add( `I'm sorry, can you try again?` );
  }

  function personFacts( agent ){
    const personId = agent.parameters["personId"];
    const personMeasurement = agent.parameters["personMeasurement"];
    var admin = require( 'firebase-admin' );
    admin.initializeApp( {
      credential: admin.credential.cert( {
        projectId: ' ',
        clientEmail: ' ',
        privateKey: ' '
      } ),
      databaseURL: 'https://*****.firebaseio.com'
    } );
    var db = admin.database();
    var ref = db.ref( `person/${personId}/${personMeasurement}` );
    return ref.once( "value" ).then( snapshot =>{
      var result = snapshot.value();
      agent.add( `${personId}'s ${personMeasurement} is ${result}` );
    } )
      .catch( err =>{
        agent.add( 'uh oh, something went wrong.' );
        console.error( err );
      } );
  }

  let intentMap = new Map();
  intentMap.set( 'Default Welcome Intent', welcome );
  intentMap.set( 'Default Fallback Intent', fallback );
  intentMap.set( 'factory', factoryFacts );
  agent.handleRequest( intentMap );
} );
got error message : 'MalformedResponse Failed to parse Dialogflow response into AppResponse because of empty speech response.' error from log :

{
 insertId:  "v*****2"  
 labels: {
  channel:  "preview"   
  querystream:  "GOOGLE_USER"   
  source:  "JSON_RESPONSE_VALIDATION"   
 }
 logName:  "projects/****/logs/actions.googleapis.com%2Factions"  
 receiveTimestamp:  "2019-01-07T14:45:29.274840871Z"  
 resource: {
  labels: {
   action_id:  "actions.intent.TEXT"    
   project_id:  "******"    
   version_id:  ""    
  }
  type:  "assistant_action"   
 }
 severity:  "ERROR"  
 textPayload:  "MalformedResponse: Failed to parse Dialogflow response into AppResponse because of empty speech response"  
 timestamp:  "2019-01-07T14:45:29.266062732Z"  
 trace:  "projects/383182941858/traces/ABwppHFK_PehMj1XEs_Arng9VL7_zShy-EWvoziK0Ro6v74TaduNG1cJaRMnGAZMoLZhtILdG2hEBkDvJQ"  
}

Here is the error in the logs:

Error: The default Firebase app already exists. This means you called initializeApp() more than once without providing an app name as the second argument. In most cases you only need to call initializeApp() once. But if you do want to initialize multiple apps, pass a second argument to initializeApp() to give each app a unique name. 
at FirebaseAppError.Error (native) 
at FirebaseAppError.FirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:39:28) 
at FirebaseAppError.PrefixedFirebaseError [as constructor] (/user_code/node_modules/firebase-admin/lib/utils/error.js:85:28) 
at new FirebaseAppError (/user_code/node_modules/firebase-admin/lib/utils/error.js:119:28) 
at FirebaseNamespaceInternals.initializeApp (/user_code/node_modules/firebase-admin/lib/firebase-namespace.js:68:23) 
at FirebaseNamespace.initializeApp (/user_code/node_modules/firebase-admin/lib/firebase-namespace.js:362:30) 
at personFacts (/user_code/index.js:34:11) 
at WebhookClient.handleRequest (/user_code/node_modules/dialogflow-fulfillment/src/dialogflow-fulfillment.js:303:44) 
at exports.dialogflowFirebaseFulfillment.functions.https.onRequest (/user_code/index.js:91:9) 
at cloudFunction (/user_code/node_modules/firebase-functions/lib/providers/https.js:57:9)

回答1:

There are several things that you'll need to learn.

The first is how to add the Firebase Admin SDK to your project.

You'll also need to learn how to retrieve the data using the library. Firebase uses a reference-path based method to fetch the data, so you need to make sure you build the path correctly.

Finally, since you're doing this inside a fulfillment handler, and you are making an asynchronous call, you need to make sure you return a Promise. Fortunately, fetching the data also involves returning a Promise, so you can return this Promise.

The code might, partially, look something like this (untested):

  function personFacts(agent) {
    const personId = agent.parameters["personId"];
    const personMeasurement = agent.parameters["personMeasurement"];        

    var db = admin.database();
    var ref = db.ref(`person/${personId}/${personMeasurement}`);
    return ref.once("value")
      .then( snapshot => {
        var result = snapshot.val();
        agent.add(`${personId}'s ${personMeasurement} is ${result}`); 
      })
      .catch( err => {
        agent.add('uh oh, something went wrong.');
        console.error( err );
      });

  }

As you noted, you need to initialize the Firebase admin library with a key that will give you access through the service account. You can generate the key and download it, and then point to the folder where you have saved it. (It looks like you've just inlined the information, which also works.)

The "Malformed Response" error means that no response was set. This can be due to a number of things, but generally means that your program crashed or failed to call agent.add() for some reason. Consult the logs of your action running for more info. (If you are using the Dialogflow Inline Editor, you can get to the logs by going to https://console.firebase.google.com/, selecting your project, selecting the "Functions" tab on the left, and selecting the "logs" tab.)

Update based on the code and error message.

As the error message suggests, you called admin.initializeApp() more than once. This should only be done when the function is first configured, rather than each time your function is called. Once initialized once - it can be used multiple times.

In your case, this can be done by moving the require that imports firebase-admin and the call to admin.initializeApp() out of the personFacts() function and put them both closer to the top - probably right after the other require() calls.