tambahannya
This commit is contained in:
@@ -5,11 +5,19 @@ import path from 'path';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { prisma } from '../prisma';
|
||||
import { getValueByPath } from '../get_value_by_path';
|
||||
import "colors"
|
||||
|
||||
// === KONFIGURASI UTAMA ===
|
||||
const MEDIA_DIR = path.join(process.cwd(), 'downloads');
|
||||
await ensureDir(MEDIA_DIR);
|
||||
|
||||
async function ensureDir(dir: string) {
|
||||
try {
|
||||
await fs.access(dir);
|
||||
} catch {
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
type DataMessage = {
|
||||
from: string;
|
||||
fromNumber: string;
|
||||
@@ -19,12 +27,7 @@ type DataMessage = {
|
||||
type: WAWebJS.MessageTypes;
|
||||
to: string;
|
||||
deviceType: string;
|
||||
media: {
|
||||
data: WAWebJS.MessageMedia["data"];
|
||||
mimetype: WAWebJS.MessageMedia["mimetype"];
|
||||
filename: WAWebJS.MessageMedia["filename"];
|
||||
filesize: WAWebJS.MessageMedia["filesize"];
|
||||
};
|
||||
media: any[] | null;
|
||||
notifyName: string;
|
||||
}
|
||||
|
||||
@@ -60,13 +63,6 @@ function log(...args: any[]) {
|
||||
console.log(`[${new Date().toISOString()}]`, ...args);
|
||||
}
|
||||
|
||||
async function ensureDir(dir: string) {
|
||||
try {
|
||||
await fs.access(dir);
|
||||
} catch {
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
async function safeRm(path: string) {
|
||||
try {
|
||||
@@ -95,6 +91,8 @@ async function destroyClient() {
|
||||
}
|
||||
}
|
||||
|
||||
let connectedAt: number | null = null;
|
||||
|
||||
// === PEMBUATAN CLIENT ===
|
||||
async function startClient() {
|
||||
if (state.isStarting || state.isReconnecting) {
|
||||
@@ -131,6 +129,7 @@ async function startClient() {
|
||||
});
|
||||
|
||||
client.on('ready', () => {
|
||||
connectedAt = Date.now();
|
||||
log('✅ WhatsApp client siap digunakan!');
|
||||
state.ready = true;
|
||||
state.isReconnecting = false;
|
||||
@@ -177,17 +176,32 @@ async function startClient() {
|
||||
}
|
||||
}
|
||||
|
||||
function detectFileCategory(mime: string) {
|
||||
if (mime.startsWith("image/")) return "image";
|
||||
if (mime.startsWith("audio/")) return "audio";
|
||||
if (mime.startsWith("video/")) return "video";
|
||||
if (mime === "application/pdf") return "pdf";
|
||||
if (mime.includes("spreadsheet") || mime.includes("excel")) return "excel";
|
||||
if (mime.includes("word")) return "document";
|
||||
if (mime.includes("presentation") || mime.includes("powerpoint")) return "presentation";
|
||||
return "file";
|
||||
}
|
||||
|
||||
// === HANDLER PESAN MASUK ===
|
||||
async function handleIncomingMessage(msg: WAWebJS.Message) {
|
||||
const chat = await msg.getChat();
|
||||
await chat.sendStateTyping();
|
||||
log(`💬 Pesan dari ${msg.from}: ${msg.body || '[MEDIA]'}`);
|
||||
|
||||
if (!connectedAt) return;
|
||||
if (msg.timestamp * 1000 < connectedAt) return;
|
||||
|
||||
if (msg.from.endsWith('@g.us') || msg.isStatus || msg.from === 'status@broadcast') {
|
||||
log(`🚫 Pesan dari grup/status diabaikan (${msg.from})`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
const body = msg.body?.toLowerCase().trim() || '';
|
||||
const notifyName = (msg as any)._data.notifyName;
|
||||
|
||||
const dataMessage: DataMessage = {
|
||||
@@ -199,32 +213,32 @@ async function handleIncomingMessage(msg: WAWebJS.Message) {
|
||||
type: msg.type,
|
||||
to: msg.to,
|
||||
deviceType: msg.deviceType,
|
||||
media: {
|
||||
data: null as unknown as WAWebJS.MessageMedia['data'],
|
||||
mimetype: null as unknown as WAWebJS.MessageMedia['mimetype'],
|
||||
filename: null as unknown as WAWebJS.MessageMedia['filename'],
|
||||
filesize: null as unknown as WAWebJS.MessageMedia['filesize'],
|
||||
|
||||
},
|
||||
media: null,
|
||||
notifyName,
|
||||
};
|
||||
|
||||
// Media handler
|
||||
// === HANDLE MEDIA ===
|
||||
if (msg.hasMedia) {
|
||||
const media = await msg.downloadMedia();
|
||||
|
||||
dataMessage.media = {
|
||||
data: media.data,
|
||||
mimetype: media.mimetype,
|
||||
filename: media.filename,
|
||||
filesize: media.filesize
|
||||
};
|
||||
// Pastikan formatnya data:<mimetype>;base64,<data>
|
||||
const mime = media.mimetype || 'application/octet-stream';
|
||||
const prefixedBase64 = `data:${mime};base64,${media.data}`;
|
||||
|
||||
dataMessage.media = [{
|
||||
type: "file:full",
|
||||
data: prefixedBase64,
|
||||
mime: mime,
|
||||
name: media.filename || `${uuid()}.${mime.split('/')[1] || 'bin'}`
|
||||
}];
|
||||
|
||||
// await fs.writeFile(path.join(MEDIA_DIR, dataMessage.media[0].name), Buffer.from(media.data, 'base64'));
|
||||
|
||||
}
|
||||
|
||||
// to web hook
|
||||
// === KIRIM KE WEBHOOK ===
|
||||
try {
|
||||
const webhooks = await prisma.webHook.findMany({ where: { enabled: true } });
|
||||
|
||||
if (!webhooks.length) {
|
||||
log('🚫 Tidak ada webhook yang aktif');
|
||||
return;
|
||||
@@ -233,8 +247,22 @@ async function handleIncomingMessage(msg: WAWebJS.Message) {
|
||||
await Promise.allSettled(
|
||||
webhooks.map(async (hook) => {
|
||||
try {
|
||||
console.log("send webhook " + hook.url);
|
||||
const body = payloadConverter({ payload: hook.payload ?? JSON.stringify(dataMessage), data: dataMessage });
|
||||
log(`🌐 Mengirim webhook ke ${hook.url}`);
|
||||
|
||||
let body = payloadConverter({
|
||||
payload: hook.payload ?? JSON.stringify(dataMessage),
|
||||
data: dataMessage,
|
||||
});
|
||||
|
||||
if (dataMessage.hasMedia) {
|
||||
const bodyMedia = JSON.parse(body);
|
||||
bodyMedia.question = msg.body ?? dataMessage.media?.[0].mime;
|
||||
bodyMedia.uploads = dataMessage.media;
|
||||
body = JSON.stringify(bodyMedia);
|
||||
}
|
||||
|
||||
// await fs.writeFile(path.join(process.cwd(), 'webhook.json'), body);
|
||||
|
||||
const res = await fetch(hook.url, {
|
||||
method: hook.method,
|
||||
headers: {
|
||||
@@ -244,56 +272,58 @@ async function handleIncomingMessage(msg: WAWebJS.Message) {
|
||||
body,
|
||||
});
|
||||
|
||||
if (!res.ok) log(`⚠️ Webhook ${hook.url} gagal: ${res.status}`);
|
||||
const responseJson = await res.json();
|
||||
const responseText = await res.text();
|
||||
|
||||
if (!res.ok) {
|
||||
log(`⚠️ Webhook ${hook.url} gagal: ${res.status}`);
|
||||
log(responseText);
|
||||
await msg.reply("Maaf, terjadi kesalahan saat memproses pesan Anda [ERR01]");
|
||||
return;
|
||||
}
|
||||
|
||||
const responseJson = JSON.parse(responseText);
|
||||
|
||||
if (hook.replay) {
|
||||
try {
|
||||
// === Simulasikan sedang mengetik ===
|
||||
const chat = await msg.getChat();
|
||||
await chat.sendStateTyping(); // tampilkan status 'sedang mengetik...'
|
||||
|
||||
// Durasi delay tergantung panjang teks (lebih panjang = lebih lama)
|
||||
const textResponseRaw = hook.replayKey
|
||||
? getValueByPath(responseJson, hook.replayKey, JSON.stringify(responseJson))
|
||||
: JSON.stringify(responseJson, null, 2);
|
||||
|
||||
const typingDelay = Math.min(5000, Math.max(1500, textResponseRaw.length * 20)); // 1.5–5 detik
|
||||
await new Promise((resolve) => setTimeout(resolve, typingDelay));
|
||||
const typingDelay = Math.min(5000, Math.max(1500, textResponseRaw.length * 20));
|
||||
await new Promise((r) => setTimeout(r, typingDelay));
|
||||
|
||||
// Setelah delay, hentikan typing indicator
|
||||
await chat.clearState(); // hilangkan status "mengetik..."
|
||||
|
||||
// Kirim balasan ke pengirim
|
||||
await msg.reply(textResponseRaw);
|
||||
await chat.clearState();
|
||||
// send message
|
||||
await chat.sendMessage(textResponseRaw);
|
||||
|
||||
log(`💬 Balasan dikirim ke ${msg.from} setelah mengetik selama ${typingDelay}ms`);
|
||||
} catch (err) {
|
||||
log('⚠️ Gagal menampilkan status mengetik:', err);
|
||||
await msg.reply(hook.replayKey
|
||||
? getValueByPath(responseJson, hook.replayKey, JSON.stringify(responseJson))
|
||||
: JSON.stringify(responseJson, null, 2)
|
||||
);
|
||||
await msg.reply("Maaf, terjadi kesalahan saat memproses pesan Anda [ERR03]");
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
log(`❌ Gagal kirim ke ${hook.url}:`, err);
|
||||
await msg.reply("Maaf, terjadi kesalahan saat memproses pesan Anda [ERR04]");
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
log('❌ Error mengirim webhook:', error);
|
||||
await msg.reply("Maaf, terjadi kesalahan saat memproses pesan Anda [ERR05]");
|
||||
}
|
||||
} catch (err) {
|
||||
log('❌ Error handling pesan:', err);
|
||||
await msg.reply("Maaf, terjadi kesalahan saat memproses pesan Anda [ERR06]");
|
||||
} finally {
|
||||
await chat.clearState();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function payloadConverter({ payload, data }: { payload: string; data: DataMessage }) {
|
||||
try {
|
||||
const map: Record<string, string | number | boolean | null> = {
|
||||
const map: Record<string, any> = {
|
||||
'data.from': data.from,
|
||||
'data.fromNumber': data.fromNumber,
|
||||
'data.fromMe': data.fromMe,
|
||||
@@ -303,19 +333,30 @@ function payloadConverter({ payload, data }: { payload: string; data: DataMessag
|
||||
'data.to': data.to,
|
||||
'data.deviceType': data.deviceType,
|
||||
'data.notifyName': data.notifyName,
|
||||
'data.media.data': data.media?.data ?? null,
|
||||
'data.media.mimetype': data.media?.mimetype ?? null,
|
||||
'data.media.filename': data.media?.filename ?? null,
|
||||
'data.media.filesize': data.media?.filesize ?? 0,
|
||||
'data.media': data.media
|
||||
};
|
||||
|
||||
let result = payload;
|
||||
|
||||
for (const [key, value] of Object.entries(map)) {
|
||||
result = result.replace(new RegExp(`{{\\s*${key}\\s*}}`, 'g'), String(value ?? ''));
|
||||
let safeValue: string;
|
||||
|
||||
if (value === null || value === undefined) {
|
||||
safeValue = '';
|
||||
} else if (typeof value === 'object') {
|
||||
// Perbaikan di sini — objek seperti media dikonversi ke JSON string
|
||||
safeValue = JSON.stringify(value);
|
||||
} else {
|
||||
safeValue = String(value);
|
||||
}
|
||||
|
||||
result = result.replace(new RegExp(`{{\\s*${key}\\s*}}`, 'g'), safeValue);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch {
|
||||
return JSON.stringify(data, null, 2);
|
||||
} catch (err) {
|
||||
console.error("⚠️ payloadConverter error:", err);
|
||||
return JSON.stringify(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
343
src/server/lib/wa/wa_service.txt
Normal file
343
src/server/lib/wa/wa_service.txt
Normal file
@@ -0,0 +1,343 @@
|
||||
import WAWebJS, { Client, LocalAuth, MessageMedia } from 'whatsapp-web.js';
|
||||
import qrcode from 'qrcode-terminal';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { prisma } from '../prisma';
|
||||
import { getValueByPath } from '../get_value_by_path';
|
||||
|
||||
// === KONFIGURASI UTAMA ===
|
||||
const MEDIA_DIR = path.join(process.cwd(), 'downloads');
|
||||
await ensureDir(MEDIA_DIR);
|
||||
|
||||
type DataMessage = {
|
||||
from: string;
|
||||
fromNumber: string;
|
||||
fromMe: boolean;
|
||||
body: string;
|
||||
hasMedia: boolean;
|
||||
type: WAWebJS.MessageTypes;
|
||||
to: string;
|
||||
deviceType: string;
|
||||
media: {
|
||||
data: WAWebJS.MessageMedia["data"];
|
||||
mimetype: WAWebJS.MessageMedia["mimetype"];
|
||||
filename: WAWebJS.MessageMedia["filename"];
|
||||
filesize: WAWebJS.MessageMedia["filesize"];
|
||||
};
|
||||
notifyName: string;
|
||||
}
|
||||
|
||||
// === STATE GLOBAL ===
|
||||
const state = {
|
||||
client: null as Client | null,
|
||||
reconnectTimeout: null as NodeJS.Timeout | null,
|
||||
isReconnecting: false,
|
||||
isStarting: false,
|
||||
qr: null as string | null,
|
||||
ready: false,
|
||||
async restart() {
|
||||
log('🔄 Restart manual diminta...');
|
||||
await destroyClient();
|
||||
await startClient();
|
||||
},
|
||||
|
||||
async forceStart() {
|
||||
log('⚠️ Force start — menghapus cache dan session auth...');
|
||||
await destroyClient();
|
||||
await safeRm("./.wwebjs_auth");
|
||||
await safeRm("./wwebjs_cache");
|
||||
await startClient();
|
||||
},
|
||||
async stop() {
|
||||
log('🛑 Stop manual diminta...');
|
||||
await destroyClient();
|
||||
},
|
||||
};
|
||||
|
||||
// === UTIL ===
|
||||
function log(...args: any[]) {
|
||||
console.log(`[${new Date().toISOString()}]`, ...args);
|
||||
}
|
||||
|
||||
async function ensureDir(dir: string) {
|
||||
try {
|
||||
await fs.access(dir);
|
||||
} catch {
|
||||
await fs.mkdir(dir, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
async function safeRm(path: string) {
|
||||
try {
|
||||
await fs.rm(path, { recursive: true, force: true });
|
||||
} catch (err) {
|
||||
log(`⚠️ Gagal hapus ${path}:`, err);
|
||||
}
|
||||
}
|
||||
|
||||
// === CLEANUP CLIENT ===
|
||||
async function destroyClient() {
|
||||
if (state.reconnectTimeout) {
|
||||
clearTimeout(state.reconnectTimeout);
|
||||
state.reconnectTimeout = null;
|
||||
}
|
||||
if (state.client) {
|
||||
try {
|
||||
state.client.removeAllListeners();
|
||||
await state.client.destroy();
|
||||
log('🧹 Client lama dihentikan & listener dibersihkan');
|
||||
} catch (err) {
|
||||
log('⚠️ Gagal destroy client:', err);
|
||||
}
|
||||
state.client = null;
|
||||
state.ready = false;
|
||||
}
|
||||
}
|
||||
|
||||
// === PEMBUATAN CLIENT ===
|
||||
async function startClient() {
|
||||
if (state.isStarting || state.isReconnecting) {
|
||||
log('⏳ startClient diabaikan — proses sedang berjalan...');
|
||||
return;
|
||||
}
|
||||
state.isStarting = true;
|
||||
|
||||
await destroyClient();
|
||||
|
||||
log('🚀 Memulai WhatsApp client...');
|
||||
const client = new Client({
|
||||
authStrategy: new LocalAuth({
|
||||
dataPath: path.join(process.cwd(), '.wwebjs_auth'),
|
||||
}),
|
||||
puppeteer: {
|
||||
headless: true,
|
||||
args: [
|
||||
'--no-sandbox',
|
||||
'--disable-setuid-sandbox',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-gpu',
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
state.client = client;
|
||||
|
||||
// === EVENT LISTENERS ===
|
||||
client.on('qr', (qr) => {
|
||||
state.qr = qr;
|
||||
qrcode.generate(qr, { small: true });
|
||||
log('🔑 QR code baru diterbitkan');
|
||||
});
|
||||
|
||||
client.on('ready', () => {
|
||||
log('✅ WhatsApp client siap digunakan!');
|
||||
state.ready = true;
|
||||
state.isReconnecting = false;
|
||||
state.isStarting = false;
|
||||
state.qr = null;
|
||||
if (state.reconnectTimeout) {
|
||||
clearTimeout(state.reconnectTimeout);
|
||||
state.reconnectTimeout = null;
|
||||
}
|
||||
});
|
||||
|
||||
client.on('auth_failure', (msg) => {
|
||||
log('❌ Autentikasi gagal:', msg);
|
||||
state.ready = false;
|
||||
});
|
||||
|
||||
client.on('disconnected', async (reason) => {
|
||||
log('⚠️ Client terputus:', reason);
|
||||
state.ready = false;
|
||||
|
||||
if (state.reconnectTimeout) clearTimeout(state.reconnectTimeout);
|
||||
log('⏳ Mencoba reconnect dalam 5 detik...');
|
||||
|
||||
state.reconnectTimeout = setTimeout(async () => {
|
||||
state.isReconnecting = false;
|
||||
await startClient();
|
||||
}, 5000);
|
||||
});
|
||||
|
||||
client.on('message', handleIncomingMessage);
|
||||
|
||||
// === INISIALISASI ===
|
||||
try {
|
||||
await client.initialize();
|
||||
} catch (err) {
|
||||
log('❌ Gagal inisialisasi client:', err);
|
||||
log('⏳ Mencoba reconnect dalam 10 detik...');
|
||||
state.reconnectTimeout = setTimeout(async () => {
|
||||
state.isReconnecting = false;
|
||||
await startClient();
|
||||
}, 10000);
|
||||
} finally {
|
||||
state.isStarting = false;
|
||||
}
|
||||
}
|
||||
|
||||
// === HANDLER PESAN MASUK ===
|
||||
async function handleIncomingMessage(msg: WAWebJS.Message) {
|
||||
log(`💬 Pesan dari ${msg.from}: ${msg.body || '[MEDIA]'}`);
|
||||
if (msg.from.endsWith('@g.us') || msg.isStatus || msg.from === 'status@broadcast') {
|
||||
log(`🚫 Pesan dari grup/status diabaikan (${msg.from})`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
const body = msg.body?.toLowerCase().trim() || '';
|
||||
const notifyName = (msg as any)._data.notifyName;
|
||||
|
||||
const dataMessage: DataMessage = {
|
||||
from: msg.from,
|
||||
fromNumber: msg.from.split('@')[0] || '',
|
||||
fromMe: msg.fromMe,
|
||||
body: msg.body,
|
||||
hasMedia: msg.hasMedia,
|
||||
type: msg.type,
|
||||
to: msg.to,
|
||||
deviceType: msg.deviceType,
|
||||
media: {
|
||||
data: null as unknown as WAWebJS.MessageMedia['data'],
|
||||
mimetype: null as unknown as WAWebJS.MessageMedia['mimetype'],
|
||||
filename: null as unknown as WAWebJS.MessageMedia['filename'],
|
||||
filesize: null as unknown as WAWebJS.MessageMedia['filesize'],
|
||||
|
||||
},
|
||||
notifyName,
|
||||
};
|
||||
|
||||
// Media handler
|
||||
if (msg.hasMedia) {
|
||||
const media = await msg.downloadMedia();
|
||||
|
||||
dataMessage.media = {
|
||||
data: media.data,
|
||||
mimetype: media.mimetype,
|
||||
filename: media.filename,
|
||||
filesize: media.filesize
|
||||
};
|
||||
}
|
||||
|
||||
// to web hook
|
||||
try {
|
||||
const webhooks = await prisma.webHook.findMany({ where: { enabled: true } });
|
||||
|
||||
if (!webhooks.length) {
|
||||
log('🚫 Tidak ada webhook yang aktif');
|
||||
return;
|
||||
}
|
||||
|
||||
await Promise.allSettled(
|
||||
webhooks.map(async (hook) => {
|
||||
try {
|
||||
console.log("send webhook " + hook.url);
|
||||
const body = payloadConverter({ payload: hook.payload ?? JSON.stringify(dataMessage), data: dataMessage });
|
||||
const res = await fetch(hook.url, {
|
||||
method: hook.method,
|
||||
headers: {
|
||||
...(JSON.parse(hook.headers ?? '{}') as Record<string, string>),
|
||||
...(hook.apiToken ? { Authorization: `Bearer ${hook.apiToken}` } : {}),
|
||||
},
|
||||
body,
|
||||
});
|
||||
|
||||
if (!res.ok) log(`⚠️ Webhook ${hook.url} gagal: ${res.status}`);
|
||||
const responseJson = await res.json();
|
||||
|
||||
if (hook.replay) {
|
||||
try {
|
||||
// === Simulasikan sedang mengetik ===
|
||||
const chat = await msg.getChat();
|
||||
await chat.sendStateTyping(); // tampilkan status 'sedang mengetik...'
|
||||
|
||||
// Durasi delay tergantung panjang teks (lebih panjang = lebih lama)
|
||||
const textResponseRaw = hook.replayKey
|
||||
? getValueByPath(responseJson, hook.replayKey, JSON.stringify(responseJson))
|
||||
: JSON.stringify(responseJson, null, 2);
|
||||
|
||||
const typingDelay = Math.min(5000, Math.max(1500, textResponseRaw.length * 20)); // 1.5–5 detik
|
||||
await new Promise((resolve) => setTimeout(resolve, typingDelay));
|
||||
|
||||
// Setelah delay, hentikan typing indicator
|
||||
await chat.clearState(); // hilangkan status "mengetik..."
|
||||
|
||||
// Kirim balasan ke pengirim
|
||||
await msg.reply(textResponseRaw);
|
||||
|
||||
log(`💬 Balasan dikirim ke ${msg.from} setelah mengetik selama ${typingDelay}ms`);
|
||||
} catch (err) {
|
||||
log('⚠️ Gagal menampilkan status mengetik:', err);
|
||||
await msg.reply(hook.replayKey
|
||||
? getValueByPath(responseJson, hook.replayKey, JSON.stringify(responseJson))
|
||||
: JSON.stringify(responseJson, null, 2)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
log(`❌ Gagal kirim ke ${hook.url}:`, err);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
} catch (err) {
|
||||
log('❌ Error handling pesan:', err);
|
||||
}
|
||||
}
|
||||
|
||||
function payloadConverter({ payload, data }: { payload: string; data: DataMessage }) {
|
||||
try {
|
||||
const map: Record<string, string | number | boolean | null> = {
|
||||
'data.from': data.from,
|
||||
'data.fromNumber': data.fromNumber,
|
||||
'data.fromMe': data.fromMe,
|
||||
'data.body': data.body,
|
||||
'data.hasMedia': data.hasMedia,
|
||||
'data.type': data.type,
|
||||
'data.to': data.to,
|
||||
'data.deviceType': data.deviceType,
|
||||
'data.notifyName': data.notifyName,
|
||||
'data.media.data': data.media?.data ?? null,
|
||||
'data.media.mimetype': data.media?.mimetype ?? null,
|
||||
'data.media.filename': data.media?.filename ?? null,
|
||||
'data.media.filesize': data.media?.filesize ?? 0,
|
||||
};
|
||||
|
||||
let result = payload;
|
||||
for (const [key, value] of Object.entries(map)) {
|
||||
result = result.replace(new RegExp(`{{\\s*${key}\\s*}}`, 'g'), String(value ?? ''));
|
||||
}
|
||||
return result;
|
||||
} catch {
|
||||
return JSON.stringify(data, null, 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === CLEANUP SAAT EXIT ===
|
||||
process.on('SIGINT', async () => {
|
||||
log('🛑 SIGINT diterima, menutup client...');
|
||||
await destroyClient();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on('SIGTERM', async () => {
|
||||
log('🛑 SIGTERM diterima, menutup client...');
|
||||
await destroyClient();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
|
||||
const getState = () => state;
|
||||
|
||||
export { startClient, destroyClient, getState };
|
||||
|
||||
if (import.meta.main) {
|
||||
await startClient();
|
||||
}
|
||||
Reference in New Issue
Block a user