node.jstypescriptexpresswebhookswhatsapi

Typescript error while sending server response to whatsapp api cloud with webhooks in NodeJs


Maybe you can help me here, I'm making my api with typescript and nodejs express. I am using Meta for whatsapp api cloud usage.

I configured a webhook as requested by the official documentation, I won't get into documentation because there is a lot to say about it but I will show you what I did in the code

I practically do my auth for the webhook request with the meta api and I have an endpoint that is a message and that is listening to the communication between my server and whatsapp. Everything works fine, sending messages from my server to whatsapp as well as sending messages from whatsapp to my server. But when I want to make conditional interactive responses simulating a bot, I can't do it, I can't make the server respond once the user presses a button in a chat

It gives me a somewhat ambiguous typescript error

error TypeError: (0 , sendWhatsapp_1.Process) is not a function
    at /app/dist/src/controller/whatsapp_test.js:56:44
      at Generator.next (<anonymous>)
   at fulfilled (/app/dist/src/controller/whatsapp_test.js:5:58)
      at processTicksAndRejections (node:internal/process/task_queues:96:5)

Investigating and reading a bit about what it can be, I read that it could be a matter of creating a circular dependency. But anyway, you tell me what it could be

Summarizing a bit the function of my code goes like this:

I have my controller that is calling the webhook so to speak here, I resolve asynchronously the GetText() function that is in charge of listening to the action that the user has triggered, once the user clicks the button

export const receiveMessage = async(req: Request, res: Response) => {
      try{
          console.log("start");
          let input = (req.body["input"])[0];
          console.log('input', input);
          let changes = (input["changes"])[0];
          console.log("changes", changes);
          let value = changes["value"];
          console.log("value", value);
          let messageObject = value["messages"];
          console.log("messageObject 1", messageObject);
 
          if (typeof messageObject !== "undefined") {
              let messages = messageObject[0];
              let cell = messages["from"];
 
              console.log('messages', input);
              console.log('number', cell);
 
              console.log("stop");
 
              console.log('messageobject 2', messages);
 
              let text = await GetTextUser(messages, cell);
 
              console.log("Interactive text", text);
 
              if(text !== "") {
                  Process (text, cell);
              }
 
 
          } else {
              console.log('no message');
          }
 
          res.send("RECEIVED_EVENT");
 
      } catch(e) {
          console.log('error', e);
          res.send("RECEIVED_EVENT");
      }
}

And if the text includes certain parameters, an email is sent using nodemailer and it is stored in a database.

async function GetTextUser(messages: any, cell: any) {
 
      let text: string = "";
      let typeMessage = messages["type"];
 
      if (messageType === "text") {
          text = (messages["text"])["body"];
      } else if (messageType === "interactive") {
          let interactiveObject = messages["interactive"];
          let interactiveType = interactiveObject["type"];
          console.log('interactiveObject', interactiveObject);
          if (interactiveType === "response_button") {
              text = (interactiveObject["button_reply"])["title"];
          } else if (interactiveType === "answer_list") {
              text = (interactiveObject["button_reply"])["title"];
          } else {
              console.log("no message");
          }
      } else if (writeMessage === "button") {
          let buttonObject = messages["button"];
          console.log("It is not the user's invoice");
          text = buttonObject["text"];
 
          console.log('text include', text.includes('Help, this is not my invoice.'));
          console.log('text', text);
          if (text.includes('Help, it's not my invoice.')) {
 
              let supabase = supabaseinstance();
 
              const contextId = (messages["context"])["id"];
 
              console.log('contextId', contextId);
 
              const { data: historyMsgWsp, error } = await supabase.from("history_mgs_whatsapp").select('*').eq("wa_id", contextId as string);
 
              if (error) {
                  res.json (error)
              } else {
 
                  console.log('historyMsgWsp DB', historyMsgWsp);
 
                  const typeHistoryMesg: TypeHistoryWspTest[] = historyMsgWsp;
                  const emailService = new EmailService();
 
                  let ticket = typeHistoryMesg.map(dataFromDB => {
 
                      let saludo_inicial = "Dear SAC Collaborator,";
                      let main_paragraph = `A support ticket has been generated and assigned from commerce ${dataFromDB.commerce_name}. Contact the client: ${dataFromDB.client_name} as soon as possible`;
                      let main_paragraph2 = "Request Details";
                      let main_paragraph3 = [`<strong>Customer Contact Cell:</strong> +${dataFromDB.client_cellphone}`, `<strong>Commerce Name:</strong> ${dataFromDB.commerce_name}`,
                      `<strong>NoInvoice:</strong> ${dataFromDB.invoice_code}`, `<strong>Date of message sent:</strong> ${dataFromDB.date_sending_message}`];
 
                      constant data = {
                          id: dataFromDB.id,
                          wa_id: dataFromDB.wa_id,
                          opening_p: opening_greeting,
                          main_paragraph: main_paragraph,
                          main_paragraph2: main_paragraph2,
                          main_paragraph3: main_paragraph3,
                          subjectData: `Problems with Help is not my invoice`,
                          fileUrl: "link"
                      }
 
                      return emailService.prepareEmail("TEST_TICKET", data, "mail");
                  });
 
 
                  console.log('email left', ticket);
 
              }
          }
 
      } else {
          console.log("no message");
      }
      return text;
}

Return the text that is received by the receiveMessage controller and once resolved it is passed to the process method

    let text = await GetTextUser(messages, cell);
     
   console.log("Interactive text", text);
     
      if(text !== "") {
           Process (text, cell);
     }

The error enters the Process method, which is the one that would be in charge of sending a response to the user once we get a response from GetTextUser(). In the process method, in turn, the type of message is processed, which we will send to the user and it is sent with the help of the https library.

export function Process(textUser: any, cellPhone: any) {
 
        console.log('Process user text', textUser);
        console.log('Process mobile phone', cellPhone);
 
        textUser = textUser.toLowerCase();
 
        let models: any = [];
 
        if (textUser.includes('Help, it's not my invoice.')) {
            console.log("Help, it's not my invoice.");
            let model = MessageText("We have generated a support ticket to help you, we will contact you as soon as possible", cell);
            models.push(model);
        } else if (textUser.includes("hello") || textUser.includes("help") || textUser.includes("help me") || textUser.includes("please") || textUser.includes(" please")) {
            console.log("greeting");
            let model = MessageText("Hi, nice to say hello. 👋 We won't be able to answer any questions or clarifications about claims here. Please contact email for any help.", cell phone);
            models.push(model);
        } else if (textUser.includes("thank you")) {
            // gratitude
            console.log("thank you");
            let model = MessageText("Thank you for writing me. 😉😎", cell);
            models.push(model);
 
        } else if (textUser.includes("bye") ||
            textUser.includes("bye") ||
            textUser.includes("bye") ||
            textUser.includes("I'm leaving")
        ) {
            console.log("fire");
            // desperate
            let model = MessageText("Watch out. 😊", cell);
            models.push(model);
        }
        else {
            console.log("I don't understand");
            // I don't understand
            let model = MessageText("I don't understand what you're saying. We won't be able to answer questions or clarifications about claims here. Contact info@cit.hn for any help.", cell phone);
            models.push(model);
        }
 
        models.forEach(model => {
            console.log('what is the model', model);
            SendMessageWhatsApp(model);
        });
 
}

The code in a certain way is sequential and conditional, so I send a message from my server to whatsapp, the user presses a button and when it is that button with the conditional text, the service that sends the mail is triggered. After this happens, the return of the text is immediately passed to the Process method, which handles the string that GetTextUser returns, and it is evaluated what the text says and depending on the text, it is the response that is sent back to whatsapp

I've been trying all day, I don't know what I'm doing wrong


Solution

  • Ok, the "problem" was easier to solve than I thought. In the end it was just that I needed to export the Process method, here I found the solution and explanation Solution