Compare commits

..

4 Commits

Author SHA1 Message Date
OrionDesign
2fee5053f3 fix(baileys): corrigir declaração de variável error em blocos catch 2025-12-11 17:11:58 -03:00
OrionDesign
67c4aa640b refactor(baileys): atualizar serviço de mensagens e schemas de validação 2025-12-11 17:01:13 -03:00
OrionDesign
076449e5d6 Refactor DecryptPollVoteDto and schema structure
Updated DecryptPollVoteDto to use a nested message.key structure and moved remoteJid to the top level. Adjusted the controller and validation schema to match the new structure for consistency and clarity.
2025-12-09 17:03:15 -03:00
OrionDesign
5faf3d18d6 Add poll vote decryption endpoint and logic
Introduces a new API endpoint and supporting logic to decrypt WhatsApp poll votes. Adds DecryptPollVoteDto, validation schema, controller method, and service logic to process and aggregate poll vote results based on poll creation message key.
2025-12-09 16:56:46 -03:00
6 changed files with 327 additions and 0 deletions

28
package-lock.json generated
View File

@@ -2907,6 +2907,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"engines": { "engines": {
"node": ">=8.0.0" "node": ">=8.0.0"
} }
@@ -2928,6 +2929,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.2.0.tgz",
"integrity": "sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ==", "integrity": "sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ==",
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"engines": { "engines": {
"node": "^18.19.0 || >=20.6.0" "node": "^18.19.0 || >=20.6.0"
}, },
@@ -2940,6 +2942,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz",
"integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==", "integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==",
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"dependencies": { "dependencies": {
"@opentelemetry/semantic-conventions": "^1.29.0" "@opentelemetry/semantic-conventions": "^1.29.0"
}, },
@@ -2955,6 +2958,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.204.0.tgz", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.204.0.tgz",
"integrity": "sha512-vV5+WSxktzoMP8JoYWKeopChy6G3HKk4UQ2hESCRDUUTZqQ3+nM3u8noVG0LmNfRWwcFBnbZ71GKC7vaYYdJ1g==", "integrity": "sha512-vV5+WSxktzoMP8JoYWKeopChy6G3HKk4UQ2hESCRDUUTZqQ3+nM3u8noVG0LmNfRWwcFBnbZ71GKC7vaYYdJ1g==",
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"dependencies": { "dependencies": {
"@opentelemetry/api-logs": "0.204.0", "@opentelemetry/api-logs": "0.204.0",
"import-in-the-middle": "^1.8.1", "import-in-the-middle": "^1.8.1",
@@ -3362,6 +3366,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz",
"integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==",
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"dependencies": { "dependencies": {
"@opentelemetry/core": "2.2.0", "@opentelemetry/core": "2.2.0",
"@opentelemetry/semantic-conventions": "^1.29.0" "@opentelemetry/semantic-conventions": "^1.29.0"
@@ -3378,6 +3383,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz",
"integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==", "integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==",
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"dependencies": { "dependencies": {
"@opentelemetry/core": "2.2.0", "@opentelemetry/core": "2.2.0",
"@opentelemetry/resources": "2.2.0", "@opentelemetry/resources": "2.2.0",
@@ -3395,6 +3401,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.38.0.tgz", "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.38.0.tgz",
"integrity": "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==", "integrity": "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==",
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"engines": { "engines": {
"node": ">=14" "node": ">=14"
} }
@@ -3643,6 +3650,7 @@
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz", "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz",
"integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"cluster-key-slot": "1.1.2", "cluster-key-slot": "1.1.2",
"generic-pool": "3.9.0", "generic-pool": "3.9.0",
@@ -4933,6 +4941,7 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"undici-types": "~7.16.0" "undici-types": "~7.16.0"
} }
@@ -5108,6 +5117,7 @@
"integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==", "integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "8.47.0", "@typescript-eslint/scope-manager": "8.47.0",
"@typescript-eslint/types": "8.47.0", "@typescript-eslint/types": "8.47.0",
@@ -5411,6 +5421,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@@ -5758,6 +5769,7 @@
"resolved": "https://registry.npmjs.org/audio-decode/-/audio-decode-2.2.3.tgz", "resolved": "https://registry.npmjs.org/audio-decode/-/audio-decode-2.2.3.tgz",
"integrity": "sha512-Z0lHvMayR/Pad9+O9ddzaBJE0DrhZkQlStrC1RwcAHF3AhQAsdwKHeLGK8fYKyp2DDU6xHxzGb4CLMui12yVrg==", "integrity": "sha512-Z0lHvMayR/Pad9+O9ddzaBJE0DrhZkQlStrC1RwcAHF3AhQAsdwKHeLGK8fYKyp2DDU6xHxzGb4CLMui12yVrg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@wasm-audio-decoders/flac": "^0.2.4", "@wasm-audio-decoders/flac": "^0.2.4",
"@wasm-audio-decoders/ogg-vorbis": "^0.1.15", "@wasm-audio-decoders/ogg-vorbis": "^0.1.15",
@@ -6746,6 +6758,7 @@
"integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"env-paths": "^2.2.1", "env-paths": "^2.2.1",
"import-fresh": "^3.3.0", "import-fresh": "^3.3.0",
@@ -7636,6 +7649,7 @@
"integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==", "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"esbuild": "bin/esbuild" "esbuild": "bin/esbuild"
}, },
@@ -7706,6 +7720,7 @@
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1", "@eslint-community/regexpp": "^4.6.1",
@@ -7762,6 +7777,7 @@
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"eslint-config-prettier": "bin/cli.js" "eslint-config-prettier": "bin/cli.js"
}, },
@@ -8368,6 +8384,7 @@
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"accepts": "~1.3.8", "accepts": "~1.3.8",
"array-flatten": "1.1.1", "array-flatten": "1.1.1",
@@ -10355,6 +10372,7 @@
"resolved": "https://registry.npmjs.org/jimp/-/jimp-1.6.0.tgz", "resolved": "https://registry.npmjs.org/jimp/-/jimp-1.6.0.tgz",
"integrity": "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg==", "integrity": "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@jimp/core": "1.6.0", "@jimp/core": "1.6.0",
"@jimp/diff": "1.6.0", "@jimp/diff": "1.6.0",
@@ -10585,6 +10603,7 @@
"resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.4.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.4.tgz",
"integrity": "sha512-eohl3hKTiVyD1ilYdw9T0OiB4hnjef89e3dMYKz+mVKDzj+5IteTseASUsOB+EU9Tf6VNTCjDePcP6wkDGmLKQ==", "integrity": "sha512-eohl3hKTiVyD1ilYdw9T0OiB4hnjef89e3dMYKz+mVKDzj+5IteTseASUsOB+EU9Tf6VNTCjDePcP6wkDGmLKQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@keyv/serialize": "^1.1.1" "@keyv/serialize": "^1.1.1"
} }
@@ -10680,6 +10699,7 @@
"resolved": "https://registry.npmjs.org/link-preview-js/-/link-preview-js-3.2.0.tgz", "resolved": "https://registry.npmjs.org/link-preview-js/-/link-preview-js-3.2.0.tgz",
"integrity": "sha512-FvrLltjOPGbTzt+RugbzM7g8XuUNLPO2U/INSLczrYdAA32E7nZVUrVL1gr61DGOArGJA2QkPGMEvNMLLsXREA==", "integrity": "sha512-FvrLltjOPGbTzt+RugbzM7g8XuUNLPO2U/INSLczrYdAA32E7nZVUrVL1gr61DGOArGJA2QkPGMEvNMLLsXREA==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"cheerio": "1.0.0-rc.11", "cheerio": "1.0.0-rc.11",
"url": "0.11.0" "url": "0.11.0"
@@ -12600,6 +12620,7 @@
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
"integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"pg-connection-string": "^2.9.1", "pg-connection-string": "^2.9.1",
"pg-pool": "^3.10.1", "pg-pool": "^3.10.1",
@@ -12909,6 +12930,7 @@
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"prettier": "bin/prettier.cjs" "prettier": "bin/prettier.cjs"
}, },
@@ -12938,6 +12960,7 @@
"integrity": "sha512-F3eX7K+tWpkbhl3l4+VkFtrwJlLXbAM+f9jolgoUZbFcm1DgHZ4cq9AgVEgUym2au5Ad/TDLN8lg83D+M10ycw==", "integrity": "sha512-F3eX7K+tWpkbhl3l4+VkFtrwJlLXbAM+f9jolgoUZbFcm1DgHZ4cq9AgVEgUym2au5Ad/TDLN8lg83D+M10ycw==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"dependencies": { "dependencies": {
"@prisma/config": "6.19.0", "@prisma/config": "6.19.0",
"@prisma/engines": "6.19.0" "@prisma/engines": "6.19.0"
@@ -14029,6 +14052,7 @@
"integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"dependencies": { "dependencies": {
"@img/colour": "^1.0.0", "@img/colour": "^1.0.0",
"detect-libc": "^2.1.2", "detect-libc": "^2.1.2",
@@ -14871,6 +14895,7 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@@ -15059,6 +15084,7 @@
"integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "~0.25.0", "esbuild": "~0.25.0",
"get-tsconfig": "^4.7.5" "get-tsconfig": "^4.7.5"
@@ -15707,6 +15733,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true, "devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@@ -16222,6 +16249,7 @@
"integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
"devOptional": true, "devOptional": true,
"license": "ISC", "license": "ISC",
"peer": true,
"bin": { "bin": {
"yaml": "bin.mjs" "yaml": "bin.mjs"
}, },

View File

@@ -1,6 +1,7 @@
import { import {
ArchiveChatDto, ArchiveChatDto,
BlockUserDto, BlockUserDto,
DecryptPollVoteDto,
DeleteMessage, DeleteMessage,
getBase64FromMediaMessageDto, getBase64FromMediaMessageDto,
MarkChatUnreadDto, MarkChatUnreadDto,
@@ -113,4 +114,12 @@ export class ChatController {
public async blockUser({ instanceName }: InstanceDto, data: BlockUserDto) { public async blockUser({ instanceName }: InstanceDto, data: BlockUserDto) {
return await this.waMonitor.waInstances[instanceName].blockUser(data); return await this.waMonitor.waInstances[instanceName].blockUser(data);
} }
public async decryptPollVote({ instanceName }: InstanceDto, data: DecryptPollVoteDto) {
const pollCreationMessageKey = {
id: data.message.key.id,
remoteJid: data.remoteJid,
};
return await this.waMonitor.waInstances[instanceName].baileysDecryptPollVote(pollCreationMessageKey);
}
} }

View File

@@ -127,3 +127,12 @@ export class BlockUserDto {
number: string; number: string;
status: 'block' | 'unblock'; status: 'block' | 'unblock';
} }
export class DecryptPollVoteDto {
message: {
key: {
id: string;
};
};
remoteJid: string;
}

View File

@@ -5119,4 +5119,251 @@ export class BaileysStartupService extends ChannelStartupService {
}, },
}; };
} }
public async baileysDecryptPollVote(pollCreationMessageKey: proto.IMessageKey) {
try {
this.logger.verbose('Starting poll vote decryption process');
// Buscar a mensagem de criação da enquete
const pollCreationMessage = (await this.getMessage(pollCreationMessageKey, true)) as proto.IWebMessageInfo;
if (!pollCreationMessage) {
throw new NotFoundException('Poll creation message not found');
}
// Extrair opções da enquete
const pollOptions =
(pollCreationMessage.message as any)?.pollCreationMessage?.options ||
(pollCreationMessage.message as any)?.pollCreationMessageV3?.options ||
[];
if (!pollOptions || pollOptions.length === 0) {
throw new NotFoundException('Poll options not found');
}
// Recuperar chave de criptografia
const pollMessageSecret = (await this.getMessage(pollCreationMessageKey)) as any;
let pollEncKey = pollMessageSecret?.messageContextInfo?.messageSecret;
if (!pollEncKey) {
throw new NotFoundException('Poll encryption key not found');
}
// Normalizar chave de criptografia
if (typeof pollEncKey === 'string') {
pollEncKey = Buffer.from(pollEncKey, 'base64');
} else if (pollEncKey?.type === 'Buffer' && Array.isArray(pollEncKey.data)) {
pollEncKey = Buffer.from(pollEncKey.data);
}
if (Buffer.isBuffer(pollEncKey) && pollEncKey.length === 44) {
pollEncKey = Buffer.from(pollEncKey.toString('utf8'), 'base64');
}
// Buscar todas as mensagens de atualização de votos
const allPollUpdateMessages = await this.prismaRepository.message.findMany({
where: {
instanceId: this.instanceId,
messageType: 'pollUpdateMessage',
},
select: {
id: true,
key: true,
message: true,
messageTimestamp: true,
},
});
this.logger.verbose(`Found ${allPollUpdateMessages.length} pollUpdateMessage messages in database`);
// Filtrar apenas mensagens relacionadas a esta enquete específica
const pollUpdateMessages = allPollUpdateMessages.filter((msg) => {
const pollUpdate = (msg.message as any)?.pollUpdateMessage;
if (!pollUpdate) return false;
const creationKey = pollUpdate.pollCreationMessageKey;
if (!creationKey) return false;
return (
creationKey.id === pollCreationMessageKey.id &&
jidNormalizedUser(creationKey.remoteJid || '') === jidNormalizedUser(pollCreationMessageKey.remoteJid || '')
);
});
this.logger.verbose(`Filtered to ${pollUpdateMessages.length} matching poll update messages`);
// Preparar candidatos de JID para descriptografia
const creatorCandidates = [
this.instance.wuid,
this.client.user?.lid,
pollCreationMessage.key.participant,
(pollCreationMessage.key as any).participantAlt,
pollCreationMessage.key.remoteJid,
(pollCreationMessage.key as any).remoteJidAlt,
].filter(Boolean);
const uniqueCreators = [...new Set(creatorCandidates.map((id) => jidNormalizedUser(id)))];
// Processar votos
const votesByUser = new Map<string, { timestamp: number; selectedOptions: string[]; voterJid: string }>();
this.logger.verbose(`Processing ${pollUpdateMessages.length} poll update messages for decryption`);
for (const pollUpdateMsg of pollUpdateMessages) {
const pollVote = (pollUpdateMsg.message as any)?.pollUpdateMessage?.vote;
if (!pollVote) continue;
const key = pollUpdateMsg.key as any;
const voterCandidates = [
this.instance.wuid,
this.client.user?.lid,
key.participant,
key.participantAlt,
key.remoteJidAlt,
key.remoteJid,
].filter(Boolean);
const uniqueVoters = [...new Set(voterCandidates.map((id) => jidNormalizedUser(id)))];
let selectedOptionNames: string[] = [];
let successfulVoterJid: string | undefined;
// Verificar se o voto já está descriptografado
if (pollVote.selectedOptions && Array.isArray(pollVote.selectedOptions)) {
const selectedOptions = pollVote.selectedOptions;
this.logger.verbose('Vote already has selectedOptions, checking format');
// Verificar se são strings (já descriptografado) ou buffers (precisa descriptografar)
if (selectedOptions.length > 0 && typeof selectedOptions[0] === 'string') {
// Já está descriptografado como nomes de opções
selectedOptionNames = selectedOptions;
successfulVoterJid = uniqueVoters[0];
this.logger.verbose(
`Using already decrypted vote: voter=${successfulVoterJid}, options=${selectedOptionNames.join(',')}`,
);
} else {
// Está como hash, precisa converter para nomes
selectedOptionNames = pollOptions
.filter((option: any) => {
const hash = createHash('sha256').update(option.optionName).digest();
return selectedOptions.some((selected: any) => {
if (Buffer.isBuffer(selected)) {
return Buffer.compare(selected, hash) === 0;
}
return false;
});
})
.map((option: any) => option.optionName);
successfulVoterJid = uniqueVoters[0];
}
} else if (pollVote.encPayload && pollEncKey) {
// Tentar descriptografar
let decryptedVote: any = null;
for (const creator of uniqueCreators) {
for (const voter of uniqueVoters) {
try {
decryptedVote = decryptPollVote(pollVote, {
pollCreatorJid: creator,
pollMsgId: pollCreationMessage.key.id,
pollEncKey,
voterJid: voter,
} as any);
if (decryptedVote) {
successfulVoterJid = voter;
break;
}
} catch {
// Continue tentando outras combinações
}
}
if (decryptedVote) break;
}
if (decryptedVote && decryptedVote.selectedOptions) {
// Converter hashes para nomes de opções
selectedOptionNames = pollOptions
.filter((option: any) => {
const hash = createHash('sha256').update(option.optionName).digest();
return decryptedVote.selectedOptions.some((selected: any) => {
if (Buffer.isBuffer(selected)) {
return Buffer.compare(selected, hash) === 0;
}
return false;
});
})
.map((option: any) => option.optionName);
this.logger.verbose(
`Successfully decrypted vote for voter: ${successfulVoterJid}, creator: ${uniqueCreators[0]}`,
);
} else {
this.logger.warn(`Failed to decrypt vote. Last error: Could not decrypt with any combination`);
continue;
}
} else {
this.logger.warn('Vote has no encPayload and no selectedOptions, skipping');
continue;
}
if (selectedOptionNames.length > 0 && successfulVoterJid) {
const normalizedVoterJid = jidNormalizedUser(successfulVoterJid);
const existingVote = votesByUser.get(normalizedVoterJid);
// Manter apenas o voto mais recente de cada usuário
if (!existingVote || pollUpdateMsg.messageTimestamp > existingVote.timestamp) {
votesByUser.set(normalizedVoterJid, {
timestamp: pollUpdateMsg.messageTimestamp,
selectedOptions: selectedOptionNames,
voterJid: successfulVoterJid,
});
}
}
}
// Agrupar votos por opção
const results: Record<string, { votes: number; voters: string[] }> = {};
// Inicializar todas as opções com zero votos
pollOptions.forEach((option: any) => {
results[option.optionName] = {
votes: 0,
voters: [],
};
});
// Agregar votos
votesByUser.forEach((voteData) => {
voteData.selectedOptions.forEach((optionName) => {
if (results[optionName]) {
results[optionName].votes++;
if (!results[optionName].voters.includes(voteData.voterJid)) {
results[optionName].voters.push(voteData.voterJid);
}
}
});
});
// Obter nome da enquete
const pollName =
(pollCreationMessage.message as any)?.pollCreationMessage?.name ||
(pollCreationMessage.message as any)?.pollCreationMessageV3?.name ||
'Enquete sem nome';
// Calcular total de votos únicos
const totalVotes = votesByUser.size;
return {
poll: {
name: pollName,
totalVotes,
results,
},
};
} catch (error) {
this.logger.error(`Error decrypting poll votes: ${error}`);
throw new InternalServerErrorException('Error decrypting poll votes', error.toString());
}
}
} }

View File

@@ -2,6 +2,7 @@ import { RouterBroker } from '@api/abstract/abstract.router';
import { import {
ArchiveChatDto, ArchiveChatDto,
BlockUserDto, BlockUserDto,
DecryptPollVoteDto,
DeleteMessage, DeleteMessage,
getBase64FromMediaMessageDto, getBase64FromMediaMessageDto,
MarkChatUnreadDto, MarkChatUnreadDto,
@@ -23,6 +24,7 @@ import {
archiveChatSchema, archiveChatSchema,
blockUserSchema, blockUserSchema,
contactValidateSchema, contactValidateSchema,
decryptPollVoteSchema,
deleteMessageSchema, deleteMessageSchema,
markChatUnreadSchema, markChatUnreadSchema,
messageUpSchema, messageUpSchema,
@@ -281,6 +283,16 @@ export class ChatRouter extends RouterBroker {
}); });
return res.status(HttpStatus.CREATED).json(response); return res.status(HttpStatus.CREATED).json(response);
})
.post(this.routerPath('getPollVote'), ...guards, async (req, res) => {
const response = await this.dataValidate<DecryptPollVoteDto>({
request: req,
schema: decryptPollVoteSchema,
ClassRef: DecryptPollVoteDto,
execute: (instance, data) => chatController.decryptPollVote(instance, data),
});
return res.status(HttpStatus.OK).json(response);
}); });
} }

View File

@@ -447,3 +447,25 @@ export const buttonsMessageSchema: JSONSchema7 = {
}, },
required: ['number'], required: ['number'],
}; };
export const decryptPollVoteSchema: JSONSchema7 = {
$id: v4(),
type: 'object',
properties: {
message: {
type: 'object',
properties: {
key: {
type: 'object',
properties: {
id: { type: 'string' },
},
required: ['id'],
},
},
required: ['key'],
},
remoteJid: { type: 'string' },
},
required: ['message', 'remoteJid'],
};