mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-12-18 11:22:21 -06:00
Merge pull request #2297 from oriondesign2015/develop
Feature: Endpoint para Descriptografar e Visualizar Votos de Enquetes
This commit is contained in:
28
package-lock.json
generated
28
package-lock.json
generated
@@ -2907,6 +2907,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
|
||||
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
@@ -2928,6 +2929,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.2.0.tgz",
|
||||
"integrity": "sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^18.19.0 || >=20.6.0"
|
||||
},
|
||||
@@ -2940,6 +2942,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz",
|
||||
"integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/semantic-conventions": "^1.29.0"
|
||||
},
|
||||
@@ -2955,6 +2958,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.204.0.tgz",
|
||||
"integrity": "sha512-vV5+WSxktzoMP8JoYWKeopChy6G3HKk4UQ2hESCRDUUTZqQ3+nM3u8noVG0LmNfRWwcFBnbZ71GKC7vaYYdJ1g==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/api-logs": "0.204.0",
|
||||
"import-in-the-middle": "^1.8.1",
|
||||
@@ -3362,6 +3366,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz",
|
||||
"integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "2.2.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",
|
||||
"integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "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",
|
||||
"integrity": "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
@@ -3643,6 +3650,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz",
|
||||
"integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cluster-key-slot": "1.1.2",
|
||||
"generic-pool": "3.9.0",
|
||||
@@ -4933,6 +4941,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
|
||||
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
@@ -5108,6 +5117,7 @@
|
||||
"integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.47.0",
|
||||
"@typescript-eslint/types": "8.47.0",
|
||||
@@ -5411,6 +5421,7 @@
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -5758,6 +5769,7 @@
|
||||
"resolved": "https://registry.npmjs.org/audio-decode/-/audio-decode-2.2.3.tgz",
|
||||
"integrity": "sha512-Z0lHvMayR/Pad9+O9ddzaBJE0DrhZkQlStrC1RwcAHF3AhQAsdwKHeLGK8fYKyp2DDU6xHxzGb4CLMui12yVrg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@wasm-audio-decoders/flac": "^0.2.4",
|
||||
"@wasm-audio-decoders/ogg-vorbis": "^0.1.15",
|
||||
@@ -6746,6 +6758,7 @@
|
||||
"integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"env-paths": "^2.2.1",
|
||||
"import-fresh": "^3.3.0",
|
||||
@@ -7636,6 +7649,7 @@
|
||||
"integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
@@ -7706,6 +7720,7 @@
|
||||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.6.1",
|
||||
@@ -7762,6 +7777,7 @@
|
||||
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"eslint-config-prettier": "bin/cli.js"
|
||||
},
|
||||
@@ -8368,6 +8384,7 @@
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
|
||||
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
@@ -10355,6 +10372,7 @@
|
||||
"resolved": "https://registry.npmjs.org/jimp/-/jimp-1.6.0.tgz",
|
||||
"integrity": "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jimp/core": "1.6.0",
|
||||
"@jimp/diff": "1.6.0",
|
||||
@@ -10585,6 +10603,7 @@
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.4.tgz",
|
||||
"integrity": "sha512-eohl3hKTiVyD1ilYdw9T0OiB4hnjef89e3dMYKz+mVKDzj+5IteTseASUsOB+EU9Tf6VNTCjDePcP6wkDGmLKQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@keyv/serialize": "^1.1.1"
|
||||
}
|
||||
@@ -10680,6 +10699,7 @@
|
||||
"resolved": "https://registry.npmjs.org/link-preview-js/-/link-preview-js-3.2.0.tgz",
|
||||
"integrity": "sha512-FvrLltjOPGbTzt+RugbzM7g8XuUNLPO2U/INSLczrYdAA32E7nZVUrVL1gr61DGOArGJA2QkPGMEvNMLLsXREA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cheerio": "1.0.0-rc.11",
|
||||
"url": "0.11.0"
|
||||
@@ -12600,6 +12620,7 @@
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
|
||||
"integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"pg-connection-string": "^2.9.1",
|
||||
"pg-pool": "^3.10.1",
|
||||
@@ -12909,6 +12930,7 @@
|
||||
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
@@ -12938,6 +12960,7 @@
|
||||
"integrity": "sha512-F3eX7K+tWpkbhl3l4+VkFtrwJlLXbAM+f9jolgoUZbFcm1DgHZ4cq9AgVEgUym2au5Ad/TDLN8lg83D+M10ycw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@prisma/config": "6.19.0",
|
||||
"@prisma/engines": "6.19.0"
|
||||
@@ -14029,6 +14052,7 @@
|
||||
"integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@img/colour": "^1.0.0",
|
||||
"detect-libc": "^2.1.2",
|
||||
@@ -14871,6 +14895,7 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -15059,6 +15084,7 @@
|
||||
"integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "~0.25.0",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
@@ -15707,6 +15733,7 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -16222,6 +16249,7 @@
|
||||
"integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
|
||||
"devOptional": true,
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
ArchiveChatDto,
|
||||
BlockUserDto,
|
||||
DecryptPollVoteDto,
|
||||
DeleteMessage,
|
||||
getBase64FromMediaMessageDto,
|
||||
MarkChatUnreadDto,
|
||||
@@ -114,6 +115,13 @@ export class ChatController {
|
||||
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);
|
||||
|
||||
public async fetchChannels({ instanceName }: InstanceDto, query: Query<Contact>) {
|
||||
return await this.waMonitor.waInstances[instanceName].fetchChannels(query);
|
||||
}
|
||||
|
||||
@@ -127,3 +127,12 @@ export class BlockUserDto {
|
||||
number: string;
|
||||
status: 'block' | 'unblock';
|
||||
}
|
||||
|
||||
export class DecryptPollVoteDto {
|
||||
message: {
|
||||
key: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
remoteJid: string;
|
||||
}
|
||||
|
||||
@@ -5136,6 +5136,253 @@ 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());
|
||||
}
|
||||
|
||||
public async fetchChannels(query: Query<Contact>) {
|
||||
const page = Number((query as any)?.page ?? 1);
|
||||
const limit = Number((query as any)?.limit ?? (query as any)?.rows ?? 50);
|
||||
|
||||
@@ -2,6 +2,7 @@ import { RouterBroker } from '@api/abstract/abstract.router';
|
||||
import {
|
||||
ArchiveChatDto,
|
||||
BlockUserDto,
|
||||
DecryptPollVoteDto,
|
||||
DeleteMessage,
|
||||
getBase64FromMediaMessageDto,
|
||||
MarkChatUnreadDto,
|
||||
@@ -23,6 +24,7 @@ import {
|
||||
archiveChatSchema,
|
||||
blockUserSchema,
|
||||
contactValidateSchema,
|
||||
decryptPollVoteSchema,
|
||||
deleteMessageSchema,
|
||||
markChatUnreadSchema,
|
||||
messageUpSchema,
|
||||
@@ -282,6 +284,12 @@ export class ChatRouter extends RouterBroker {
|
||||
|
||||
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),
|
||||
.post(this.routerPath('findChannels'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate({
|
||||
request: req,
|
||||
|
||||
@@ -447,3 +447,25 @@ export const buttonsMessageSchema: JSONSchema7 = {
|
||||
},
|
||||
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'],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user