Files
ts-gateway-send-wa/source/server.ts
mario cdc0118852 new feat: gateway dipakai bersama dengan send-wa-kwitansi
including:
    - remove batch_size di config-gw-wa.json
    - merge function, bedakan dengan message_type
2025-07-08 18:15:48 +07:00

264 lines
7.1 KiB
TypeScript

import config from "./config/config";
import logging from "./config/logging";
import * as Xcron from "node-cron";
import {
getListOutbox,
sendToQontak,
uploadFileCdn,
changeStatusOutbox,
} from "./lib-inject";
export const NAME_SPACE = "WA_GATEWAY";
const VERSION = "1.1";
let isRunning = false;
logging.info(NAME_SPACE, "Starting. Ver:", `${VERSION}`);
type MessageType = "mcu" | "payment";
const on_init = async () => {
logging.info(
NAME_SPACE,
"-------------------- ON INIT START --------------------"
);
// Process both message types
await processMessageType("mcu");
await processMessageType("payment");
logging.info(
NAME_SPACE,
"-------------------- ON INIT END --------------------"
);
};
on_init();
async function processMessageType(messageType: MessageType) {
logging.info(
NAME_SPACE,
`Processing ${config.messageTypes[messageType].name} messages`
);
await main(messageType, "N");
await main(messageType, "E");
await main(messageType, "R");
}
async function processOutboxItem(
messageType: MessageType,
outbox: any,
status: string,
retry: number
) {
if (outbox.fileUrl == null) {
const uploadResult = await uploadFile(messageType, outbox);
logging.info(
NAME_SPACE,
`\t Uploading File to CDN [${config.messageTypes[messageType].name}]:`,
uploadResult
);
await delay(1000);
if (uploadResult === "OK") {
logging.info(NAME_SPACE, "\t File Uploaded");
const response = await sentMsg(messageType, outbox, status, retry);
await delay(1000);
logging.info(
NAME_SPACE,
`\t Resp Qontak [${config.messageTypes[messageType].name}]:`,
response
);
} else {
await changeStatus(messageType, outbox, "E", retry);
}
} else {
const response = await sentMsg(messageType, outbox, status, retry);
await delay(1000);
logging.info(
NAME_SPACE,
`\t Resp Qontak [${config.messageTypes[messageType].name}]:`,
response
);
}
}
async function main(messageType: MessageType, status: string) {
if (isRunning) {
logging.info(NAME_SPACE, "Process is running. Skip this run.");
return;
}
isRunning = true;
try {
logging.info(
NAME_SPACE,
`Process is running. Start to get list outbox for ${config.messageTypes[messageType].name}.`
);
const outboxs = await getLists(messageType, status);
switch (status) {
case "N":
logging.info(
NAME_SPACE,
`GET Processed (N) Message - ${config.messageTypes[messageType].name}`
);
for (const outbox of outboxs) {
await processOutboxItem(
messageType,
outbox,
status,
outbox.XWaOutboxIsRetry
);
}
break;
case "E":
logging.info(
NAME_SPACE,
`GET Error (E) Message - ${config.messageTypes[messageType].name}`
);
for (const outbox of outboxs) {
const retry = outbox.XWaOutboxIsRetry;
if (retry >= 5) {
logging.info(
NAME_SPACE,
"\t Retry count more than 5. Skip this message."
);
continue;
}
await processOutboxItem(
messageType,
outbox,
status,
retry + 1
);
}
break;
case "R":
logging.info(
NAME_SPACE,
`GET Rejected (R) Message - ${config.messageTypes[messageType].name}`
);
for (const outbox of outboxs) {
await processOutboxItem(
messageType,
outbox,
status,
outbox.XWaOutboxIsRetry
);
}
break;
}
logging.info(
NAME_SPACE,
`End Job Get Order - ${config.messageTypes[messageType].name}`
);
} catch (e) {
if (e instanceof Error) {
logging.error(NAME_SPACE, e.message);
} else {
logging.error(NAME_SPACE, "Unknown error");
}
} finally {
isRunning = false;
}
}
async function getLists(messageType: MessageType, status: string) {
const { startDate, endDate } = config;
const resp = await getListOutbox(messageType, status, startDate, endDate);
if (resp?.status !== "OK") {
logging.error(NAME_SPACE, "\t Error get outbox data", resp);
return [];
}
logging.info(
NAME_SPACE,
`\t Success get order data found [${config.messageTypes[messageType].name}]`,
resp.data?.length || 0
);
return typeof resp.data === "string"
? JSON.parse(resp.data)
: resp.data || [];
}
async function sentMsg(
messageType: MessageType,
item: any,
status: string,
retry: number
) {
const payload = {
orderID: item.orderID,
orderDate: item.orderDate,
patientDOB: item.patientDOB,
patientName: item.patientName,
patientHp: item.patientHp,
corpName: item.CorporateName,
fileName: item.fileName,
statusOutbox: status,
retryOutbox: retry,
sendWaID: item.sendWaID,
};
return await sendToQontak(messageType, payload);
}
async function uploadFile(messageType: MessageType, item: any) {
const payload = {
fileName: item.fileName,
rptUrl: item.localUrl,
mime: "application/pdf",
XWaOutboxID: item.sendWaID,
};
return await uploadFileCdn(messageType, payload);
}
async function changeStatus(
messageType: MessageType,
item: any,
status: string,
retry: number
) {
const payload = {
toStatus: status,
XWaOutboxID: item.sendWaID,
retry: retry,
};
return await changeStatusOutbox(messageType, payload);
}
async function delay(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Schedule both message types
for (const sched of config.schedule) {
Xcron.schedule(
sched,
async () => {
logging.info(
NAME_SPACE,
" -------------------- ON INIT SCHEDULE --------------------"
);
await processMessageType("mcu");
await processMessageType("payment");
logging.info(
NAME_SPACE,
"-------------------- END SCHEDULE --------------------"
);
},
{
timezone: "Asia/Jakarta",
}
);
}