Search code examples
javascriptgoogle-cloud-functionsactions-on-googlevoice

Cannot map variable from data stream to users identified response while developing voice app


I am currently developing a voice app with Google Actions where users are able to ask for information about items in a list that is provided through a file stream with Axios as shown in the following LINK. The data looks like this:

[
  {
    "Name": "Beam",
    "Level": "2",
    "Zone": "A",
    "Location": "Beam is located on Level 2 in zone A",
    "Responsible": "Contractor"
  },
  {
    "Name": "Column",
    "Level": "3",
    "Zone": "A",
    "Location": "Column is located on Level 3 in zone A",
    "Responsible": "Kiewit"
  },
  {
    "Name": "Window",
    "Level": "2",
    "Zone": "B",
    "Location": "Window is located on Level 2 in zone B",
    "Responsible": "Tech"
  }
]

Here, it shows three items being a BEam, a Column, and a Window so the objective is that users ask about one of the items and the voice app will provide the other information such as Level, ZOne, Location, or Responsible to the user.

To complete this, I am using the web interface of Google Actions and using inline cloud functions as webhooks in Google Actions that looks like this:

const { conversation } = require('@assistant/conversation');
const functions = require('firebase-functions');
require('firebase-functions/lib/logger/compat'); // console.log compact
const axios = require('axios');

const app = conversation({debug: true});

app.handle('getItem', async conv => {
  const data = await getItem();
  const itemParam = conv.intent.params.Item.resolved;
//   console.log(itemParam);
//   conv.add(`This test to see if we are accessing the webhook for ${itemParam}`);
  
  data.map(item  => {
      if (item.Name === itemParam);
        conv.add(`These are the datails for ${itemParam}. It is located in zone ${item.Zone}, at level ${item.Level}`);
//              conv.add(`This test to see if we are accessing the webhook for ${item.Name}`);
  });
  
});

async function getItem() {
    const res = await axios.get('https://sheetdb.io/api/v1/n3ol4hwmfsmqd');
  console.log(res.data);
  return res.data; // To use in your Action's response
}

exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);

When I check the console logs, I can see that I am retrieving the data in a single event as provided in the LINK. Also, the recognition of the item name is working in the app by defining a type within the app to be recognized based on type categories. And this information is being stored in ItemParam.

However, the main issue I have right now is to link both things together. I was trying to use a map function to match the itemParam and the Item.Name from the data stream. However, this is not working at al. The function I was trying to do is:

data.map(item  => {
          if (item.Name === itemParam);
            conv.add(`These are the datails for ${itemParam}. It is located in zone ${item.Zone}, at level ${item.Level}`);

What I am trying to do here is when the function detects that the user ItemParam is matched to the Item in the stream, use the information from that stream item and add a phrase to the conversation that includes the ItemParam, and the other information about that same item.

Besides, the way this function is right now, also shoots this error:

cf-GPfYHj4HKDWGvHKWArq34w-name
Error: Error adding simple response: **Two simple responses already defined** 
at addSimple (/workspace/node_modules/@assistant/conversation/dist/conversation/prompt/prompt.js:34:15) 

at Prompt.add (/workspace/node_modules/@assistant/conversation/dist/conversation/prompt/prompt.js:108:17) 

at ConversationV3.add (/workspace/node_modules/@assistant/conversation/dist/conversation/conv.js:102:21) 
at data.map.item (/workspace/index.js:16:13) 
at Array.map (<anonymous>) at app.handle (/workspace/index.js:14:8) at process._tickCallback (internal/process/next_tick.js:68:7)

I am honestly not that familiar with Javascript and I might be doing silly mistakes but I really cannot figure out this.

Any help will be much appreciated. Thank you


Solution

  • Thanks, Nick, I fixed my function based on your feedback and now I understand a little better about the map function. Another issue that I figured out along the way was that upper and lower case does matter to match the map function so I also had to modify the type to lowercase and add .toLowerCase() methods to variables.

    Now my code is working with two variables Item and Item_ID so if the user asks about a generic item, it can get detailed by adding the ID of the item to the query question.

    Now my code looks like this:

    // From here, there are all the required libraries to be loaded
    const { conversation } = require('@assistant/conversation'); // This the app coversation
    const functions = require('firebase-functions'); //These are the firebase functions
    require('firebase-functions/lib/logger/compat'); // console.log compact
    const axios = require('axios'); // This is axios to retrieve the data stream
    // To here, there all the required libraries to be loaded
    
    const app = conversation({debug: true}); // This instantiate the conversation
    
    /* This function retrieve the data from the file stream */
    async function getItem() {
        const res = await axios.get('https://sheetdb.io/api/v1/n3ol4hwmfsmqd');
       return res.data; // To use in your Action's response
    }
    
    /* This is the fuction to match user's responses and data stream*/
    app.handle('getItem', async conv => { //getItem is the weekhook name used in Google Actions, conv is the conversation
      const data = await getItem(); // Here the data stream is retrieved and send to the data variable
    //   console.log(data);
      const itemParam = conv.intent.params.Item.resolved; // This is the user's response, in other words, what item the user's want to know from the data.
      const itemIDParam = conv.intent.params.Item_ID.resolved.replace(/\s/g, ''); //This is the user's response for item ID
      const itemFromUser = itemParam + " " + itemIDParam;
      console.log(itemParam);
      console.log(itemIDParam);
      console.log(itemFromUser);
    //   conv.add(`This test to see if we are accessing the webhook for ${itemParam}`); // This is to know if I was getting the correct item from the user. Currently this is working
    //   console.log(data);
        data.map(item  => { //Then, I am trying to map the data stream to recognize the data headers and identify items
    //     console.log(data);
    //     console.log(item);
          if (item.Name.toLowerCase() === itemFromUser.toLowerCase()){
            console.log(item);
            conv.add(`These are the details for ${itemFromUser}. It is located in zone ${item.Zone}, at level ${item.Level}.`);
    //         console.log(conv);
    //          console.log(data);
          } 
        else {
          conv.add(`I am sorry. I could not find any information about that object. Please try with another construction object.`);
          }
      });
    });
    
    exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);
    

    Now I can handle most of the questions except when something is not in the data stream which makes the app to show me this error:

    "error": "Error adding simple response: Two simple responses already defined"
    

    This is the same error as I was getting before and I am not sure how to fix it yet. I tried to implement an else statement for that condition as follows:

    else {
      conv.add(`I am sorry. I could not find any information about that object. Please try with another construction object.`);
      }
    

    But I am still getting same error.

    I am still working on this.