1

I want to send data to my firestore using dialogFlow fulfillment but it is not working. Here is my index.js:

// See https://github.com/dialogflow/dialogflow-fulfillment-nodejs
// for Dialogflow fulfillment library docs, samples, and to report issues
'use strict';

const functions = require('firebase-functions');
const {WebhookClient} = require('dialogflow-fulfillment');
const {Card, Suggestion} = require('dialogflow-fulfillment');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
var product,phoneNo,eMail;;

process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements

var flag=0;


exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
  const agent = new WebhookClient({ request, response });

function welcome(agent) {
      if(flag==0){
        agent.add(`Can you please tell me what is your product?`);
        flag=1;
      }
      else if(flag==1){
        var prod=request.body.queryResult.queryText;
        product=prod;
        flag=2;
        agent.add(`Please provide me your Phone No or your E-Mail ID so that my team can contact you.`);
      }
      else if(flag==2||flag==3){
        let missingSlots1 = [];
        var queryRes=request.body.queryResult.queryText;

        var [phone,email] = [agent.parameters[`phone`], agent.parameters[`mail`]];

        if(queryRes.includes(`@`)&&queryRes.includes(`.`)){
          email=queryRes;
          eMail=queryRes;
          agent.parameters[`mail`]=queryRes;
        }
        else if(queryRes.length>=10&&queryRes!=product){
          console.log(`phone ke andar wala if `+queryRes);
            phone=queryRes;
            phoneNo=queryRes;
            agent.parameters[`phone`]=phoneNo;
        }

        if(!phoneNo){missingSlots1.push(`Phone No`);}
        if(!eMail){missingSlots1.push(`E-mail`);}

        if(missingSlots1.length==2){
            agent.add(`Please provide me your Phone No or your E-Mail ID so that my team can contact you.`);
        }
        else if(flag==2){
            if(!eMail){
                agent.add(`Would you please provide your E-Mail ID?`);
            }
            if(!phoneNo){
                agent.add(`Would you please provide your Phone No?`);
            }
            flag=3;
        }
        else{
            flag=4;
            addLeads();
            agent.add(`Okay.Now you are good to go!`);  
        }
      }
  }


  function addLeads(){
    var data={
        'product':product,
        'email':eMail,
        'phoneNo':phoneNo
    };

    const dialogflowAgentRef = db.collection('botData').doc(eMail);
    let setDoc = dialogflowAgentRef.set(data,{merge:true});
  }



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

I have removed other functions for simplicity. Here is my package.json dependencies:

"dependencies": {
    "actions-on-google": "^2.2.0",
    "firebase-functions": "^2.0.2",
    "dialogflow": "^0.6.0",
    "dialogflow-fulfillment": "^0.5.0",
    "@google-cloud/firestore": "^0.16.1",
    "firebase-admin": "^6.0.0"
  }

And here is my firestore permission:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if true;
    }
  }
}

Main error that I can see in my logs is:

Warning, estimating Firebase Config based on GCLOUD_PROJECT. Initializing firebase-admin may fail

If I comment the let setDoc = dialogflowAgentRef.set(data,{merge:true}); line ,my program works fine but with this line, the program does not even enters this function and shows the intent response rather than my fulfillment response. How should I fix this?

Shashank Gupta
  • 315
  • 1
  • 4
  • 16
  • I think you've over-simplified the example - nothing calls `addLeads()`, so it is difficult to see how it is triggered. Can you update the question to show the code that calls it, as well as show any thing from your logs, both with and without the line in question? – Prisoner Jan 27 '20 at 10:20
  • @Prisoner Do you want me to add anything else? Or is is good now? – Shashank Gupta Jan 27 '20 at 11:05
  • Aside from the error you've shown, do you get any other errors when `set()` is called? – Prisoner Jan 27 '20 at 11:27
  • .set(data) was working yesterday but then I changes it to set(data,{merge:true}) as it was overwriting my data everytime. But now both of them are not working. I also tried .add() but it shows the same error. – Shashank Gupta Jan 27 '20 at 12:13

1 Answers1

1

The "error" you're showing is a warning and does not usually prevent things from working. It is saying that it is assuming the environment based on the Project it is running in. If you're accessing a data store in the same project - you shouldn't be encountering any problems with this.

You don't indicate what is happening when you try to call set(), but it sounds like this may not even be happening.

Given the state machine that is depending on the value of the global flag variable, that may not be surprising. Trying to track the conversation this way has two problems:

  1. It looks like they're only getting invoked at the welcome Intent. You're not showing this Intent definition, but this may only be happening when the bot is first invoked and not afterwards.

  2. Since this is a global variable, and not a value that is attached to the conversation, it could be changed if multiple users tried to use the bot at the same time or if the server it is running on is reset. If you're using Firebase Cloud Functions or the Dialogflow Built In editor, this could happen without you knowing it.

In particular, (2) may be causing it to never get to the state where set() is called.

There does not appear to be anything wrong with the call to set() itself, but you're not doing any error handling. And it looks like you're sending the "You're good to go" message before you know if the set() actually works. To address this, you may want to change addLeads() so it returns a Promise, and then make the call to it also work with a Promise. This might change addLeads() to something like

  function addLeads(){
    var data={
        'product':product,
        'email':eMail,
        'phoneNo':phoneNo
    };

    const dialogflowAgentRef = db.collection('botData').doc(eMail);
    return dialogflowAgentRef.set(data,{merge:true});
  }

Then, when you call it, you need to both work with the Promise and return it (so Dialogflow will wait to send the reply until the set() completes.) Probably something like:

        return addLeads()
          .then( () => {
            agent.add(`Okay. Now you are good to go!`);  
          })
          .catch( err => {
            console.error( "There was a problem", err );
            agent.add( "There was a problem saving the data." );
          });
Prisoner
  • 49,922
  • 7
  • 53
  • 105
  • If I declare my global variable inside my intent and then pass on to the addLead(), will that make them different for every conversation? Or is there any other way to do this? – Shashank Gupta Jan 31 '20 at 04:45
  • The short answer is that you need to use Contexts to store session information. There are a number of other answers on SO that discuss this, but if you have a problem with them, feel free to ask a new question rather than trying to extend this one. – Prisoner Jan 31 '20 at 10:39