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