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;
}
}