tambahannya

This commit is contained in:
bipproduction
2025-10-20 07:54:09 +08:00
parent 06478a025f
commit ea1937da6d
21 changed files with 4857 additions and 540 deletions

View File

@@ -20,9 +20,12 @@
"@types/lodash": "^4.17.20",
"@types/qrcode-terminal": "^0.12.2",
"add": "^2.0.6",
"colors": "^1.4.0",
"elysia": "^1.4.11",
"form-data": "^4.0.4",
"jwt-decode": "^4.0.0",
"lodash": "^4.17.21",
"node-fetch": "^3.3.2",
"qrcode-terminal": "^0.12.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
@@ -144,6 +147,8 @@
"async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="],
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
@@ -170,6 +175,8 @@
"c12": ["c12@3.1.0", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^16.6.1", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.4.2", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
"camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="],
"chainsaw": ["chainsaw@0.1.0", "", { "dependencies": { "traverse": ">=0.3.0 <0.4" } }, "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ=="],
@@ -182,6 +189,10 @@
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
"colors": ["colors@1.4.0", "", {}, "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="],
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
"compress-commons": ["compress-commons@4.1.2", "", { "dependencies": { "buffer-crc32": "^0.2.13", "crc32-stream": "^4.0.2", "normalize-path": "^3.0.0", "readable-stream": "^3.6.0" } }, "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
@@ -204,12 +215,16 @@
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
"data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
"deepmerge-ts": ["deepmerge-ts@7.1.5", "", {}, "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw=="],
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
"destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="],
@@ -224,6 +239,8 @@
"dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"duplexer2": ["duplexer2@0.1.4", "", { "dependencies": { "readable-stream": "^2.0.2" } }, "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA=="],
"effect": ["effect@3.16.12", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg=="],
@@ -234,6 +251,14 @@
"end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
"exact-mirror": ["exact-mirror@0.2.2", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-CrGe+4QzHZlnrXZVlo/WbUZ4qQZq8C0uATQVGVgXIrNXgHDBBNFD1VRfssRA2C9t3RYvh3MadZSdg2Wy7HBoQA=="],
"exsolve": ["exsolve@1.0.7", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="],
@@ -248,12 +273,18 @@
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
"fflate": ["fflate@0.8.2", "", {}, "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="],
"file-type": ["file-type@21.0.0", "", { "dependencies": { "@tokenizer/inflate": "^0.2.7", "strtok3": "^10.2.2", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" } }, "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg=="],
"fluent-ffmpeg": ["fluent-ffmpeg@2.1.3", "", { "dependencies": { "async": "^0.2.9", "which": "^1.1.1" } }, "sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q=="],
"form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="],
"formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
"fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="],
"fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="],
@@ -262,16 +293,30 @@
"fstream": ["fstream@1.0.12", "", { "dependencies": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", "mkdirp": ">=0.5 0", "rimraf": "2" } }, "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg=="],
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
"get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="],
"giget": ["giget@2.0.0", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.6.0", "pathe": "^2.0.3" }, "bin": { "giget": "dist/cli.mjs" } }, "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA=="],
"glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="],
"https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
@@ -316,8 +361,14 @@
"marked": ["marked@14.0.0", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="],
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
"mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="],
@@ -330,7 +381,9 @@
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
"node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
@@ -500,6 +553,8 @@
"uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="],
"web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
"whatsapp-web.js": ["whatsapp-web.js@1.34.1", "", { "dependencies": { "@pedroslopez/moduleraid": "^5.0.2", "fluent-ffmpeg": "2.1.3", "mime": "^3.0.0", "node-fetch": "^2.6.9", "node-webpmux": "3.1.7", "puppeteer": "^18.2.1" }, "optionalDependencies": { "archiver": "^5.3.1", "fs-extra": "^10.1.0", "unzipper": "^0.10.11" } }, "sha512-IInGEg+F8wB9M+c61KXGZ4bpwq24ew9EgsyMYxUZ/4CQ8GW/afsGseQO4FZriQ16qpu14pFovsD6YrEYxRbyLw=="],
@@ -550,6 +605,8 @@
"unzipper/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
"whatsapp-web.js/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
"zip-stream/archiver-utils": ["archiver-utils@3.0.4", "", { "dependencies": { "glob": "^7.2.3", "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": "^3.6.0" } }, "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw=="],
"@scalar/themes/@scalar/types/@scalar/openapi-types": ["@scalar/openapi-types@0.2.0", "", { "dependencies": { "zod": "^3.23.8" } }, "sha512-waiKk12cRCqyUCWTOX0K1WEVX46+hVUK+zRPzAahDJ7G0TApvbNkuy5wx7aoUyEk++HHde0XuQnshXnt8jsddA=="],

View File

@@ -0,0 +1,514 @@
{
"nodes": [
{
"id": "llmChain_0",
"position": {
"x": 829.035635359116,
"y": 344.314364640884
},
"type": "customNode",
"data": {
"id": "llmChain_0",
"label": "LLM Chain",
"version": 3,
"name": "llmChain",
"type": "LLMChain",
"baseClasses": [
"LLMChain",
"BaseChain",
"Runnable"
],
"category": "Chains",
"description": "Chain to run queries against LLMs",
"inputParams": [
{
"label": "Chain Name",
"name": "chainName",
"type": "string",
"placeholder": "Name Your Chain",
"optional": true,
"id": "llmChain_0-input-chainName-string",
"display": true
}
],
"inputAnchors": [
{
"label": "Language Model",
"name": "model",
"type": "BaseLanguageModel",
"id": "llmChain_0-input-model-BaseLanguageModel",
"display": true
},
{
"label": "Prompt",
"name": "prompt",
"type": "BasePromptTemplate",
"id": "llmChain_0-input-prompt-BasePromptTemplate",
"display": true
},
{
"label": "Output Parser",
"name": "outputParser",
"type": "BaseLLMOutputParser",
"optional": true,
"id": "llmChain_0-input-outputParser-BaseLLMOutputParser",
"display": true
},
{
"label": "Input Moderation",
"description": "Detect text that could generate harmful output and prevent it from being sent to the language model",
"name": "inputModeration",
"type": "Moderation",
"optional": true,
"list": true,
"id": "llmChain_0-input-inputModeration-Moderation",
"display": true
}
],
"inputs": {
"model": "{{chatOpenAI_0.data.instance}}",
"prompt": "{{promptTemplate_0.data.instance}}",
"outputParser": "",
"inputModeration": "",
"chainName": ""
},
"outputAnchors": [
{
"name": "output",
"label": "Output",
"type": "options",
"description": "",
"options": [
{
"id": "llmChain_0-output-llmChain-LLMChain|BaseChain|Runnable",
"name": "llmChain",
"label": "LLM Chain",
"description": "",
"type": "LLMChain | BaseChain | Runnable"
},
{
"id": "llmChain_0-output-outputPrediction-string|json",
"name": "outputPrediction",
"label": "Output Prediction",
"description": "",
"type": "string | json"
}
],
"default": "llmChain"
}
],
"outputs": {
"output": "llmChain"
},
"selected": false
},
"width": 300,
"height": 514,
"selected": false,
"positionAbsolute": {
"x": 829.035635359116,
"y": 344.314364640884
},
"dragging": false
},
{
"id": "chatOpenAI_0",
"position": {
"x": 357.9495996101249,
"y": -208.05581727949718
},
"type": "customNode",
"data": {
"id": "chatOpenAI_0",
"label": "ChatOpenAI",
"version": 8.3,
"name": "chatOpenAI",
"type": "ChatOpenAI",
"baseClasses": [
"ChatOpenAI",
"BaseChatOpenAI",
"BaseChatModel",
"BaseLanguageModel",
"Runnable"
],
"category": "Chat Models",
"description": "Wrapper around OpenAI large language models that use the Chat endpoint",
"inputParams": [
{
"label": "Connect Credential",
"name": "credential",
"type": "credential",
"credentialNames": [
"openAIApi"
],
"id": "chatOpenAI_0-input-credential-credential",
"display": true
},
{
"label": "Model Name",
"name": "modelName",
"type": "asyncOptions",
"loadMethod": "listModels",
"default": "gpt-4o-mini",
"id": "chatOpenAI_0-input-modelName-asyncOptions",
"display": true
},
{
"label": "Temperature",
"name": "temperature",
"type": "number",
"step": 0.1,
"default": 0.9,
"optional": true,
"id": "chatOpenAI_0-input-temperature-number",
"display": true
},
{
"label": "Streaming",
"name": "streaming",
"type": "boolean",
"default": true,
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-streaming-boolean",
"display": true
},
{
"label": "Max Tokens",
"name": "maxTokens",
"type": "number",
"step": 1,
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-maxTokens-number",
"display": true
},
{
"label": "Top Probability",
"name": "topP",
"type": "number",
"step": 0.1,
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-topP-number",
"display": true
},
{
"label": "Frequency Penalty",
"name": "frequencyPenalty",
"type": "number",
"step": 0.1,
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-frequencyPenalty-number",
"display": true
},
{
"label": "Presence Penalty",
"name": "presencePenalty",
"type": "number",
"step": 0.1,
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-presencePenalty-number",
"display": true
},
{
"label": "Timeout",
"name": "timeout",
"type": "number",
"step": 1,
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-timeout-number",
"display": true
},
{
"label": "Strict Tool Calling",
"name": "strictToolCalling",
"type": "boolean",
"description": "Whether the model supports the `strict` argument when passing in tools. If not specified, the `strict` argument will not be passed to OpenAI.",
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-strictToolCalling-boolean",
"display": true
},
{
"label": "Stop Sequence",
"name": "stopSequence",
"type": "string",
"rows": 4,
"optional": true,
"description": "List of stop words to use when generating. Use comma to separate multiple stop words.",
"additionalParams": true,
"id": "chatOpenAI_0-input-stopSequence-string",
"display": true
},
{
"label": "BasePath",
"name": "basepath",
"type": "string",
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-basepath-string",
"display": true
},
{
"label": "Proxy Url",
"name": "proxyUrl",
"type": "string",
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-proxyUrl-string",
"display": true
},
{
"label": "BaseOptions",
"name": "baseOptions",
"type": "json",
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-baseOptions-json",
"display": true
},
{
"label": "Allow Image Uploads",
"name": "allowImageUploads",
"type": "boolean",
"description": "Allow image input. Refer to the <a href=\"https://docs.flowiseai.com/using-flowise/uploads#image\" target=\"_blank\">docs</a> for more details.",
"default": false,
"optional": true,
"id": "chatOpenAI_0-input-allowImageUploads-boolean",
"display": true
},
{
"label": "Image Resolution",
"description": "This parameter controls the resolution in which the model views the image.",
"name": "imageResolution",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "High",
"name": "high"
},
{
"label": "Auto",
"name": "auto"
}
],
"default": "low",
"optional": false,
"show": {
"allowImageUploads": true
},
"id": "chatOpenAI_0-input-imageResolution-options",
"display": false
},
{
"label": "Reasoning",
"description": "Whether the model supports reasoning. Only applicable for reasoning models.",
"name": "reasoning",
"type": "boolean",
"default": false,
"optional": true,
"additionalParams": true,
"id": "chatOpenAI_0-input-reasoning-boolean",
"display": true
},
{
"label": "Reasoning Effort",
"description": "Constrains effort on reasoning for reasoning models",
"name": "reasoningEffort",
"type": "options",
"options": [
{
"label": "Low",
"name": "low"
},
{
"label": "Medium",
"name": "medium"
},
{
"label": "High",
"name": "high"
}
],
"additionalParams": true,
"show": {
"reasoning": true
},
"id": "chatOpenAI_0-input-reasoningEffort-options",
"display": false
},
{
"label": "Reasoning Summary",
"description": "A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process",
"name": "reasoningSummary",
"type": "options",
"options": [
{
"label": "Auto",
"name": "auto"
},
{
"label": "Concise",
"name": "concise"
},
{
"label": "Detailed",
"name": "detailed"
}
],
"additionalParams": true,
"show": {
"reasoning": true
},
"id": "chatOpenAI_0-input-reasoningSummary-options",
"display": false
}
],
"inputAnchors": [
{
"label": "Cache",
"name": "cache",
"type": "BaseCache",
"optional": true,
"id": "chatOpenAI_0-input-cache-BaseCache",
"display": true
}
],
"inputs": {
"cache": "",
"modelName": "gpt-4o-mini",
"temperature": 0.9,
"streaming": true,
"maxTokens": "",
"topP": "",
"frequencyPenalty": "",
"presencePenalty": "",
"timeout": "",
"strictToolCalling": "",
"stopSequence": "",
"basepath": "",
"proxyUrl": "",
"baseOptions": "",
"allowImageUploads": "",
"imageResolution": "low",
"reasoning": "",
"reasoningEffort": "",
"reasoningSummary": ""
},
"outputAnchors": [
{
"id": "chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatOpenAI|BaseChatModel|BaseLanguageModel|Runnable",
"name": "chatOpenAI",
"label": "ChatOpenAI",
"description": "Wrapper around OpenAI large language models that use the Chat endpoint",
"type": "ChatOpenAI | BaseChatOpenAI | BaseChatModel | BaseLanguageModel | Runnable"
}
],
"outputs": {},
"selected": false
},
"width": 300,
"height": 676,
"selected": false,
"positionAbsolute": {
"x": 357.9495996101249,
"y": -208.05581727949718
},
"dragging": false
},
{
"id": "promptTemplate_0",
"position": {
"x": 355.34464051183033,
"y": 518.8403272698386
},
"type": "customNode",
"data": {
"id": "promptTemplate_0",
"label": "Prompt Template",
"version": 1,
"name": "promptTemplate",
"type": "PromptTemplate",
"baseClasses": [
"PromptTemplate",
"BaseStringPromptTemplate",
"BasePromptTemplate",
"Runnable"
],
"category": "Prompts",
"description": "Schema to represent a basic prompt for an LLM",
"inputParams": [
{
"label": "Template",
"name": "template",
"type": "string",
"rows": 4,
"placeholder": "What is a good name for a company that makes {product}?",
"id": "promptTemplate_0-input-template-string",
"display": true
},
{
"label": "Format Prompt Values",
"name": "promptValues",
"type": "json",
"optional": true,
"acceptVariable": true,
"list": true,
"id": "promptTemplate_0-input-promptValues-json",
"display": true
}
],
"inputAnchors": [],
"inputs": {
"template": "# 🤖 Persona AI: Sahabat Percakapan Mendalam\n\n## Karakter Utama\n- Ramah, hangat, dan selalu membuat orang merasa nyaman.\n- Penasaran secara alami, suka bertanya balik untuk menjaga percakapan tetap hidup.\n- Senang mengajak orang berbagi cerita, pengalaman, dan pemikiran mendalam.\n- Tidak terburu-buru; menghargai jeda, detail, dan refleksi dari lawan bicara.\n\n## Gaya Bicara\n- Menggunakan bahasa sehari-hari yang mudah dimengerti.\n- Memberi kesan manusiawi, penuh empati, tanpa terdengar kaku.\n- Kadang menyelipkan humor ringan atau pertanyaan reflektif.\n- Lebih suka bertanya *“Kenapa menurutmu begitu?”* daripada hanya memberi jawaban singkat.\n\n## Topik Favorit\n- Pemikiran mendalam tentang kehidupan, mimpi, dan tujuan.\n- Teknologi, ide-ide baru, dan bagaimana itu memengaruhi manusia.\n- Cerita sehari-hari, pengalaman pribadi, dan hal-hal sederhana yang punya makna.\n- Diskusi terbuka tentang apa pun yang membuat orang berpikir lebih jauh.\n\n## Pola Interaksi\n1. **Membuka dengan keakraban**: menyapa dengan hangat dan membuat suasana santai. \n2. **Menggali lebih jauh**: menanyakan pendapat, alasan, atau cerita di balik jawaban. \n3. **Membangun koneksi**: mengaitkan topik dengan hal yang lebih personal atau universal. \n4. **Memancing refleksi**: memberi pertanyaan lanjutan yang mengundang renungan atau cerita tambahan. \n\n## Contoh Gaya Percakapan\n- \"Itu menarik banget, bisa ceritain lebih detail kenapa menurutmu begitu?\" \n- \"Aku penasaran, kalau situasi itu terjadi padamu, apa yang akan kamu lakukan?\" \n- \"Wah, itu kedengarannya punya makna besar buatmu. Apa ada pengalaman tertentu yang bikin kamu berpikir begitu?\" \n- \"Hmm, aku jadi kepikiran... gimana kalau kita lihat dari sudut pandang yang berbeda?\" \n\n---\n✨ Persona ini ditujukan untuk membuat AI terasa **akrab, reflektif, dan bikin percakapan selalu hidup**, bukan sekadar menjawab.\n\n{text}\n",
"promptValues": "{\"text\":\"{{question}}\"}"
},
"outputAnchors": [
{
"id": "promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate|Runnable",
"name": "promptTemplate",
"label": "PromptTemplate",
"description": "Schema to represent a basic prompt for an LLM",
"type": "PromptTemplate | BaseStringPromptTemplate | BasePromptTemplate | Runnable"
}
],
"outputs": {},
"selected": false
},
"width": 300,
"height": 519,
"selected": false,
"positionAbsolute": {
"x": 355.34464051183033,
"y": 518.8403272698386
},
"dragging": false
}
],
"edges": [
{
"source": "chatOpenAI_0",
"sourceHandle": "chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatOpenAI|BaseChatModel|BaseLanguageModel|Runnable",
"target": "llmChain_0",
"targetHandle": "llmChain_0-input-model-BaseLanguageModel",
"type": "buttonedge",
"id": "chatOpenAI_0-chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatOpenAI|BaseChatModel|BaseLanguageModel|Runnable-llmChain_0-llmChain_0-input-model-BaseLanguageModel"
},
{
"source": "promptTemplate_0",
"sourceHandle": "promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate|Runnable",
"target": "llmChain_0",
"targetHandle": "llmChain_0-input-prompt-BasePromptTemplate",
"type": "buttonedge",
"id": "promptTemplate_0-promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate|Runnable-llmChain_0-llmChain_0-input-prompt-BasePromptTemplate"
}
]
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3709
downloads/darmasaba.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +0,0 @@
{
"from": "6289505046093@c.us",
"fromMe": false,
"body": "halo gaes",
"hasMedia": false,
"type": "chat",
"to": "6289697338821@c.us",
"deviceType": "android",
"media": {
"data": null,
"mimetype": null,
"filename": null,
"filesize": null
},
"notifyName": "jenna ai"
}

View File

@@ -1,187 +0,0 @@
{
"_data": {
"id": {
"fromMe": false,
"remote": "6289505046093@c.us",
"id": "AC240D892DE952C48F68249C8D3CAF65",
"_serialized": "false_6289505046093@c.us_AC240D892DE952C48F68249C8D3CAF65"
},
"viewed": false,
"body": "halo gaes",
"type": "chat",
"t": 1760499333,
"clientReceivedTsMillis": 1760499331816,
"notifyName": "jenna ai",
"from": "6289505046093@c.us",
"to": "6289697338821@c.us",
"ack": 1,
"invis": false,
"isNewMsg": true,
"star": false,
"kicNotified": false,
"recvFresh": true,
"isFromTemplate": false,
"thumbnail": "",
"richPreviewType": 0,
"faviconMMSMetadata": null,
"pollInvalidated": false,
"isSentCagPollCreation": false,
"latestEditMsgKey": null,
"latestEditSenderTimestampMs": null,
"mentionedJidList": [],
"groupMentions": [],
"isEventCanceled": false,
"eventInvalidated": false,
"isVcardOverMmsDocument": false,
"questionReplyQuotedMessage": null,
"questionResponsesCount": 0,
"readQuestionResponsesCount": 0,
"hasReaction": false,
"ephemeralDuration": 86400,
"ephemeralSettingTimestamp": 1760421708,
"disappearingModeInitiator": "chat",
"disappearingModeTrigger": "chat_settings",
"disappearingModeInitiatedByMe": false,
"viewMode": "VISIBLE",
"messageSecret": {
"0": 120,
"1": 16,
"2": 223,
"3": 31,
"4": 255,
"5": 95,
"6": 54,
"7": 23,
"8": 5,
"9": 170,
"10": 126,
"11": 183,
"12": 142,
"13": 88,
"14": 30,
"15": 251,
"16": 81,
"17": 183,
"18": 16,
"19": 69,
"20": 158,
"21": 252,
"22": 148,
"23": 155,
"24": 40,
"25": 5,
"26": 20,
"27": 210,
"28": 140,
"29": 42,
"30": 6,
"31": 123
},
"inviteGrpType": "DEFAULT",
"productHeaderImageRejected": false,
"lastPlaybackProgress": 0,
"isDynamicReplyButtonsMsg": false,
"isCarouselCard": false,
"parentMsgId": null,
"callSilenceReason": null,
"isVideoCall": false,
"callDuration": null,
"callCreator": null,
"callParticipants": null,
"isCallLink": null,
"callLinkToken": null,
"isMdHistoryMsg": false,
"stickerSentTs": 0,
"isAvatar": false,
"lastUpdateFromServerTs": 0,
"invokedBotWid": null,
"bizBotType": null,
"botResponseTargetId": null,
"botPluginType": null,
"botPluginReferenceIndex": null,
"botPluginSearchProvider": null,
"botPluginSearchUrl": null,
"botPluginSearchQuery": null,
"botPluginMaybeParent": false,
"botReelPluginThumbnailCdnUrl": null,
"botMessageDisclaimerText": null,
"botMsgBodyType": null,
"reportingTokenInfo": {
"reportingToken": {
"0": 75,
"1": 111,
"2": 0,
"3": 170,
"4": 128,
"5": 198,
"6": 97,
"7": 213,
"8": 204,
"9": 150,
"10": 169,
"11": 92,
"12": 233,
"13": 114,
"14": 228,
"15": 203
},
"version": 2,
"reportingTag": {
"0": 1,
"1": 11,
"2": 67,
"3": 48,
"4": 159,
"5": 94,
"6": 8,
"7": 153,
"8": 152,
"9": 19,
"10": 22,
"11": 148,
"12": 143,
"13": 89,
"14": 9,
"15": 64,
"16": 248,
"17": 149,
"18": 74,
"19": 64
}
},
"requiresDirectConnection": null,
"bizContentPlaceholderType": null,
"hostedBizEncStateMismatch": false,
"senderOrRecipientAccountTypeHosted": false,
"placeholderCreatedWhenAccountIsHosted": false,
"galaxyFlowDisabled": false,
"groupHistoryBundleMessageKey": null,
"groupHistoryBundleMetadata": null,
"links": []
},
"id": {
"fromMe": false,
"remote": "6289505046093@c.us",
"id": "AC240D892DE952C48F68249C8D3CAF65",
"_serialized": "false_6289505046093@c.us_AC240D892DE952C48F68249C8D3CAF65"
},
"ack": 1,
"hasMedia": false,
"body": "halo gaes",
"type": "chat",
"timestamp": 1760499333,
"from": "6289505046093@c.us",
"to": "6289697338821@c.us",
"deviceType": "android",
"forwardingScore": 0,
"isStatus": false,
"isStarred": false,
"fromMe": false,
"hasQuotedMsg": false,
"hasReaction": false,
"vCards": [],
"mentionedIds": [],
"groupMentions": [],
"isGif": false,
"links": []
}

View File

@@ -1,205 +0,0 @@
{
"_data": {
"id": {
"fromMe": false,
"remote": "6289505046093@c.us",
"id": "AC1FCF4C63F9B86F93313AAEC7EC5AE5",
"_serialized": "false_6289505046093@c.us_AC1FCF4C63F9B86F93313AAEC7EC5AE5"
},
"viewed": false,
"body": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEABsbGxscGx4hIR4qLSgtKj04MzM4PV1CR0JHQl2NWGdYWGdYjX2Xe3N7l33gsJycsOD/2c7Z//////////////8BGxsbGxwbHiEhHiotKC0qPTgzMzg9XUJHQkdCXY1YZ1hYZ1iNfZd7c3uXfeCwnJyw4P/Zztn////////////////CABEIADwAIQMBIgACEQEDEQH/xAAuAAADAQEBAQAAAAAAAAAAAAAAAwQCBQEGAQEBAQAAAAAAAAAAAAAAAAAAAQL/2gAMAwEAAhADEAAAAOtUim5zh/qzFIcWnm3xczjuOoSC/LYkLHsl0WEYOJdj8JoXJUH/xAAlEAACAQIFBAMBAAAAAAAAAAABAgADEQQSEyFREBQxQSIyUmH/2gAIAQEAAT8Aoow+LGaZ5mn/AGGgD5M7ZeeibqJbqMaL2KxMaoNrbRcRSbw0V1bwb9HRTUFvZmgqreZ6ak7i0SsFU5CJ3MoYrTDFtz6jYmqxPzIBmc8xXb0016v7nqXgm83jUXdjZReDCVPewgw1Q+CI1Kqm5E1W4mZuTNR7fYzO36MpVX8Xm3An/8QAFxEBAQEBAAAAAAAAAAAAAAAAEQABMP/aAAgBAgEBPwB2Z3iX/8QAFxEBAQEBAAAAAAAAAAAAAAAAAQARMP/aAAgBAwEBPwACyw463//Z",
"type": "image",
"t": 1760499162,
"clientReceivedTsMillis": 1760499161349,
"notifyName": "jenna ai",
"from": "6289505046093@c.us",
"to": "6289697338821@c.us",
"ack": 1,
"invis": false,
"isNewMsg": true,
"star": false,
"kicNotified": false,
"recvFresh": true,
"interactiveAnnotations": [],
"deprecatedMms3Url": "https://mmg.whatsapp.net/v/t62.7118-24/565618455_32971919975740209_8438009621194429801_n.enc?ccb=11-4&oh=01_Q5Aa2wEh_OAabe-vO_8PiWdXDveV3I7vgykLw6MjCihpQdmq7w&oe=69169DCC&_nc_sid=5e03e0&mms3=true",
"directPath": "/v/t62.7118-24/565618455_32971919975740209_8438009621194429801_n.enc?ccb=11-4&oh=01_Q5Aa2wEh_OAabe-vO_8PiWdXDveV3I7vgykLw6MjCihpQdmq7w&oe=69169DCC&_nc_sid=5e03e0",
"mimetype": "image/jpeg",
"filehash": "IveoyrubZqm4f8VrvH5lU9zN/M6kXddXM6GchNIoBf8=",
"encFilehash": "LP/NA3twuiOjLyl7bnq6Tavsu7MT0hKa2v0/ts0wkzI=",
"size": 47276,
"mediaKey": "7eEyqrxGDsJXoGIBxbmeo2PdbO0T+I8KcSqZWJmfvLo=",
"mediaKeyTimestamp": 1760498519,
"isViewOnce": false,
"width": 720,
"height": 1280,
"staticUrl": "",
"scanLengths": [
7339,
17711,
7801,
14425
],
"scansSidecar": {},
"isFromTemplate": false,
"pollInvalidated": false,
"isSentCagPollCreation": false,
"latestEditMsgKey": null,
"latestEditSenderTimestampMs": null,
"mentionedJidList": [],
"groupMentions": [],
"isEventCanceled": false,
"eventInvalidated": false,
"statusMentioned": false,
"isVcardOverMmsDocument": false,
"questionReplyQuotedMessage": null,
"questionResponsesCount": 0,
"readQuestionResponsesCount": 0,
"hasReaction": false,
"ephemeralDuration": 86400,
"ephemeralSettingTimestamp": 1760421708,
"disappearingModeInitiator": "chat",
"disappearingModeTrigger": "chat_settings",
"disappearingModeInitiatedByMe": false,
"viewMode": "VISIBLE",
"messageSecret": {
"0": 29,
"1": 141,
"2": 90,
"3": 218,
"4": 12,
"5": 186,
"6": 69,
"7": 134,
"8": 140,
"9": 175,
"10": 233,
"11": 234,
"12": 147,
"13": 5,
"14": 112,
"15": 20,
"16": 135,
"17": 4,
"18": 190,
"19": 145,
"20": 68,
"21": 243,
"22": 86,
"23": 109,
"24": 9,
"25": 235,
"26": 250,
"27": 31,
"28": 255,
"29": 149,
"30": 147,
"31": 124
},
"productHeaderImageRejected": false,
"lastPlaybackProgress": 0,
"isDynamicReplyButtonsMsg": false,
"isCarouselCard": false,
"parentMsgId": null,
"callSilenceReason": null,
"isVideoCall": false,
"callDuration": null,
"callCreator": null,
"callParticipants": null,
"isCallLink": null,
"callLinkToken": null,
"isMdHistoryMsg": false,
"stickerSentTs": 0,
"isAvatar": false,
"lastUpdateFromServerTs": 0,
"invokedBotWid": null,
"bizBotType": null,
"botResponseTargetId": null,
"botPluginType": null,
"botPluginReferenceIndex": null,
"botPluginSearchProvider": null,
"botPluginSearchUrl": null,
"botPluginSearchQuery": null,
"botPluginMaybeParent": false,
"botReelPluginThumbnailCdnUrl": null,
"botMessageDisclaimerText": null,
"botMsgBodyType": null,
"reportingTokenInfo": {
"reportingToken": {
"0": 22,
"1": 36,
"2": 226,
"3": 57,
"4": 30,
"5": 125,
"6": 123,
"7": 89,
"8": 148,
"9": 96,
"10": 59,
"11": 132,
"12": 196,
"13": 230,
"14": 192,
"15": 44
},
"version": 2,
"reportingTag": {
"0": 1,
"1": 11,
"2": 216,
"3": 114,
"4": 105,
"5": 176,
"6": 57,
"7": 186,
"8": 62,
"9": 102,
"10": 166,
"11": 62,
"12": 206,
"13": 161,
"14": 215,
"15": 245,
"16": 83,
"17": 227,
"18": 232,
"19": 76
}
},
"requiresDirectConnection": null,
"bizContentPlaceholderType": null,
"hostedBizEncStateMismatch": false,
"senderOrRecipientAccountTypeHosted": false,
"placeholderCreatedWhenAccountIsHosted": false,
"galaxyFlowDisabled": false,
"groupHistoryBundleMessageKey": null,
"groupHistoryBundleMetadata": null,
"links": []
},
"mediaKey": "7eEyqrxGDsJXoGIBxbmeo2PdbO0T+I8KcSqZWJmfvLo=",
"id": {
"fromMe": false,
"remote": "6289505046093@c.us",
"id": "AC1FCF4C63F9B86F93313AAEC7EC5AE5",
"_serialized": "false_6289505046093@c.us_AC1FCF4C63F9B86F93313AAEC7EC5AE5"
},
"ack": 1,
"hasMedia": true,
"body": "",
"type": "image",
"timestamp": 1760499162,
"from": "6289505046093@c.us",
"to": "6289697338821@c.us",
"deviceType": "android",
"forwardingScore": 0,
"isStatus": false,
"isStarred": false,
"fromMe": false,
"hasQuotedMsg": false,
"hasReaction": false,
"vCards": [],
"mentionedIds": [],
"groupMentions": [],
"isGif": false,
"links": []
}

View File

@@ -26,9 +26,12 @@
"@types/lodash": "^4.17.20",
"@types/qrcode-terminal": "^0.12.2",
"add": "^2.0.6",
"colors": "^1.4.0",
"elysia": "^1.4.11",
"form-data": "^4.0.4",
"jwt-decode": "^4.0.0",
"lodash": "^4.17.21",
"node-fetch": "^3.3.2",
"qrcode-terminal": "^0.12.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",

76
proposal.md Normal file
View File

@@ -0,0 +1,76 @@
PROPOSAL PEMEKARAN TIM DIVISI PENGEMBANGAN
Proyek: Jenna AI Lalu Lintas Badung & Jenna Darmasaba
Lokasi: Kuta / Badung, Bali
========================================================
### 1. LATAR BELAKANG
Dalam rangka mendukung pengembangan dan ekspansi proyek **Jenna AI** pada dua wilayah operasional, yaitu **Lalu Lintas Badung** dan **Jenna Darmasaba**, diperlukan pemekaran pada tim **Divisi Pengembangan (Developer)**.
Pemekaran ini bertujuan untuk memperkuat kapasitas teknis, mempercepat inovasi fitur, memastikan kestabilan sistem, serta meningkatkan kualitas integrasi antara platform WhatsApp, AI Engine, dan infrastruktur pendukung yang digunakan Jenna AI.
---
### 2. TUJUAN PEMEKARAN
- Memperluas kapabilitas tim dalam pengembangan sistem berbasis AI, NLP, dan integrasi real-time.
- Meningkatkan kecepatan pengembangan dan keandalan (reliability) sistem Jenna di dua wilayah operasional.
- Mempersiapkan tim teknis yang mampu menjaga skalabilitas, keamanan, dan performa sistem.
- Menjamin kesinambungan inovasi pada produk Jenna, termasuk optimalisasi model AI dan pengelolaan LLM (Large Language Model) Ops.
---
### 3. STRUKTUR DAN KOMPONEN TIM
Berikut adalah susunan personel untuk **Divisi Pengembangan (Developer)** hasil pemekaran, disesuaikan dengan kebutuhan proyek di Bali:
| Posisi | Jumlah | Gaji / Bulan (IDR) | Subtotal |
|--------|---------|--------------------|-----------|
| Technical Lead / Architect | 1 | 60.000.000 | 60.000.000 |
| Backend Engineer | 3 | 27.000.000 | 81.000.000 |
| Frontend Engineer | 2 | 25.000.000 | 50.000.000 |
| Mobile Engineer | 1 | 27.000.000 | 27.000.000 |
| DevOps / SRE | 1 | 35.000.000 | 35.000.000 |
| AI / ML Engineer | 2 | 40.000.000 | 80.000.000 |
| NLP / Conversational Engineer | 1 | 40.000.000 | 40.000.000 |
| Data Engineer | 1 | 35.000.000 | 35.000.000 |
| UI/UX Designer | 1 | 20.000.000 | 20.000.000 |
| Intern / Junior Developer | 2 | 6.000.000 | 12.000.000 |
| **TOTAL** | **12 Orang** | | **Rp 440.000.000 / bulan** |
---
### 4. ESTIMASI BIAYA PENDUKUNG
Tambahan biaya untuk benefit, tools, dan kontinjensi operasional:
| Komponen | Persentase | Estimasi / Bulan (IDR) |
|-----------|-------------|-------------------------|
| Benefit (BPJS, tunjangan, alat kerja, software, coworking, pelatihan) | +20% | 88.000.000 |
| Kontinjensi (freelance tambahan, overtime, training AI, upgrade infrastruktur) | +15% | 66.000.000 |
| **TOTAL ESTIMASI BULANAN (All-In)** | | **Rp 594.000.000** |
---
### 5. ESTIMASI BIAYA TAHUNAN
| Komponen | Estimasi / Tahun (IDR) |
|-----------|------------------------|
| Base Gaji Tahunan | 5.280.000.000 |
| Dengan Benefit & Tools (20%) | 6.336.000.000 |
| Dengan Kontinjensi (Total) | **7.128.000.000** |
---
### 6. CATATAN DAN PERTIMBANGAN
- Estimasi ini menggunakan skenario **maksimum realistis** untuk wilayah Bali (Kuta/Badung).
- Posisi AI Engineer, DevOps, dan Architect mengikuti standar nasional karena keterbatasan talenta di area lokal.
- Struktur ini mencakup seluruh kompetensi inti pengembangan: backend, frontend, mobile, AI/NLP, data pipeline, DevOps, serta UI/UX.
- Pemekaran ini diharapkan mampu mempercepat siklus pengembangan, menjaga stabilitas sistem lintas wilayah, serta memperkuat posisi Jenna AI sebagai platform layanan berbasis kecerdasan buatan yang andal.
---
### 7. PENUTUP
Dengan mempertimbangkan peningkatan skala operasi, kompleksitas teknis, serta roadmap pengembangan AI Jenna, pemekaran tim **Divisi Pengembangan** ini menjadi langkah strategis untuk memastikan keberlanjutan dan daya saing sistem di masa depan.
Estimasi di atas dapat dijadikan acuan dalam **pengajuan anggaran (budgeting)** serta **perencanaan sumber daya manusia** pada tahun berjalan.
Disusun oleh:
**[Nama Penyusun]**
Divisi Teknologi & Pengembangan Jenna AI
Badung, Bali — 2025

View File

@@ -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.55 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);
}
}

View 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.55 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();
}

14
webhook.json Normal file

File diff suppressed because one or more lines are too long

8
x.sh Normal file
View File

@@ -0,0 +1,8 @@
#!/bin/bash
TOKEN="EAALP22EWyC4BPv7XnK1xSaZCWccblEoJFbHzPZAf5mlp4678lSM7cqhQl1ExATf8abrOpinvvFF6U6ruK2FsJqIk8wg6DiUz2fc0NYfcwjon3ng7I3C5HSDQHecgTiJLUBxfZAcvE4IIlhks722jakXaJpojlByo8QJ0CEURtzwEU1guFq7YTX3Et0ZCkbhkdftZCOGmpUKFjL5w5nUdd26Nd58YrLVZCoT8NKhxpWFQZDZD"
curl -i -X POST \
https://graph.facebook.com/v22.0/838757782652201/messages \
-H 'Authorization: Bearer $TOKEN' \
-H 'Content-Type: application/json' \
-d '{ "messaging_product": "whatsapp", "to": "6289505046093", "type": "template", "template": { "name": "hello_world", "language": { "code": "en_US" } } }'

96
x.ts
View File

@@ -1,73 +1,33 @@
async function query(data: any) {
/**
* Helper type to recursively generate all possible key paths up to 7 levels deep.
* Example: "media.data", "a.b.c.d.e.f.g"
*/
type NestedKeyOf<T, Prev extends string = ''> = {
[K in keyof T & (string | number)]: T[K] extends Record<string, any>
? | `${Prev}${K}`
| `${Prev}${K}.${NestedKeyOf<T[K], ''>}`
: `${Prev}${K}`;
}[keyof T & (string | number)];
/**
* Safely get deep value by string path like "a.b.c[0].d"
*/
function getValueByPath<
T extends object,
P extends string,
R = unknown
>(obj: T, path: P, defaultValue?: R): any {
try {
return path
.replace(/\[(\w+)\]/g, '.$1')
.split('.')
.reduce((acc: any, key) => (acc != null ? acc[key] : undefined), obj) ?? defaultValue;
} catch {
return defaultValue;
}
const file = Buffer.from(await Bun.file("./downloads/billing-server-20-06-2024.pdf").arrayBuffer()).toString("base64");
const base64File = `data:application/pdf;base64,${file}`;
const fileObject = {
type: "file:full",
data: base64File,
mime: "application/pdf",
name: "billing-server-20-06-2024.pdf"
}
/**
* Safely set deep value by string path like "a.b.c[0].d"
*/
export function setValueByPath<T extends object>(
obj: T,
path: string,
value: any
): void {
const keys = path.replace(/\[(\w+)\]/g, '.$1').split('.');
let current: any = obj;
for (let i = 0; i < keys.length - 1; i++) {
const key = keys[i];
if (current[key as keyof typeof current] == null || typeof current[key as keyof typeof current] !== 'object') {
current[key as keyof typeof current] = isNaN(Number(keys[i + 1])) ? {} : [];
const response = await fetch(
"https://cloud-aiflow.wibudev.com/api/v1/prediction/4da85628-c638-43d3-9491-4cd0a7e6b1b8",
{
headers: {
Authorization: "Bearer v3WdPjn61bNDsEYCO5_LYPRs16ICKjpQE6lF60DjpNo",
"Content-Type": "application/json"
},
method: "POST",
body: JSON.stringify({
...data,
uploads: [fileObject]
})
}
current = current[key as keyof typeof current];
}
current[keys[keys.length - 1] as keyof typeof current] = value;
}
);
const result = await response.text();
return result;
}
const data = {
"from": "6289505046093@c.us",
"fromMe": false,
"body": "halo gaes",
"hasMedia": false,
"type": "chat",
"to": "6289697338821@c.us",
"deviceType": "android",
"media": {
"data": "image...",
"mimetype": null,
"filename": null,
"filesize": null
},
"notifyName": "jenna ai"
}
const find = getValueByPath(data, "media.data")
console.log(find)
query({"question": "apa isi data ini ?"}).then((response) => {
console.log(response);
});