router_Webhook.js

const { Message, WAParser } = require("wacloudapi");
const db = require("../db/db.js");
const config = require("../config");
const message = new Message(config.apiVersion, config.phone_id, config.token);
const fs = require("fs");
const axios = require("axios");
const { MarkMessageAsRead } = require("../lib/sendMessage");
const { KeiLog } = require("../lib/Logger.js");
let number = [];
const processedEvents = new Map();
/**
 * Webhook handler function.
 * @param {Object} app - The Express app object.
 * @param {Object} io - The Socket.IO server instance.
 */
module.exports = function (app, io) {
  app.post("/webhook", async (req, res) => {
    res.status(200).send("ok");
    //  io.emit("webhook", "");
    try {
      const WebhookData = req.body;
      const parser = new WAParser(WebhookData);
      const parsedMessage = parser.parseMessage();
      const parsedStatus = parseStatus(WebhookData);
      const webhookId = WebhookData; // Replace with the correct unique identifier for your webhook
      if (parsedStatus) {
        if (parseStatus.status) {
        //  console.log(parseStatus);
          await db.updateStatusPesan(parsedStatus.id, parsedStatus.status);
          await db.updateStatusLogPesan(parsedStatus.id, parsedStatus.status);
          // await db1.updateStatusPesan(parsedStatus.id, parsedStatus.status);
          // await db1.updateStatusLogPesan(parsedStatus.id, parsedStatus.status);
          
          // await db2.updateStatusPesan(parsedStatus.id, parsedStatus.status);
          // await db2.updateStatusLogPesan(parsedStatus.id, parsedStatus.status);
        }
      }

      if (parsedMessage) {
        io.emit("webhook", parsedMessage);
        const nomor = parsedMessage.from;
        const getMessageNasabah = await db.getMessageNasabah(nomor);
        if (getMessageNasabah) {
            MarkMessageAsRead(parsedMessage.wa_id);
        //  console.log(send);
          const [Nasabah] = await db.getNumberFromDb(nomor);

        
          if (Nasabah) {
            const isNewMessage = await db.getMessage(nomor);
            await handleMessage(parsedMessage, isNewMessage, Nasabah);
            await processAgentNotifications(Nasabah, parsedMessage);
            io.emit('logpesan',parsedMessage);
          } else {
            const data = await db.getAgentByNumber(nomor);
            if(data){
              if (parsedMessage.quoted) {
                const frek = await db.getNumberFromLog(parsedMessage.quoted.id);
       KeiLog("INFO", " Memasukan kedalam log internal")
                await message.sendTextMessage(frek.nomor, parsedMessage.text);
              }
            }
            const {
              wa_id,
              from,
              senderName,
              timestamp,
              type,
              quoted,
              forwarded, phone_id,
              display_number
            } = parsedMessage;
            let pesan, imageid;
            let isMessageSent = false;
          //  console.log(number);
            if (type === "text") {
              pesan = parsedMessage.text;
              imageid = "";
            } else if (type === "image") {
              pesan = parsedMessage.image.caption || "";
              imageid = parsedMessage.image.id;
            } else {
              return;
            }
            const quote = quoted ? JSON.stringify(quoted) : null;
            KeiLog("INFO", `1 Pesan masuk bukan dari nasabah dengan nomor ${from} dengan pesan (${pesan} )`);
            await db.insertLogPesan(
              wa_id,
              type,
              pesan,
              from,
              senderName,
              timestamp,
              imageid,
              forwarded,
              quote, phone_id,
              display_number
          
            );
            // await db1.insertLogPesan(
            //   wa_id,
            //   type,
            //   pesan,
            //   from,
            //   senderName,
            //   timestamp,
            //   imageid,
            //   forwarded,
            //   quote
            // );
            // await db2.insertLogPesan(
            //   wa_id,
            //   type,
            //   pesan,
            //   from,
            //   senderName,
            //   timestamp,
            //   imageid,
            //   forwarded,
            //   quote
            // );
            io.emit('logpesan',parsedMessage);
          }
        }
      }

    } catch (error) {
        KeiLog("ERROR", error);
    }
  });
};

/**
 * Processes notifications for agents.
 * @async
 * @param {Object} Nasabah - The nasabah object containing information about the customer.
 * @param {Object} parsedMessage - The parsed message object.
 * @param {Object} res - The response object.
 */
async function processAgentNotifications(Nasabah, parsedMessage, res) {
  const Agent = await db.getDivisiAgent(
    Nasabah.tim,
    Nasabah.dept,
    Nasabah.user_id
  );

  for (i in Agent) {
    const data = Agent[i];

    const LogID = await sendNotification(
      data.nomor_agent,
      data.nama_agent,
      Nasabah.nama,
      parsedMessage.from,
      parsedMessage.text
    );
    KeiLog("INFO", `Proses send notif`);
    if (data && LogID) {
      //console.log(LogID);
      await db.insertLogAgent(
        LogID.messages[0].id,
        Nasabah.tim,
        Nasabah.user_id,
        parsedMessage.from,
        Nasabah.nama,
        data.nomor_agent,
        data.nama_agent,
        parsedMessage.timestamp,
        parsedMessage.text
      );
      await db.insertLogInternal(
        parsedMessage.wa_id,
        parsedMessage.text,
        parsedMessage.from,
        data.nomor_agent
      );

      // await db1.insertLogAgent(
      //   LogID.messages[0].id,
      //   Nasabah.tim,
      //   Nasabah.user_id,
      //   parsedMessage.from,
      //   Nasabah.nama,
      //   data.nomor_agent,
      //   data.nama_agent,
      //   parsedMessage.timestamp,
      //   parsedMessage.text
      // );
      // await db1.insertLogInternal(
      //   parsedMessage.wa_id,
      //   parsedMessage.text,
      //   parsedMessage.from,
      //   data.nomor_agent
      // );
      // await db2.insertLogAgent(
      //   LogID.messages[0].id,
      //   Nasabah.tim,
      //   Nasabah.user_id,
      //   parsedMessage.from,
      //   Nasabah.nama,
      //   data.nomor_agent,
      //   data.nama_agent,
      //   parsedMessage.timestamp,
      //   parsedMessage.text
      // );
      // await db2.insertLogInternal(
      //   parsedMessage.wa_id,
      //   parsedMessage.text,
      //   parsedMessage.from,
      //   data.nomor_agent
      // );
      KeiLog("INFO", `Berhasil memasukan kedatabase ${parsedMessage.wa_id} ${parsedMessage.text} ${parsedMessage.from}`);
      return;
    }
  }
  return;
}

/**
 * Handles incoming messages.
 * @async
 * @param {Object} parsedMessage - The parsed message object.
 * @param {boolean} isNewMessage - Flag indicating if the message is new.
 * @param {Object} nasabah - The nasabah object.
 * @param {Object} res - The response object.
 * @param {Object} io - The Socket.IO server instance.
 */
async function handleMessage(parsedMessage, isNewMessage, nasabah, res,io) {
  const { wa_id, from, senderName, timestamp, type, quoted, forwarded , phone_id,
    display_number} =
    parsedMessage;
  let pesan, imageid;
  let isMessageSent = false;
  //console.log(number);
  if (type === "text") {
    pesan = parsedMessage.text;
    imageid = "";
  } else if (type === "image") {
    pesan = parsedMessage.image.caption || "";
    imageid = parsedMessage.image.id;
  } else {
    return;
  }

  const quote = quoted ? JSON.stringify(quoted) : null;
  KeiLog("INFO", `1 Pesan masuk dari nasabah ${from} dengan pesan (${pesan} )`);
  const currentDate = new Date(timestamp * 1000);
  const isCurrentDateHoliday = await isHoliday(currentDate);
  const currentHour = currentDate.getHours();

  await db.insertLogPesan(
    wa_id,
    type,
    pesan,
    from,
    senderName,
    timestamp,
    imageid,
    forwarded,
    quote,
    phone_id,
    display_number
  );
  // await db1.insertLogPesan(
  //   wa_id,
  //   type,
  //   pesan,
  //   from,
  //   senderName,
  //   timestamp,
  //   imageid,
  //   forwarded,
  //   quote
  // );
  // await db2.insertLogPesan(
  //   wa_id,
  //   type,
  //   pesan,
  //   from,
  //   senderName,
  //   timestamp,
  //   imageid,
  //   forwarded,
  //   quote
  // );

  if (isCurrentDateHoliday) {
    if (!isMessageSent && !number.includes(from)) {
      await message.sendTextMessage(
        from,
        "Bapak / Ibu, Mohon maaf saat ini kami sedang libur. Silakan menghubungi kami kembali di hari kerja. Terima kasih."
      );
      isMessageSent = true;
      number.push(from);
    }
    return "ok";
  } else if (currentHour < 9 || currentHour >= 24) {
    if (!isMessageSent && !number.includes(from)) {
      await message.sendTextMessage(
        from,
        `Bapak / Ibu, Mohon maaf saat ini kami sedang di luar jam kerja.
      Silakan menghubungi kami kembali pada jam kerja (9 pagi - 5 sore).

      _*Terima kasih.*_`
      );
      isMessageSent = true;
      number.push(from);
    }
    return "ok";
  } else {
    if (isNewMessage) {
      await handleNewMessage(parsedMessage);
    } else {
    }
  }
}
/**
 * Handles new incoming messages.
 * @async
 * @param {Object} parsedMessage - The parsed message object.
 */
async function handleNewMessage(parsedMessage) {
  try {
    const { wa_id, from, senderName, timestamp, type, quoted, forwarded } =
      parsedMessage;
    let pesan, imageid;

    if (type === "text") {
      pesan = parsedMessage.text;
      imageid = "";
    } else if (type === "image") {
      pesan = parsedMessage.image.caption || "";
      imageid = parsedMessage.image.id;
    } else {
      return;
    }

    const quote = quoted ? JSON.stringify(quoted) : null;

    await message.sendTextMessage(
      from,
      "Bapak / Ibu, Terimakasih sudah menghubungi kami, kami akan balas secepatnya."
    );
    // const dbins = await db.insertLogPesan(
    //   wa_id,
    //   type,
    //   pesan,
    //   from,
    //   senderName,
    //   timestamp,
    //   imageid,
    //   forwarded,
    //   quote
    // );

    //console.log(dbins);
  } catch (error) {
    KeiLog("ERROR", "Gagal memasukkan pesan ke log pesan "+error);
  }
}
/**
 * Sends a notification message.
 * @async
 * @param {string} nomor - The phone number to send the notification to.
 * @param {string} nama_divisi - The name of the division.
 * @param {string} nama_nasabah - The name of the customer (nasabah).
 * @param {string} nomor_nasabah - The phone number of the customer (nasabah).
 * @param {string} isi_pesan - The content of the message.
 * @returns {Promise<Object>} - The response from the message sending action.
 */
async function sendNotification(
  nomor,
  nama_divisi,
  nama_nasabah,
  nomor_nasabah,
  isi_pesan
) {
  try {
    return await message.sendTemplateMessage(
      nomor,
      "chat_update_fix_",
      "en_US",
      [
        {
          type: "body",
          parameters: [
            {
              type: "text",
              text: nama_nasabah,
            },
            {
              type: "text",
              text: nomor_nasabah,
            },
            {
              type: "text",
              text: isi_pesan,
            },
            {
              type: "text",
              text: nama_divisi,
            },
          ],
        },
      ]
    );
  } catch (error) {
   // console.log(error);
  }
}

/**
 * Parses the status from the webhook data.
 * @param {Object} webhookData - The data received from the webhook.
 * @returns {Object|null} - The parsed status object, or null if not applicable.
 */
function parseStatus(webhookData) {
  if (webhookData.entry) {
    const entry = webhookData.entry[0];

    if (entry.changes) {
      const change = entry.changes[0];
      const value = change.value;

      if (
        value.messaging_product === "whatsapp" &&
        value.statuses &&
        value.statuses[0]
      ) {
        const status = value.statuses[0];
        return {
          id: status.id,
          status: status.status,
          timestamp: status.timestamp,
          recipient_id: status.recipient_id,
        };
      }
    }
  }

  return null;
}

/**
 * Checks if a given date is a holiday.
 * @async
 * @param {Date} date - The date to check.
 * @returns {Promise<boolean>} - True if the date is a holiday, otherwise false.
 */
async function isHoliday(date) {
  try {
    const response = await axios.get("https://api-harilibur.vercel.app/api");
    const holidays = response.data;
    const formattedDate = date.toISOString().slice(0, 10);

    for (let holiday of holidays) {
      if (
        holiday.holiday_date === formattedDate &&
        holiday.is_national_holiday
      ) {
        return true;
      }
    }

    return false;
  } catch (error) {
    KeiLog("ERROR", "Error while checking if the date is a holiday "+error);
    return false;
  }
}