From 74c63c39440473c36ec9ccc2e01cacce687d8da7 Mon Sep 17 00:00:00 2001 From: bipproduction Date: Mon, 3 Nov 2025 21:18:51 +0800 Subject: [PATCH] tambahan --- bun.lock | 12 ++++- package.json | 2 + qr.png | Bin 0 -> 4773 bytes src/server/routes/wa_route.ts | 82 +++++++++++++++++++++++++++++++--- x.sh | 5 ++- xx.ts | 4 +- 6 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 qr.png diff --git a/bun.lock b/bun.lock index 83bc8c2..f22d38c 100644 --- a/bun.lock +++ b/bun.lock @@ -20,6 +20,7 @@ "@types/js-yaml": "^4.0.9", "@types/jwt-decode": "^3.1.0", "@types/lodash": "^4.17.20", + "@types/mime-types": "^3.0.1", "@types/qrcode-terminal": "^0.12.2", "@types/sharp": "^0.32.0", "add": "^2.0.6", @@ -32,6 +33,7 @@ "lodash": "^4.17.21", "meta-cloud-api": "^1.3.0", "mime": "^4.1.0", + "mime-types": "^3.0.1", "node-fetch": "^3.3.2", "pino": "^10.1.0", "pino-pretty": "^13.1.2", @@ -202,6 +204,8 @@ "@types/mime": ["@types/mime@1.3.5", "", {}, "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="], + "@types/mime-types": ["@types/mime-types@3.0.1", "", {}, "sha512-xRMsfuQbnRq1Ef+C+RKaENOxXX87Ygl38W1vDfPHRku02TgQr+Qd8iivLtAMcR0KF5/29xlnFihkTlbqFrGOVQ=="], + "@types/node": ["@types/node@24.7.0", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-IbKooQVqUBrlzWTi79E8Fw78l8k1RNtlDDNWsFZs7XonuQSJ8oNYfEeclhprUldXISRMLzBpILuKgPlIxm+/Yw=="], "@types/qrcode-terminal": ["@types/qrcode-terminal@0.12.2", "", {}, "sha512-v+RcIEJ+Uhd6ygSQ0u5YYY7ZM+la7GgPbs0V/7l/kFs2uO4S8BcIUEMoP7za4DNIqNnUD5npf0A/7kBhrCKG5Q=="], @@ -480,9 +484,9 @@ "mime": ["mime@4.1.0", "", { "bin": { "mime": "bin/cli.js" } }, "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw=="], - "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], - "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="], "minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], @@ -742,6 +746,8 @@ "fluent-ffmpeg/async": ["async@0.2.10", "", {}, "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ=="], + "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "giget/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], @@ -776,6 +782,8 @@ "duplexer2/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], "lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], diff --git a/package.json b/package.json index 5c29178..4e12950 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@types/js-yaml": "^4.0.9", "@types/jwt-decode": "^3.1.0", "@types/lodash": "^4.17.20", + "@types/mime-types": "^3.0.1", "@types/qrcode-terminal": "^0.12.2", "@types/sharp": "^0.32.0", "add": "^2.0.6", @@ -38,6 +39,7 @@ "lodash": "^4.17.21", "meta-cloud-api": "^1.3.0", "mime": "^4.1.0", + "mime-types": "^3.0.1", "node-fetch": "^3.3.2", "pino": "^10.1.0", "pino-pretty": "^13.1.2", diff --git a/qr.png b/qr.png new file mode 100644 index 0000000000000000000000000000000000000000..7643d732b29ed987a9b0908637ce817de71156d6 GIT binary patch literal 4773 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 A;Q#;t literal 0 HcmV?d00001 diff --git a/src/server/routes/wa_route.ts b/src/server/routes/wa_route.ts index 8894d27..b290c6a 100644 --- a/src/server/routes/wa_route.ts +++ b/src/server/routes/wa_route.ts @@ -1,6 +1,8 @@ -import Elysia, { t } from "elysia"; +import Elysia, { t, type Context } from "elysia"; import { startClient, getState } from "../lib/wa/wa_service"; import _ from "lodash"; +import mime from "mime-types"; +import { MessageMedia } from "whatsapp-web.js"; const WaRoute = new Elysia({ prefix: "/wa", @@ -48,7 +50,7 @@ const WaRoute = new Elysia({ state: _.omit(state, "client"), }; }) - .post("send-text", async ({body}) => { + .post("send-text", async ({ body }) => { const state = getState(); if (!state.ready) { return { @@ -62,15 +64,15 @@ const WaRoute = new Elysia({ message: "WhatsApp client not ready", }; } - - + + const chat = await client.getChatById(`${body.number}@c.us`); await chat.sendMessage(body.text); return { message: "WhatsApp route ready", }; - },{ + }, { body: t.Object({ number: t.String(), text: t.String(), @@ -80,5 +82,75 @@ const WaRoute = new Elysia({ tags: ["WhatsApp"], } }) + .post( + "/send-media", + async ({ body }) => { + const state = getState(); + if (!state.ready) + return { message: "WhatsApp route not ready" }; + + const client = state.client; + if (!client) + return { message: "WhatsApp client not ready" }; + + try { + const { number, caption, media } = body; + const jid = `${number}@c.us`; + + // Siapkan data media + const { data, filename, mimetype } = media; + const mimeType = mimetype || mime.lookup(filename) || "application/octet-stream"; + const fileName = filename || `file.${mime.extension(mimeType) || "bin"}`; + + const waMedia = new MessageMedia(mimeType, data, fileName); + + // Tentukan opsi pengiriman otomatis + const sendOptions: any = { caption }; + + if (mimeType.startsWith("audio/")) { + // kirim voice note jika ogg/opus + sendOptions.sendAudioAsVoice = + mimeType.includes("ogg") || mimeType.includes("opus"); + } else if ( + !mimeType.startsWith("image/") && + !mimeType.startsWith("video/") + ) { + // selain gambar/video kirim sebagai dokumen + sendOptions.sendMediaAsDocument = true; + } + + await client.sendMessage(jid, waMedia, sendOptions); + + return { + success: true, + message: `✅ Media sent to ${number}`, + info: { filename: fileName, mimetype: mimeType }, + }; + } catch (err: any) { + console.error("Send media error:", err); + return { + success: false, + message: "❌ Failed to send media", + error: err.message, + }; + } + }, + { + body: t.Object({ + number: t.String({ minLength: 10, maxLength: 15 }), + caption: t.Optional(t.String({ maxLength: 255 })), + media: t.Object({ + data: t.String(), // base64 tanpa prefix + filename: t.String({ minLength: 1, maxLength: 255 }), + mimetype: t.String({ minLength: 1, maxLength: 255 }), + }), + }), + detail: { + description: + "Send media (image, audio, video, PDF, or any file) to WhatsApp", + tags: ["WhatsApp"], + }, + } + ); export default WaRoute; diff --git a/x.sh b/x.sh index 4bb1993..e1e68e5 100644 --- a/x.sh +++ b/x.sh @@ -1,3 +1,4 @@ -curl -X POST https://n8n.wibudev.com/form/82848bc4-5ea2-4e5a-8bb6-3c09b94a8c5d \ +curl -X POST "https://n8n.wibudev.com/form/d65f7cda-4fb6-40cc-aaa0-59127e224429" \ + -H "accept: */*" \ -H "Content-Type: multipart/form-data" \ - -F "file=@/Users/bip/Documents/projects/jenna/wajs-server/xarif.pdf" + -F "data=@//Users/bip/Documents/projects/jenna/wajs-server/xarif.pdf" diff --git a/xx.ts b/xx.ts index 9e068d2..64419f8 100644 --- a/xx.ts +++ b/xx.ts @@ -1,3 +1 @@ -import mime from "mime"; - -console.log(mime.getAllExtensions("image/png")); +console.log("nama".includes("namxa")) \ No newline at end of file