tambahannya
This commit is contained in:
11
bun.lock
11
bun.lock
@@ -17,6 +17,7 @@
|
||||
"@prisma/client": "^6.17.1",
|
||||
"@tabler/icons-react": "^3.35.0",
|
||||
"@types/express": "^5.0.3",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/jwt-decode": "^3.1.0",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/qrcode-terminal": "^0.12.2",
|
||||
@@ -25,6 +26,7 @@
|
||||
"dayjs": "^1.11.18",
|
||||
"elysia": "^1.4.11",
|
||||
"form-data": "^4.0.4",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"meta-cloud-api": "^1.3.0",
|
||||
@@ -41,6 +43,7 @@
|
||||
"whatsapp-api-js": "^6.1.1",
|
||||
"whatsapp-client-sdk": "^1.6.0",
|
||||
"whatsapp-web.js": "^1.34.1",
|
||||
"yaml": "^2.8.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
@@ -140,6 +143,8 @@
|
||||
|
||||
"@types/http-errors": ["@types/http-errors@2.0.5", "", {}, "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg=="],
|
||||
|
||||
"@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="],
|
||||
|
||||
"@types/jwt-decode": ["@types/jwt-decode@3.1.0", "", { "dependencies": { "jwt-decode": "*" } }, "sha512-tthwik7TKkou3mVnBnvVuHnHElbjtdbM63pdBCbZTirCt3WAdM73Y79mOri7+ljsS99ZVwUFZHLMxJuJnv/z1w=="],
|
||||
|
||||
"@types/lodash": ["@types/lodash@4.17.20", "", {}, "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA=="],
|
||||
@@ -174,6 +179,8 @@
|
||||
|
||||
"archiver-utils": ["archiver-utils@2.1.0", "", { "dependencies": { "glob": "^7.1.4", "graceful-fs": "^4.2.0", "lazystream": "^1.0.0", "lodash.defaults": "^4.2.0", "lodash.difference": "^4.5.0", "lodash.flatten": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.union": "^4.6.0", "normalize-path": "^3.0.0", "readable-stream": "^2.0.0" } }, "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw=="],
|
||||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="],
|
||||
|
||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||
@@ -386,6 +393,8 @@
|
||||
|
||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
|
||||
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
||||
|
||||
"jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="],
|
||||
|
||||
"jwt-decode": ["jwt-decode@4.0.0", "", {}, "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA=="],
|
||||
@@ -652,6 +661,8 @@
|
||||
|
||||
"ws": ["ws@8.9.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg=="],
|
||||
|
||||
"yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="],
|
||||
|
||||
"yauzl": ["yauzl@2.10.0", "", { "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g=="],
|
||||
|
||||
"zhead": ["zhead@2.2.4", "", {}, "sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag=="],
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"@prisma/client": "^6.17.1",
|
||||
"@tabler/icons-react": "^3.35.0",
|
||||
"@types/express": "^5.0.3",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/jwt-decode": "^3.1.0",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/qrcode-terminal": "^0.12.2",
|
||||
@@ -31,6 +32,7 @@
|
||||
"dayjs": "^1.11.18",
|
||||
"elysia": "^1.4.11",
|
||||
"form-data": "^4.0.4",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"meta-cloud-api": "^1.3.0",
|
||||
@@ -46,7 +48,8 @@
|
||||
"uuid": "^13.0.0",
|
||||
"whatsapp-api-js": "^6.1.1",
|
||||
"whatsapp-client-sdk": "^1.6.0",
|
||||
"whatsapp-web.js": "^1.34.1"
|
||||
"whatsapp-web.js": "^1.34.1",
|
||||
"yaml": "^2.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
|
||||
@@ -2,8 +2,6 @@ import Elysia, { t } from "elysia";
|
||||
import { prisma } from "../lib/prisma";
|
||||
import type { WAHookMessage } from "types/wa_messages";
|
||||
import _ from "lodash";
|
||||
|
||||
import type { GetParams, PostData } from "whatsapp-api-js/types";
|
||||
import { logger } from "../lib/logger";
|
||||
|
||||
|
||||
@@ -27,24 +25,62 @@ async function fetchWithTimeout(input: RequestInfo, init: RequestInit, timeoutMs
|
||||
}
|
||||
}
|
||||
|
||||
async function sendReplyMessage(to: string, message: string, phoneNumberId: string, token: string) {
|
||||
return await fetch(`https://graph.facebook.com/v21.0/${phoneNumberId}/messages`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
async function flowAi({ question, name, number }: { question: string, name: string, number: string }) {
|
||||
|
||||
const flow = await prisma.chatFlows.findUnique({
|
||||
where: {
|
||||
id: "1",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messaging_product: "whatsapp",
|
||||
to,
|
||||
text: { body: message }
|
||||
})
|
||||
|
||||
if (!flow) {
|
||||
logger.info("[POST] no flow found")
|
||||
}
|
||||
|
||||
if (flow?.defaultFlow && flow.active) {
|
||||
const { flowUrl, flowToken } = flow
|
||||
const response = await fetchWithTimeout(`${flowUrl}/prediction/${flow.defaultFlow}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${flowToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
question,
|
||||
overrideConfig: {
|
||||
sessionId: `${_.kebabCase(name)}_x_${number}`,
|
||||
vars: { userName: _.kebabCase(name), userPhone: number },
|
||||
},
|
||||
}),
|
||||
})
|
||||
});
|
||||
|
||||
const responseText = await response.text()
|
||||
try {
|
||||
const result = JSON.parse(responseText)
|
||||
const create = await prisma.waHook.create({
|
||||
data: {
|
||||
data: JSON.stringify({
|
||||
question,
|
||||
name,
|
||||
number,
|
||||
answer: result.text,
|
||||
flowId: flow.defaultFlow,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
if (flow?.waPhoneNumberId && flow?.waToken && flow.active) {
|
||||
client.sendText(number, result.text)
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(responseText)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
const allowedTypeMessage = ["text"]
|
||||
|
||||
const WaHookRoute = new Elysia({
|
||||
prefix: "/wa-hook",
|
||||
tags: ["WhatsApp Hook"],
|
||||
@@ -90,95 +126,28 @@ const WaHookRoute = new Elysia({
|
||||
|
||||
// ✅ Handle incoming message (POST)
|
||||
.post("/hook", async ({ body }) => {
|
||||
logger.info("[POST] Incoming WhatsApp Webhook:", body)
|
||||
|
||||
const webhook = client.parseWebhook(body)
|
||||
|
||||
logger.info(`[POST] Message Type: ${webhook[0]?.type}`)
|
||||
|
||||
if (webhook[0]?.type === WhatsAppMessageType.TEXT) {
|
||||
const message = webhook[0]?.text
|
||||
const from = webhook[0]?.from
|
||||
const name = webhook[0].contact?.name
|
||||
|
||||
const dataMessage = {
|
||||
message,
|
||||
from,
|
||||
name,
|
||||
if (message && from) {
|
||||
logger.info(`[POST] Message: ${JSON.stringify({
|
||||
message,
|
||||
from,
|
||||
name
|
||||
})}`)
|
||||
flowAi({
|
||||
question: message,
|
||||
name: name || "default_name",
|
||||
number: from,
|
||||
})
|
||||
}
|
||||
|
||||
logger.info(`[POST] Message: ${JSON.stringify(dataMessage)}`)
|
||||
}
|
||||
|
||||
// const create = await prisma.waHook.create({
|
||||
// data: {
|
||||
// data: body,
|
||||
// },
|
||||
// });
|
||||
|
||||
// const waHook = body as WAHookMessage
|
||||
// const flow = await prisma.chatFlows.findUnique({
|
||||
// where: {
|
||||
// id: "1",
|
||||
// },
|
||||
// })
|
||||
|
||||
// if (!flow) {
|
||||
// logger.info("[POST] no flow found")
|
||||
// }
|
||||
|
||||
// if (flow?.defaultFlow && flow.active) {
|
||||
// const { flowUrl, flowToken } = flow
|
||||
// const question = waHook?.entry[0]?.changes[0]?.value?.messages[0]?.text?.body
|
||||
// const contacts = waHook?.entry[0]?.changes[0]?.value?.contacts[0]
|
||||
// const name = contacts?.profile?.name
|
||||
// const number = contacts?.wa_id
|
||||
|
||||
// const response = await fetchWithTimeout(`${flowUrl}/prediction/${flow.defaultFlow}`, {
|
||||
// headers: {
|
||||
// Authorization: `Bearer ${flowToken}`,
|
||||
// 'Content-Type': 'application/json',
|
||||
// },
|
||||
// method: 'POST',
|
||||
// body: JSON.stringify({
|
||||
// question,
|
||||
// overrideConfig: {
|
||||
// sessionId: `${_.kebabCase(name)}_x_${number}`,
|
||||
// vars: { userName: _.kebabCase(name), userPhone: number },
|
||||
// },
|
||||
// }),
|
||||
// })
|
||||
|
||||
// const responseText = await response.text()
|
||||
// try {
|
||||
// const result = JSON.parse(responseText)
|
||||
// let createData = create.data as any
|
||||
// createData.answer = {
|
||||
// text: result.text,
|
||||
// type: "text",
|
||||
// flowId: flow.defaultFlow
|
||||
// }
|
||||
|
||||
// await prisma.waHook.update({
|
||||
// where: {
|
||||
// id: create.id,
|
||||
// },
|
||||
// data: {
|
||||
// data: createData,
|
||||
// },
|
||||
// })
|
||||
|
||||
// if (flow?.waPhoneNumberId && flow?.waToken && number) {
|
||||
// // await sendReplyMessage(number, result.text, flow.waPhoneNumberId, flow.waToken)
|
||||
// }
|
||||
|
||||
// } catch (error) {
|
||||
// console.log(error)
|
||||
// console.log(responseText)
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "WhatsApp Hook received"
|
||||
|
||||
67
xx.ts
67
xx.ts
@@ -1,3 +1,66 @@
|
||||
import { whatsappApiInit } from "@/server/lib/wa-api/wa-api";
|
||||
import fs from "fs";
|
||||
import { parse, stringify } from "yaml";
|
||||
|
||||
whatsappApiInit()
|
||||
export interface LogRotateOptions {
|
||||
maxSize?: string;
|
||||
maxFile?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tambahkan log rotate (logging.driver json-file) ke semua service
|
||||
* yang belum memiliki konfigurasi logging di docker-compose.yml.
|
||||
*/
|
||||
export async function applyLogRotateCompose(
|
||||
filePath: string,
|
||||
options: LogRotateOptions = {}
|
||||
) {
|
||||
const { maxSize = "10m", maxFile = "3" } = options;
|
||||
|
||||
// Pastikan file ada
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(`❌ File not found: ${filePath}`);
|
||||
}
|
||||
|
||||
const raw = fs.readFileSync(filePath, "utf8");
|
||||
const compose = parse(raw); // ✅ Pakai yaml.parse()
|
||||
|
||||
if (!compose.services) {
|
||||
throw new Error("❌ Tidak ditemukan 'services:' di docker-compose.yml");
|
||||
}
|
||||
|
||||
let modified = false;
|
||||
|
||||
for (const [name, service] of Object.entries<any>(compose.services)) {
|
||||
if (!service.logging) {
|
||||
service.logging = {
|
||||
driver: "json-file",
|
||||
options: {
|
||||
"max-size": maxSize,
|
||||
"max-file": maxFile,
|
||||
},
|
||||
};
|
||||
console.log(`✅ Log rotate ditambahkan ke: ${name}`);
|
||||
modified = true;
|
||||
} else {
|
||||
console.log(`⚠️ Lewati (sudah ada logging): ${name}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!modified) {
|
||||
console.log("👌 Semua service sudah punya log-rotate, tidak ada perubahan.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Backup file lama
|
||||
const backupPath = `${filePath}.backup-${Date.now()}`;
|
||||
fs.writeFileSync(backupPath, raw, "utf8");
|
||||
|
||||
// Simpan file baru
|
||||
const updated = stringify(compose); // ✅ Pakai yaml.stringify()
|
||||
fs.writeFileSync(filePath, updated, "utf8");
|
||||
|
||||
console.log(`✅ Selesai update file: ${filePath}`);
|
||||
console.log(`📦 Backup dibuat: ${backupPath}`);
|
||||
}
|
||||
|
||||
applyLogRotateCompose("compose.yml");
|
||||
|
||||
Reference in New Issue
Block a user