diff --git a/deploy.sh b/deploy.sh old mode 100644 new mode 100755 diff --git a/package-lock.json b/package-lock.json index caa484c..eaef611 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "node-gyp": "^12.2.0", "node-webpmux": "^3.2.1", "qrcode-terminal": "^0.12.0", + "wa-sticker-formatter": "^4.4.4", "whatsapp-web.js": "^1.24.0" } }, @@ -126,6 +127,12 @@ "node": ">=18" } }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" + }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", @@ -161,6 +168,18 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -293,6 +312,15 @@ "license": "MIT", "optional": true }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, "node_modules/b4a": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz", @@ -419,8 +447,7 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/basic-ftp": { "version": "5.2.0", @@ -460,7 +487,6 @@ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "license": "MIT", - "optional": true, "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -504,7 +530,6 @@ } ], "license": "MIT", - "optional": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -680,6 +705,19 @@ "node": ">=12" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -698,6 +736,16 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "node_modules/compress-commons": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", @@ -807,6 +855,30 @@ } } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/degenerator": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", @@ -821,6 +893,15 @@ "node": ">= 14" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/devtools-protocol": { "version": "0.0.1581282", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1581282.tgz", @@ -964,6 +1045,24 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/events-universal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", @@ -973,6 +1072,15 @@ "bare-events": "^2.7.0" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, "node_modules/exponential-backoff": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", @@ -1031,6 +1139,23 @@ } } }, + "node_modules/file-type": { + "version": "16.5.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", + "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", + "license": "MIT", + "dependencies": { + "readable-web-to-node-stream": "^3.0.0", + "strtok3": "^6.2.4", + "token-types": "^4.1.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/fluent-ffmpeg": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.3.tgz", @@ -1050,19 +1175,37 @@ "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==" }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "license": "MIT", - "optional": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -1146,6 +1289,12 @@ "node": ">= 14" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -1241,8 +1390,22 @@ "url": "https://feross.org/support" } ], - "license": "BSD-3-Clause", - "optional": true + "license": "BSD-3-Clause" + }, + "node_modules/image-size": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", + "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", + "license": "MIT", + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } }, "node_modules/import-fresh": { "version": "3.3.1", @@ -1285,8 +1448,13 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC", - "optional": true + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" }, "node_modules/ip-address": { "version": "10.1.0", @@ -1354,7 +1522,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "license": "MIT", - "optional": true, "dependencies": { "universalify": "^2.0.0" }, @@ -1499,6 +1666,18 @@ "node": ">=10.0.0" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -1517,7 +1696,6 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "license": "MIT", - "optional": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -1663,12 +1841,24 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, "node_modules/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", @@ -1687,6 +1877,18 @@ "node": ">= 0.4.0" } }, + "node_modules/node-abi": { + "version": "3.88.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.88.0.tgz", + "integrity": "sha512-At6b4UqIEVudaqPsXjmUO1r/N5BUr4yhDGs5PkBE8/oG5+TfLPhFechiskFsnT6Ql0VfUXbalUUCbfXxtj7K+w==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-addon-api": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", @@ -1910,6 +2112,19 @@ "node": "20 || >=22" } }, + "node_modules/peek-readable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", + "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -1934,6 +2149,51 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prebuild-install/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/prebuild-install/node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, "node_modules/proc-log": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", @@ -1943,6 +2203,15 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -2041,12 +2310,35 @@ "qrcode-terminal": "bin/qrcode-terminal.js" } }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "license": "MIT", + "dependencies": { + "inherits": "~2.0.3" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", - "optional": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -2056,6 +2348,62 @@ "node": ">= 6" } }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz", + "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^4.7.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/readable-web-to-node-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/readable-web-to-node-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/readdir-glob": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", @@ -2148,8 +2496,7 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -2177,6 +2524,113 @@ "license": "MIT", "optional": true }, + "node_modules/sharp": { + "version": "0.30.7", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.30.7.tgz", + "integrity": "sha512-G+MY2YW33jgflKPTXXptVO28HvNOo9G3j0MybYAHeEmby+QuD2U98dT6ueht9cv/XDqZspSpIhoSW+BAKJ7Hig==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.1", + "node-addon-api": "^5.0.0", + "prebuild-install": "^7.1.1", + "semver": "^7.3.7", + "simple-get": "^4.0.1", + "tar-fs": "^2.1.1", + "tunnel-agent": "^0.6.0" + }, + "engines": { + "node": ">=12.13.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/sharp/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/sharp/node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", + "license": "MIT" + }, + "node_modules/sharp/node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "license": "MIT" + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -2253,7 +2707,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "license": "MIT", - "optional": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -2284,6 +2737,32 @@ "node": ">=8" } }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strtok3": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", + "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^4.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/tar": { "version": "7.5.11", "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.11.tgz", @@ -2331,7 +2810,6 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "license": "MIT", - "optional": true, "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", @@ -2377,6 +2855,23 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/token-types": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", + "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -2399,6 +2894,18 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/typed-query-selector": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.1.tgz", @@ -2441,7 +2948,6 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "license": "MIT", - "optional": true, "engines": { "node": ">= 10.0.0" } @@ -2502,8 +3008,22 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/wa-sticker-formatter": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/wa-sticker-formatter/-/wa-sticker-formatter-4.4.4.tgz", + "integrity": "sha512-tl2tXlu7HEs630+1LcJsdavNGEysyQV7EIe/tFsPJCEd2jteG0nGouWNUglUEjW+bDCZGXw9VpJgUtwVGIKr8w==", "license": "MIT", - "optional": true + "dependencies": { + "axios": "^0.21.1", + "file-type": "^16.5.3", + "fluent-ffmpeg": "^2.1.2", + "fs-extra": "^10.0.0", + "image-size": "^1.0.0", + "node-webpmux": "^3.1.0", + "sharp": "^0.30.0" + } }, "node_modules/webdriver-bidi-protocol": { "version": "0.4.1", diff --git a/package.json b/package.json index 28dcaca..5de4074 100755 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "node-gyp": "^12.2.0", "node-webpmux": "^3.2.1", "qrcode-terminal": "^0.12.0", + "wa-sticker-formatter": "^4.4.4", "whatsapp-web.js": "^1.24.0" } } diff --git a/src/client/whatsappClient.js b/src/client/whatsappClient.js index b234d53..d72d472 100644 --- a/src/client/whatsappClient.js +++ b/src/client/whatsappClient.js @@ -6,49 +6,97 @@ import os from "os"; export const { Client, LocalAuth, MessageMedia } = pkg; -// detecta termux, e usa o executável do chromium do sistema em vez do puppeteer +// ── Logger ────────────────────────────────────────────────── +const c = { + reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m", + green: "\x1b[32m", yellow: "\x1b[33m", cyan: "\x1b[36m", + red: "\x1b[31m", gray: "\x1b[90m", white: "\x1b[37m", + blue: "\x1b[34m", magenta: "\x1b[35m", +}; + +const now = () => + new Date().toLocaleString("pt-BR", { dateStyle: "short", timeStyle: "short" }); + +export const logger = { + info: (...a) => console.log(`${c.gray}${now()}${c.reset} ℹ️ `, ...a), + success: (...a) => console.log(`${c.gray}${now()}${c.reset} ${c.green}✅${c.reset}`, ...a), + warn: (...a) => console.log(`${c.gray}${now()}${c.reset} ${c.yellow}⚠️ ${c.reset}`, ...a), + error: (...a) => console.log(`${c.gray}${now()}${c.reset} ${c.red}❌${c.reset}`, ...a), + bot: (...a) => console.log(`${c.gray}${now()}${c.reset} ${c.magenta}🤖${c.reset}`, ...a), +}; + +// ── Banner ─────────────────────────────────────────────────── +function printBanner() { + console.log(`${c.blue}${c.bold}`); + console.log(` _____ _____ _ `); + console.log(`| |___ ___ _ _| __ |___| |_ `); + console.log(`| | | | .'| | | | __ -| . | _|`); + console.log(`|_|_|_|__,|_|_|_ |_____|___|_| `); + console.log(` |___| `); + console.log(); + console.log(` website : ${c.reset}${c.cyan}www.mlplovers.com.br/manybot${c.reset}`); + console.log(` repo : ${c.reset}${c.cyan}github.com/synt-xerror/manybot${c.reset}`); + console.log(); + console.log(` ${c.bold}✨ A Amizade é Mágica!${c.reset}`); + console.log(); +} + +// ── Ambiente ───────────────────────────────────────────────── const isTermux = (os.platform() === "linux" || os.platform() === "android") && process.env.PREFIX?.startsWith("/data/data/com.termux"); +logger.info(isTermux + ? `Ambiente: ${c.yellow}${c.bold}Termux${c.reset} — usando Chromium do sistema` + : `Ambiente: ${c.blue}${c.bold}${os.platform()}${c.reset} — usando Puppeteer padrão` +); + const puppeteerConfig = isTermux ? { - executablePath: "/data/data/com.termux/files/usr/bin/chromium-browser", - args: [ - "--headless=new", - "--no-sandbox", - "--disable-setuid-sandbox", - "--disable-dev-shm-usage", - "--disable-gpu", - "--single-process", - "--no-zygote", - "--disable-software-rasterizer" - ] + executablePath: "/data/data/com.termux/files/usr/bin/chromium-browser", + args: [ + "--headless=new", "--no-sandbox", "--disable-setuid-sandbox", + "--disable-dev-shm-usage", "--disable-gpu", "--single-process", + "--no-zygote", "--disable-software-rasterizer", + ], } : {}; +// ── Cliente ────────────────────────────────────────────────── export const client = new Client({ - authStrategy: new LocalAuth({ clientId: CLIENT_ID }), - puppeteer: { - headless: true, - ...puppeteerConfig - } + authStrategy: new LocalAuth({ clientId: CLIENT_ID }), + puppeteer: { headless: true, ...puppeteerConfig }, }); -client.on("qr", qr => { - console.log("[BOT] Escaneie o QR Code"); - console.log(qr); +client.on("qr", async qr => { + if (isTermux) { + try { + await QRCode.toFile(QR_PATH, qr, { width: 400 }); + logger.bot(`QR Code salvo em: ${c.cyan}${c.bold}${QR_PATH}${c.reset}`); + logger.bot(`Abra com: ${c.yellow}termux-open qr.png${c.reset}`); + } catch (err) { + logger.error("Falha ao salvar QR Code:", err.message); + } + } else { + logger.bot(`Escaneie o ${c.yellow}${c.bold}QR Code${c.reset} abaixo:`); qrcode.generate(qr, { small: true }); + } }); client.on("ready", () => { - exec("clear"); - console.log("[BOT] WhatsApp conectado."); + exec("clear"); + printBanner(); + logger.success(`${c.green}${c.bold}WhatsApp conectado e pronto!${c.reset}`); + logger.info(`Client ID: ${c.cyan}${CLIENT_ID}${c.reset}`); }); client.on("disconnected", reason => { - console.warn("[BOT] Reconectando:", reason); - setTimeout(() => client.initialize(), 5000); + logger.warn(`Desconectado — motivo: ${c.yellow}${reason}${c.reset}`); + logger.info(`Reconectando em ${c.cyan}5s${c.reset}...`); + setTimeout(() => { + logger.bot("Reinicializando cliente..."); + client.initialize(); + }, 5000); }); export default client; \ No newline at end of file diff --git a/src/commands/figurinha.js b/src/commands/figurinha.js index 691994f..fc75510 100644 --- a/src/commands/figurinha.js +++ b/src/commands/figurinha.js @@ -1,97 +1,256 @@ -import pkg from "whatsapp-web.js"; -const { MessageMedia } = pkg; - import fs from "fs"; +import path from "path"; import os from "os"; import { execFile } from "child_process"; import { promisify } from "util"; -import webpmux from "node-webpmux"; -import { botMsg } from "../utils/botMsg.js"; +import pkg from "whatsapp-web.js"; +import { createSticker } from "wa-sticker-formatter"; + import { client } from "../client/whatsappClient.js"; +import { botMsg } from "../utils/botMsg.js"; +import { emptyFolder } from "../utils/file.js"; +import { stickerSessions } from "./index.js"; -const exec = promisify(execFile); +const { MessageMedia } = pkg; +const execFileAsync = promisify(execFile); -export async function gerarSticker(msg) { +const DOWNLOADS_DIR = path.resolve("downloads"); +const FFMPEG = os.platform() === "win32" + ? ".\\bin\\ffmpeg.exe" + : "./bin/ffmpeg"; - if (!msg.hasMedia) { - await msg.reply(botMsg("Envie uma imagem junto com o comando: `!figurinha`.")); - return; +const MAX_STICKER_SIZE = 900 * 1024; +const SESSION_TIMEOUT = 2 * 60 * 1000; +const MAX_MEDIA = 10; + +// ───────────────── Helpers ───────────────── + +function ensureDownloadsDir() { + if (!fs.existsSync(DOWNLOADS_DIR)) { + fs.mkdirSync(DOWNLOADS_DIR, { recursive: true }); + } +} + +function cleanupFiles(...files) { + for (const f of files) { + if (f && fs.existsSync(f)) fs.unlinkSync(f); + } +} + +// Converte vídeo/gif → GIF 512x512 com paleta preservada +async function convertVideoToGif(inputPath, outputPath, fps = 12) { + + const clampedFps = Math.min(fps, 12); + + const filter = [ + `fps=${clampedFps},scale=512:512:flags=lanczos,split[s0][s1]`, + `[s0]palettegen=max_colors=256:reserve_transparent=1[p]`, + `[s1][p]paletteuse=dither=bayer` + ].join(";"); + + await execFileAsync(FFMPEG, [ + "-i", inputPath, + "-filter_complex", filter, // <-- era -vf, tem que ser -filter_complex pro split funcionar + "-loop", "0", + "-y", + outputPath + ]); +} + +// Força imagem estática para 512x512 +async function resizeToSticker(inputPath, outputPath) { + + await execFileAsync(FFMPEG, [ + "-i", inputPath, + "-vf", "scale=512:512:flags=lanczos", // lanczos = melhor qualidade no resize + "-y", + outputPath + ]); + +} + +async function createStickerWithFallback(stickerInputPath, isAnimated) { + + const qualities = [80, 60, 40, 20]; + + for (const quality of qualities) { + + const buffer = await createSticker( + fs.readFileSync(stickerInputPath), + { + pack: "Criada por ManyBot\n", + author: "\ngithub.com/synt-xerror/manybot", + type: isAnimated ? "FULL" : "STATIC", + categories: ["🤖"], + quality, + } + ); + + if (buffer.length <= MAX_STICKER_SIZE) { + return buffer; } - const media = await msg.downloadMedia(); - const ext = media.mimetype.split("/")[1]; + } - const input = `downloads/${msg.id._serialized}.${ext}`; - const output = `downloads/${msg.id._serialized}.webp`; + throw new Error("Não foi possível reduzir o sticker para menos de 900 KB."); +} - fs.writeFileSync(input, Buffer.from(media.data, "base64")); +// ───────────────── Sessão ───────────────── - const so = os.platform(); - const cmd = so === "win32" ? ".\\bin\\ffmpeg.exe" : "./bin/ffmpeg"; +export function iniciarSessao(chatId, author) { - await exec(cmd, [ - "-y", - "-i", input, - "-vf", - "scale=512:512:force_original_aspect_ratio=decrease,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=0x00000000", - "-vcodec", "libwebp", - "-lossless", "1", - "-qscale", "75", - "-preset", "default", - "-an", - "-vsync", "0", - output - ]); + if (stickerSessions.has(chatId)) return false; - const img = new webpmux.Image(); - await img.load(output); + const timeout = setTimeout(() => { - const metadata = { - "sticker-pack-id": "manybot", - "sticker-pack-name": "Feito por ManyBot", - "sticker-pack-publisher": "My Little Pony Lovers", - "emojis": ["🤖"] - }; + stickerSessions.delete(chatId); + client.sendMessage(chatId, botMsg("Sessão de figurinha expirou.")); - const json = Buffer.from(JSON.stringify(metadata)); + }, SESSION_TIMEOUT); - const exif = Buffer.concat([ - Buffer.from([ - 0x49,0x49,0x2A,0x00, - 0x08,0x00,0x00,0x00, - 0x01,0x00, - 0x41,0x57, - 0x07,0x00 - ]), - Buffer.from([ - json.length & 0xff, - (json.length >> 8) & 0xff, - (json.length >> 16) & 0xff, - (json.length >> 24) & 0xff - ]), - json - ]); + stickerSessions.set(chatId, { + author, + medias: [], + timeout + }); - img.exif = exif; - await img.save(output); + return true; - const data = fs.readFileSync(output); +} - const sticker = new MessageMedia( - "image/webp", - data.toString("base64"), - "sticker.webp" - ); +// ───────────────── Coleta de mídia ───────────────── - const chat = await msg.getChat(); +export async function coletarMidia(msg) { - await client.sendMessage( - chat.id._serialized, - sticker, - { sendMediaAsSticker: true } - ); + const chat = await msg.getChat(); + const chatId = chat.id._serialized; + + const session = stickerSessions.get(chatId); + if (!session) return; + + const sender = msg.author || msg.from; + if (sender !== session.author) return; + if (!msg.hasMedia) return; + + const media = await msg.downloadMedia(); + if (!media) return; + + const isGif = + media.mimetype === "image/gif" || + (media.mimetype === "video/mp4" && msg._data?.isGif); + + if ( + !media.mimetype || + (!media.mimetype.startsWith("image/") && + !media.mimetype.startsWith("video/") && + !isGif) + ) { + return; + } + + if (session.medias.length < MAX_MEDIA) { + session.medias.push(media); + } + +} + +// ───────────────── Criar stickers ───────────────── + +export async function gerarSticker(msg, chatId) { + + const sender = msg.author || msg.from; + const session = stickerSessions.get(chatId); + + if (!session) { + return msg.reply(botMsg("Nenhuma sessão de figurinha ativa.")); + } + + if (session.author !== sender) { + return msg.reply(botMsg("Apenas quem iniciou a sessão pode criar as figurinhas.")); + } + + const medias = session.medias; + + if (!medias.length) { + return msg.reply(botMsg("Nenhuma imagem recebida.")); + } + + clearTimeout(session.timeout); + + console.log("midias:", medias.length); + + await msg.reply(botMsg("Aguarde! Estou criando as suas figurinhas...")); + + ensureDownloadsDir(); + + for (const media of medias) { + try { + const ext = media.mimetype.split("/")[1]; + const isVideo = media.mimetype.startsWith("video/"); + const isGif = media.mimetype === "image/gif"; + const isAnimated = isVideo || isGif; + + const id = Date.now() + "-" + Math.random().toString(36).slice(2); + const inputPath = path.join(DOWNLOADS_DIR, `${id}.${ext}`); + const gifPath = path.join(DOWNLOADS_DIR, `${id}.gif`); + const resizedPath = path.join(DOWNLOADS_DIR, `${id}-scaled.${ext}`); + + fs.writeFileSync(inputPath, Buffer.from(media.data, "base64")); + + // LOG 1 — arquivo de entrada + const inputSize = fs.statSync(inputPath).size; + console.log(`[1] mimetype: ${media.mimetype} | isAnimated: ${isAnimated} | inputPath: ${inputPath} | size: ${inputSize} bytes`); + + let stickerInputPath = inputPath; + + if (isAnimated) { + console.log("[2] Convertendo para GIF..."); + await convertVideoToGif(inputPath, gifPath, isVideo ? 12 : 24); + + // LOG 2 — gif gerado + if (fs.existsSync(gifPath)) { + console.log(`[2] GIF gerado: ${fs.statSync(gifPath).size} bytes`); + } else { + console.error("[2] ERRO: gifPath não foi criado pelo ffmpeg!"); + } + + stickerInputPath = gifPath; + } else { + console.log("[2] Redimensionando imagem estática..."); + await resizeToSticker(inputPath, resizedPath); + + if (fs.existsSync(resizedPath)) { + console.log(`[2] Resized gerado: ${fs.statSync(resizedPath).size} bytes`); + } else { + console.error("[2] ERRO: resizedPath não foi criado!"); + } + + stickerInputPath = resizedPath; + } + + // LOG 3 — antes de criar o sticker + console.log(`[3] stickerInputPath: ${stickerInputPath} | exists: ${fs.existsSync(stickerInputPath)} | size: ${fs.existsSync(stickerInputPath) ? fs.statSync(stickerInputPath).size : "N/A"} bytes`); + + const stickerBuffer = await createStickerWithFallback(stickerInputPath, isAnimated); + + // LOG 4 — sticker gerado + console.log(`[4] Sticker buffer: ${stickerBuffer.length} bytes`); + + const stickerMedia = new MessageMedia("image/webp", stickerBuffer.toString("base64")); + await client.sendMessage(chatId, stickerMedia, { sendMediaAsSticker: true }); + + cleanupFiles(inputPath, gifPath, resizedPath); + + } catch (err) { + console.error("Erro ao gerar sticker:", err); + await msg.reply(botMsg("Erro ao gerar uma das figurinhas.")); + } + } + + await msg.reply(botMsg("Figurinhas geradas com sucesso!")); + + stickerSessions.delete(chatId); + emptyFolder("downloads"); - fs.unlinkSync(input); - fs.unlinkSync(output); } \ No newline at end of file diff --git a/src/commands/index.js b/src/commands/index.js index a686324..409c860 100644 --- a/src/commands/index.js +++ b/src/commands/index.js @@ -1,71 +1,129 @@ import { enqueueDownload } from "../download/queue.js"; -import { gerarSticker } from "./figurinha.js"; +import { iniciarSessao, gerarSticker } from "./figurinha.js"; import { botMsg } from "../utils/botMsg.js"; -import { iniciarJogo, pararJogo, processarJogo } from "../games/adivinhacao.js"; +import { iniciarJogo, pararJogo } from "../games/adivinhacao.js"; import { processarInfo } from "./info.js"; +export const stickerSessions = new Map(); + +const c = { + reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m", + green: "\x1b[32m", yellow: "\x1b[33m", cyan: "\x1b[36m", + red: "\x1b[31m", gray: "\x1b[90m", +}; + +const now = () => + new Date().toLocaleString("pt-BR", { dateStyle: "short", timeStyle: "medium" }); + +const log = { + cmd: (cmd, ...a) => console.log(`${c.gray}${now()}${c.reset} ${c.cyan}⚙️ ${c.bold}${cmd}${c.reset}`, ...a), + ok: (...a) => console.log(`${c.gray}${now()}${c.reset} ${c.green}✅${c.reset}`, ...a), + warn: (...a) => console.log(`${c.gray}${now()}${c.reset} ${c.yellow}⚠️ ${c.reset}`, ...a), + error: (...a) => console.log(`${c.gray}${now()}${c.reset} ${c.red}❌${c.reset}`, ...a), +}; + export async function processarComando(msg, chat, chatId) { - const tokens = msg.body.trim().split(/\s+/); - try { - switch(tokens[0]) { - case "!many": - await chat.sendMessage(botMsg( - "Comandos:\n\n"+ - "- `!ping`\n"+ - "- `!video `\n"+ - "- `!audio `\n"+ - "- `!figurinha`\n"+ - "- `!adivinhação começar|parar`\n"+ - "- `!info `" - )); - break; + const tokens = msg.body.trim().split(/\s+/); + const cmd = tokens[0]?.toLowerCase(); - case "!ping": - await msg.reply(botMsg("pong 🏓")); - break; + if (!cmd?.startsWith("!") && cmd !== "a") return; - case "!video": - if (!tokens[1]) return; - await msg.reply(botMsg("⏳ Baixando vídeo...")); - enqueueDownload("video", tokens[1], msg, chatId); - break; + log.cmd(cmd); - case "!audio": - if (!tokens[1]) return; - await msg.reply(botMsg("⏳ Baixando áudio...")); - enqueueDownload("audio", tokens[1], msg, chatId); - break; + try { + switch (cmd) { + case "!many": + await chat.sendMessage(botMsg( + "Comandos:\n\n" + + "- `!ping`\n" + + "- `!video `\n" + + "- `!audio `\n" + + "- `!figurinha`\n" + + "- `!adivinhação começar|parar`\n" + + "- `!info `" + )); + break; - case "!figurinha": - await gerarSticker(msg); - break; + case "!ping": + await msg.reply(botMsg("pong 🏓")); + log.ok("pong enviado"); + break; - case "!adivinhação": - if (!tokens[1]) { - await chat.sendMessage(botMsg("`!adivinhação começar`\n`!adivinhação parar`")); - return; - } - if (tokens[1] === "começar") { - iniciarJogo(); - await chat.sendMessage(botMsg("Jogo iniciado! Tente adivinhar o número de 1 a 100.")); - } - if (tokens[1] === "parar") { - pararJogo(); - await chat.sendMessage(botMsg("Jogo parado.")); - } - break; + case "!video": + if (!tokens[1]) { log.warn("!video sem link"); return; } + await msg.reply(botMsg("⏳ Baixando vídeo...")); + enqueueDownload("video", tokens[1], msg, chatId); + log.ok("vídeo enfileirado →", tokens[1]); + break; - case "!info": - if (!tokens[1]) { - await chat.sendMessage(botMsg("Use:\n`!info `")); - return; - } else { - processarInfo(tokens[1], chat); - } - break; + case "!audio": + if (!tokens[1]) { log.warn("!audio sem link"); return; } + await msg.reply(botMsg("⏳ Baixando áudio...")); + enqueueDownload("audio", tokens[1], msg, chatId); + log.ok("áudio enfileirado →", tokens[1]); + break; + + case "!figurinha": + const author = msg.author || msg.from; + + if (tokens[1] === "criar") { + await gerarSticker(msg, chatId); + } else { + if (stickerSessions.has(chatId)) { + return msg.reply("Já existe uma sessão ativa."); + } + + iniciarSessao(chatId, author); + + await msg.reply( + `Sessão de figurinha iniciada por @${author.split("@")[0]}. Envie no máximo 10 imagens, quando estiver pronto mande \`!figurinha criar\``, + null, + { mentions: [author] } + ); } - } catch(err) { - console.error(err); - await chat.sendMessage(botMsg("Erro:\n`"+err.message+"`")); + + break; + + + case "!adivinhação": + if (!tokens[1]) { + await chat.sendMessage(botMsg("`!adivinhação começar`\n`!adivinhação parar`")); + return; + } + if (tokens[1] === "começar") { + iniciarJogo(); + await chat.sendMessage(botMsg("Jogo iniciado! Tente adivinhar o número de 1 a 100.")); + log.ok("jogo iniciado"); + } else if (tokens[1] === "parar") { + pararJogo(); + await chat.sendMessage(botMsg("Jogo parado.")); + log.ok("jogo parado"); + } else { + log.warn("!adivinhação — subcomando desconhecido:", tokens[1]); + } + break; + + case "!info": + if (!tokens[1]) { + await chat.sendMessage(botMsg("Use:\n`!info `")); + return; + } + processarInfo(tokens[1], chat); + log.ok("info →", tokens[1]); + break; + + case "!obrigado": + case "!valeu": + case "!brigado": + await msg.reply(botMsg("Por nada!")); + break; + + case "a": + if (!tokens[1]) await msg.reply(botMsg("B")); + break; } + } catch (err) { + log.error("Falha em", cmd, "—", err.message); + await chat.sendMessage(botMsg("Erro:\n`" + err.message + "`")); + } } \ No newline at end of file diff --git a/src/download/video.js b/src/download/video.js index e169ba9..d598250 100644 --- a/src/download/video.js +++ b/src/download/video.js @@ -23,6 +23,7 @@ export async function get_video(url, id) { '--fragment-retries', '5', '--socket-timeout', '15', '--sleep-interval', '1', '--max-sleep-interval', '4', + '--no-playlist', url ]; diff --git a/src/main.js b/src/main.js index aeaed6c..fb90d56 100644 --- a/src/main.js +++ b/src/main.js @@ -1,29 +1,134 @@ -console.log("Iniciando..."); - import client from "./client/whatsappClient.js"; -import { CHATS } from "./config.js"; +import { CHATS, BOT_PREFIX } from "./config.js"; // <- importar PREFIX import { processarComando } from "./commands/index.js"; +import { coletarMidia } from "./commands/figurinha.js"; import { processarJogo } from "./games/adivinhacao.js"; import { getChatId } from "./utils/getChatId.js"; -client.on("message_create", async msg => { +// ── Cores ──────────────────────────────────────────────────── +const c = { + reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m", + green: "\x1b[32m", yellow: "\x1b[33m", cyan: "\x1b[36m", + red: "\x1b[31m", gray: "\x1b[90m", white: "\x1b[37m", + blue: "\x1b[34m", magenta: "\x1b[35m", +}; + +const now = () => + new Date().toLocaleString("pt-BR", { dateStyle: "short", timeStyle: "medium" }); + +const SEP = `${c.gray}${"─".repeat(52)}${c.reset}`; + +// ── Logger ─────────────────────────────────────────────────── +const logger = { + info: (...a) => console.log(`${SEP}\n${c.gray}[${now()}]${c.reset} ${c.cyan}INFO ${c.reset}`, ...a), + success: (...a) => console.log(`${c.gray}[${now()}]${c.reset} ${c.green}OK ${c.reset}`, ...a), + warn: (...a) => console.log(`${c.gray}[${now()}]${c.reset} ${c.yellow}WARN ${c.reset}`, ...a), + error: (...a) => console.log(`${c.gray}[${now()}]${c.reset} ${c.red}ERROR ${c.reset}`, ...a), + + msg: async (chatName, chatId, from, body, msg = {}) => { + const number = String(from).split("@")[0]; + const isGroup = /@g\.us$/.test(chatId); + let name = msg?.notifyName || number; + try { - const chat = await msg.getChat(); - const chatId = getChatId(chat); - if (!CHATS.includes(chatId)) return; + if (typeof client?.getContactById === "function") { + const contact = await client.getContactById(from); + name = contact?.pushname || contact?.formattedName || name; + } + } catch {} - console.log("=================================="); - console.log(`CHAT NAME : ${chat.name || chat.id.user}`); - console.log(`CHAT ID : ${chatId}`); - console.log(`FROM : ${msg.from}`); - console.log(`BODY : ${msg.body}`); - console.log("==================================\n"); + const type = msg?.type || "text"; + const typeLabel = + type === "sticker" ? `${c.magenta}sticker${c.reset}` : + type === "image" ? `${c.cyan}imagem${c.reset}` : + type === "video" ? `${c.cyan}vídeo${c.reset}` : + type === "audio" ? `${c.cyan}áudio${c.reset}` : + type === "ptt" ? `${c.cyan}áudio${c.reset}` : + type === "document" ? `${c.cyan}arquivo${c.reset}` : + type === "chat" ? `${c.white}texto${c.reset}` : + `${c.gray}${type}${c.reset}`; - await processarComando(msg, chat, chatId); - await processarJogo(msg, chat); - } catch(err) { - console.error("[ERRO]", err); + const isCommand = body?.trimStart().startsWith(BOT_PREFIX); + + const context = isGroup + ? `${c.bold}${chatName}${c.reset} ${c.dim}(grupo)${c.reset}` + : `${c.bold}${chatName}${c.reset} ${c.dim}(privado)${c.reset}`; + + const bodyPreview = body?.trim() + ? `${isCommand ? c.yellow : c.green}"${body.length > 80 ? body.slice(0, 80) + "…" : body}"${c.reset}` + : `${c.dim}<${typeLabel}>${c.reset}`; + + // Resolve reply + let replyLine = ""; + if (msg?.hasQuotedMsg) { + try { + const quoted = await msg.getQuotedMessage(); + const quotedNumber = String(quoted.from).split("@")[0]; + let quotedName = quotedNumber; + try { + const quotedContact = await client.getContactById(quoted.from); + quotedName = quotedContact?.pushname || quotedContact?.formattedName || quotedNumber; + } catch {} + const quotedPreview = quoted.body?.trim() + ? `"${quoted.body.length > 60 ? quoted.body.slice(0, 60) + "…" : quoted.body}"` + : `<${quoted.type}>`; + replyLine = + `\n${c.gray} ↩ Para: ${c.reset}${c.white}${quotedName}${c.reset} ${c.dim}+${quotedNumber}${c.reset}` + + `\n${c.gray} ↩ Msg: ${c.reset}${c.dim}${quotedPreview}${c.reset}`; + } catch {} } + + console.log( + `${SEP}\n` + + `${c.gray}[${now()}]${c.reset} ${c.cyan}MSG ${c.reset}${context}\n` + + `${c.gray} De: ${c.reset}${c.white}${name}${c.reset} ${c.dim}+${number}${c.reset}\n` + + `${c.gray} Tipo: ${c.reset}${typeLabel}${isCommand ? ` ${c.yellow}(bot)${c.reset}` : ""}\n` + + `${c.gray} Text: ${c.reset}${bodyPreview}` + + replyLine + ); + }, + + cmd: (cmd, extra = "") => + console.log( + `${c.gray}[${now()}]${c.reset} ${c.yellow}CMD ${c.reset}` + + `${c.bold}${cmd}${c.reset}` + + (extra ? ` ${c.dim}${extra}${c.reset}` : "") + ), + + done: (cmd, detail = "") => + console.log( + `${c.gray}[${now()}]${c.reset} ${c.green}DONE ${c.reset}` + + `${c.dim}${cmd}${c.reset}` + + (detail ? ` — ${detail}` : "") + ), +}; + +export { logger }; + +// ── Boot ───────────────────────────────────────────────────── +logger.info("Iniciando ManyBot..."); + +client.on("message_create", async msg => { + try { + const chat = await msg.getChat(); + const chatId = getChatId(chat); + + if (!CHATS.includes(chatId)) return; + + await logger.msg(chat.name || chat.id.user, chatId, msg.from, msg.body, msg); + + await coletarMidia(msg); + await processarComando(msg, chat, chatId); + await processarJogo(msg, chat); + + logger.done("message_create", `de +${String(msg.from).split("@")[0]}`); + } catch (err) { + logger.error( + `Falha ao processar — ${err.message}`, + `\n Stack: ${err.stack?.split("\n")[1]?.trim() ?? ""}` + ); + } }); -client.initialize(); \ No newline at end of file +client.initialize(); +logger.info("Cliente inicializado. Aguardando conexão com WhatsApp..."); \ No newline at end of file diff --git a/todo.txt b/todo.txt new file mode 100644 index 0000000..7b74601 --- /dev/null +++ b/todo.txt @@ -0,0 +1,9 @@ +Deixar o log mais claro, permanecendo limpo e simples + +Possibilidade de tirar fundo de figurinhas + +Salvar mensagens num banco de dados + +Mudar a licença para GPLv3 + +Possibilidade de baixar playlists do youtube \ No newline at end of file