Search code examples
javascriptfirebasefirebase-realtime-databasedialogflow-esfulfillment

How do I check user parameters to match a value in firebase?


Im new to dialogflow and javascript so I am really unfamiliar with how to code in fulfillment of dialogflow this is my if statement to check if the user parameter matches with the value in the firebase, but it does not work and fallback intent is triggered. The scenario is, If answer = firebase value, bot replies that it is correct, adds score and asks the next question.

function q1correct(agent){  
    const q1snapshot = admin.database().ref('quizanswers/q1').once('value');
    if(q1snapshot.val() == agent.parameters.pikachu){
      agent.add(`That's correct!`);
      score = score + 1;
      return admin.database().ref('quizquestions').once('value').then((snapshot) =>{
        const value = snapshot.child('q2').val();                                                            
        agent.add(`${value}`); 
    }); 
    }
    else{
      agent.add(`Sadly, that is incorrect.`);
      score = score;
      return admin.database().ref('quizquestions').once('value').then((snapshot) =>{
      const value = snapshot.child('q2').val();                                                            
      agent.add(`${value}`);

    });
    }
  }

the if statement just doesnt work

As shown here, I have no training phrases as I am letting it be dynamic if there were to be changes in the answers inside the database, is that not possible to do? Must I have training phrases? enter image description here

enter image description here


Solution

  • It sounds like you have two different issues here - one with how you are expecting Dialogflow to be detecting Intents, the other with how you are expecting Firebase to fetch values.

    Dialogflow and Detecting Intents

    The purpose of Dialogflow is to take the user's phrases and determine their broad Intent by matching them with a number of possible statements. This lets you create an Intent, for example, where the user can say "yes" or "sure" or "of course" or other variants and your code just needs to handle them one way.

    Sometimes we have responses where they may reply with a phrase, but some values in that phrase will vary, so we can setup parameters. If those parameters can come from a fixed set of values (or synonyms for those values), we may set these up as Entities. Dialogflow defines some Entity types, and we're allowed to define our own custom ones.

    If your questions expect multiple-choice answers you can even create a Session Entity which contains possible responses for the next question. This way you will know if they answered with something you expect, or if they didn't try.

    Things become much more difficult if you are expecting open-ended answers. Since Dialogflow doesn't have any patterns it knows about when the user replies, it will usually end up in the Fallback Intent. This is a special Intent that matches "everything else that doesn't match", and is usually meant for cases where the users says something totally unexpected - although it can be used to collect free-form answers.

    All of this happens before any if statements in the fulfillment webhook that you write are evaluated. Once an Intent has been detected (or it reverts to pick the Fallback Intent), it sends this Intent, any parameters detected, and the full phrase to your webhook.

    Update

    As you note, you don't have any training phrases set for your Intent at all. If you don't have any Events (and you probably don't want one in this case), then Dialogflow will end up never matching this Intent.

    The parameter, in this case, is irrelevant. Parameters are placeholders in phrases, and those parameters will be filled in with that part of the phrase as the user entered it.

    You can create a Fallback Intent that has an Input Context. This will only trigger the Fallback Intent if no other Intent matches and all of the Contexts listed in the Input Context section are currently active. This will deliver the entire phrase from the user, and you can use this phrase in your comparison in your Handler for this Fallback Intent.

    This, however, is probably not a good idea. Not providing training phrases and Entities can make for much more stilted conversations. It sounds like you can create an Entity for all the possible pokemon (say, @pokemon), and create your training phrases to accept things like:

    • I think it is pikachu
    • Pikachu
    • Pikachu is probably correct
    • ...

    This way Dialogflow can match all these phrases to the same Intent, but just report "Pikachu" as the parameter to you.

    Getting Values from Firebase

    With the Firebase database, values are fetched by having a reference to a record in the database and then fetching this record by using once(). (You can also subscribe to updates to a record by using on(), but this is unnecessary in your case.)

    Creating a reference is a local operation, so it returns immediately. Getting a value, however, operates asynchronously, so you need to use a Promise that will resolve only when the network operation completes. For sufficiently modern versions of node (which you should be using), you can use async/await.

    In your if test, you are checking the value from the user against the reference instead of the value. To check against the value, you may need to do something more like this in an async function (untested):

    const db = admin.database();
    const q1snapshot = await db.ref('quizanswers/q1').once('value');
    if( q1snapshot.val() === agent.parameters.pikachu ){
      // They got it right. Fetch and ask the next question
      const q2snapshot = await db.ref('quizquestions/q2').once('value');
      agent.add(q2snapshot.val());
    } else {
      // They got it wrong. Say so.
      agent.add( "That isn't correct. Please try again." );
    }
    

    Update based on your updated code and saying that you're using the Dialogflow Inline Editor.

    The Inline Editor is still (apparently) using Node 6, which does not support async/await. If you don't use async/await, then you need to use Promises (which Node 6 does support).

    So your lines

    const q1snapshot = admin.database().ref('quizanswers/q1').once('value');
    if(q1snapshot.val() == agent.parameters.pikachu){
    

    still end up being incorrect because q1snapshot is assigned a Promise, and isn't a snapshot of the result at all. So trying to compare val() of a Promise to the parameter won't work.

    Your best bet would be to upgrade to a modern Node engine. You would edit your package.json file to include an "engines" section. It might look something like this:

    {
      "name": "dialogflowFirebaseFulfillment",
      "description": "This is the default fulfillment for a Dialogflow agents using Cloud Functions for Firebase",
      "version": "0.0.1",
      "private": true,
      "license": "Apache Version 2.0",
      "author": "Google Inc.",
      "engines": {
        "node": "8"
      },
      "scripts": {
        ...
      },
      "dependencies": {
        ...
      }
    }
    

    This show setting it for Node 8. You may wish to consider using Node 10, although this is still in beta support in Cloud Functions for Firebase.

    To make this change if you're using the Inline Editor in Dialogflow, you need to select the (package.json) tab that is part of the editor:

    inline editor showing package.json