@@ -5119,4 +5119,247 @@ export class BaileysStartupService extends ChannelStartupService {
51195119 } ,
51205120 } ;
51215121 }
5122+
5123+ public async baileysDecryptPollVote ( pollCreationMessageKey : proto . IMessageKey ) {
5124+ try {
5125+ this . logger . verbose ( 'Starting poll vote decryption process' ) ;
5126+
5127+ // Buscar a mensagem de criação da enquete
5128+ const pollCreationMessage = ( await this . getMessage ( pollCreationMessageKey , true ) ) as proto . IWebMessageInfo ;
5129+
5130+ if ( ! pollCreationMessage ) {
5131+ throw new NotFoundException ( 'Poll creation message not found' ) ;
5132+ }
5133+
5134+ // Extrair opções da enquete
5135+ const pollOptions =
5136+ ( pollCreationMessage . message as any ) ?. pollCreationMessage ?. options ||
5137+ ( pollCreationMessage . message as any ) ?. pollCreationMessageV3 ?. options ||
5138+ [ ] ;
5139+
5140+ if ( ! pollOptions || pollOptions . length === 0 ) {
5141+ throw new NotFoundException ( 'Poll options not found' ) ;
5142+ }
5143+
5144+ // Recuperar chave de criptografia
5145+ const pollMessageSecret = ( await this . getMessage ( pollCreationMessageKey ) ) as any ;
5146+ let pollEncKey = pollMessageSecret ?. messageContextInfo ?. messageSecret ;
5147+
5148+ if ( ! pollEncKey ) {
5149+ throw new NotFoundException ( 'Poll encryption key not found' ) ;
5150+ }
5151+
5152+ // Normalizar chave de criptografia
5153+ if ( typeof pollEncKey === 'string' ) {
5154+ pollEncKey = Buffer . from ( pollEncKey , 'base64' ) ;
5155+ } else if ( pollEncKey ?. type === 'Buffer' && Array . isArray ( pollEncKey . data ) ) {
5156+ pollEncKey = Buffer . from ( pollEncKey . data ) ;
5157+ }
5158+
5159+ if ( Buffer . isBuffer ( pollEncKey ) && pollEncKey . length === 44 ) {
5160+ pollEncKey = Buffer . from ( pollEncKey . toString ( 'utf8' ) , 'base64' ) ;
5161+ }
5162+
5163+ // Buscar todas as mensagens de atualização de votos
5164+ const allPollUpdateMessages = await this . prismaRepository . message . findMany ( {
5165+ where : {
5166+ instanceId : this . instanceId ,
5167+ messageType : 'pollUpdateMessage' ,
5168+ } ,
5169+ select : {
5170+ id : true ,
5171+ key : true ,
5172+ message : true ,
5173+ messageTimestamp : true ,
5174+ } ,
5175+ } ) ;
5176+
5177+ this . logger . verbose ( `Found ${ allPollUpdateMessages . length } pollUpdateMessage messages in database` ) ;
5178+
5179+ // Filtrar apenas mensagens relacionadas a esta enquete específica
5180+ const pollUpdateMessages = allPollUpdateMessages . filter ( ( msg ) => {
5181+ const pollUpdate = ( msg . message as any ) ?. pollUpdateMessage ;
5182+ if ( ! pollUpdate ) return false ;
5183+
5184+ const creationKey = pollUpdate . pollCreationMessageKey ;
5185+ if ( ! creationKey ) return false ;
5186+
5187+ return (
5188+ creationKey . id === pollCreationMessageKey . id &&
5189+ jidNormalizedUser ( creationKey . remoteJid || '' ) === jidNormalizedUser ( pollCreationMessageKey . remoteJid || '' )
5190+ ) ;
5191+ } ) ;
5192+
5193+ this . logger . verbose ( `Filtered to ${ pollUpdateMessages . length } matching poll update messages` ) ;
5194+
5195+ // Preparar candidatos de JID para descriptografia
5196+ const creatorCandidates = [
5197+ this . instance . wuid ,
5198+ this . client . user ?. lid ,
5199+ pollCreationMessage . key . participant ,
5200+ ( pollCreationMessage . key as any ) . participantAlt ,
5201+ pollCreationMessage . key . remoteJid ,
5202+ ( pollCreationMessage . key as any ) . remoteJidAlt ,
5203+ ] . filter ( Boolean ) ;
5204+
5205+ const uniqueCreators = [ ...new Set ( creatorCandidates . map ( ( id ) => jidNormalizedUser ( id ) ) ) ] ;
5206+
5207+ // Processar votos
5208+ const votesByUser = new Map < string , { timestamp : number ; selectedOptions : string [ ] ; voterJid : string } > ( ) ;
5209+
5210+ this . logger . verbose ( `Processing ${ pollUpdateMessages . length } poll update messages for decryption` ) ;
5211+
5212+ for ( const pollUpdateMsg of pollUpdateMessages ) {
5213+ const pollVote = ( pollUpdateMsg . message as any ) ?. pollUpdateMessage ?. vote ;
5214+ if ( ! pollVote ) continue ;
5215+
5216+ const key = pollUpdateMsg . key as any ;
5217+ const voterCandidates = [
5218+ this . instance . wuid ,
5219+ this . client . user ?. lid ,
5220+ key . participant ,
5221+ key . participantAlt ,
5222+ key . remoteJidAlt ,
5223+ key . remoteJid ,
5224+ ] . filter ( Boolean ) ;
5225+
5226+ const uniqueVoters = [ ...new Set ( voterCandidates . map ( ( id ) => jidNormalizedUser ( id ) ) ) ] ;
5227+
5228+ let selectedOptionNames : string [ ] = [ ] ;
5229+ let successfulVoterJid : string | undefined ;
5230+
5231+ // Verificar se o voto já está descriptografado
5232+ if ( pollVote . selectedOptions && Array . isArray ( pollVote . selectedOptions ) ) {
5233+ const selectedOptions = pollVote . selectedOptions ;
5234+ this . logger . verbose ( 'Vote already has selectedOptions, checking format' ) ;
5235+
5236+ // Verificar se são strings (já descriptografado) ou buffers (precisa descriptografar)
5237+ if ( selectedOptions . length > 0 && typeof selectedOptions [ 0 ] === 'string' ) {
5238+ // Já está descriptografado como nomes de opções
5239+ selectedOptionNames = selectedOptions ;
5240+ successfulVoterJid = uniqueVoters [ 0 ] ;
5241+ this . logger . verbose ( `Using already decrypted vote: voter=${ successfulVoterJid } , options=${ selectedOptionNames . join ( ',' ) } ` ) ;
5242+ } else {
5243+ // Está como hash, precisa converter para nomes
5244+ selectedOptionNames = pollOptions
5245+ . filter ( ( option : any ) => {
5246+ const hash = createHash ( 'sha256' ) . update ( option . optionName ) . digest ( ) ;
5247+ return selectedOptions . some ( ( selected : any ) => {
5248+ if ( Buffer . isBuffer ( selected ) ) {
5249+ return Buffer . compare ( selected , hash ) === 0 ;
5250+ }
5251+ return false ;
5252+ } ) ;
5253+ } )
5254+ . map ( ( option : any ) => option . optionName ) ;
5255+ successfulVoterJid = uniqueVoters [ 0 ] ;
5256+ }
5257+ } else if ( pollVote . encPayload && pollEncKey ) {
5258+ // Tentar descriptografar
5259+ let decryptedVote : any = null ;
5260+
5261+ for ( const creator of uniqueCreators ) {
5262+ for ( const voter of uniqueVoters ) {
5263+ try {
5264+ decryptedVote = decryptPollVote ( pollVote , {
5265+ pollCreatorJid : creator ,
5266+ pollMsgId : pollCreationMessage . key . id ,
5267+ pollEncKey,
5268+ voterJid : voter ,
5269+ } as any ) ;
5270+
5271+ if ( decryptedVote ) {
5272+ successfulVoterJid = voter ;
5273+ break ;
5274+ }
5275+ } catch ( error ) {
5276+ // Continue tentando outras combinações
5277+ }
5278+ }
5279+ if ( decryptedVote ) break ;
5280+ }
5281+
5282+ if ( decryptedVote && decryptedVote . selectedOptions ) {
5283+ // Converter hashes para nomes de opções
5284+ selectedOptionNames = pollOptions
5285+ . filter ( ( option : any ) => {
5286+ const hash = createHash ( 'sha256' ) . update ( option . optionName ) . digest ( ) ;
5287+ return decryptedVote . selectedOptions . some ( ( selected : any ) => {
5288+ if ( Buffer . isBuffer ( selected ) ) {
5289+ return Buffer . compare ( selected , hash ) === 0 ;
5290+ }
5291+ return false ;
5292+ } ) ;
5293+ } )
5294+ . map ( ( option : any ) => option . optionName ) ;
5295+
5296+ this . logger . verbose ( `Successfully decrypted vote for voter: ${ successfulVoterJid } , creator: ${ uniqueCreators [ 0 ] } ` ) ;
5297+ } else {
5298+ this . logger . warn ( `Failed to decrypt vote. Last error: Could not decrypt with any combination` ) ;
5299+ continue ;
5300+ }
5301+ } else {
5302+ this . logger . warn ( 'Vote has no encPayload and no selectedOptions, skipping' ) ;
5303+ continue ;
5304+ }
5305+
5306+ if ( selectedOptionNames . length > 0 && successfulVoterJid ) {
5307+ const normalizedVoterJid = jidNormalizedUser ( successfulVoterJid ) ;
5308+ const existingVote = votesByUser . get ( normalizedVoterJid ) ;
5309+
5310+ // Manter apenas o voto mais recente de cada usuário
5311+ if ( ! existingVote || pollUpdateMsg . messageTimestamp > existingVote . timestamp ) {
5312+ votesByUser . set ( normalizedVoterJid , {
5313+ timestamp : pollUpdateMsg . messageTimestamp ,
5314+ selectedOptions : selectedOptionNames ,
5315+ voterJid : successfulVoterJid ,
5316+ } ) ;
5317+ }
5318+ }
5319+ }
5320+
5321+ // Agrupar votos por opção
5322+ const results : Record < string , { votes : number ; voters : string [ ] } > = { } ;
5323+
5324+ // Inicializar todas as opções com zero votos
5325+ pollOptions . forEach ( ( option : any ) => {
5326+ results [ option . optionName ] = {
5327+ votes : 0 ,
5328+ voters : [ ] ,
5329+ } ;
5330+ } ) ;
5331+
5332+ // Agregar votos
5333+ votesByUser . forEach ( ( voteData ) => {
5334+ voteData . selectedOptions . forEach ( ( optionName ) => {
5335+ if ( results [ optionName ] ) {
5336+ results [ optionName ] . votes ++ ;
5337+ if ( ! results [ optionName ] . voters . includes ( voteData . voterJid ) ) {
5338+ results [ optionName ] . voters . push ( voteData . voterJid ) ;
5339+ }
5340+ }
5341+ } ) ;
5342+ } ) ;
5343+
5344+ // Obter nome da enquete
5345+ const pollName =
5346+ ( pollCreationMessage . message as any ) ?. pollCreationMessage ?. name ||
5347+ ( pollCreationMessage . message as any ) ?. pollCreationMessageV3 ?. name ||
5348+ 'Enquete sem nome' ;
5349+
5350+ // Calcular total de votos únicos
5351+ const totalVotes = votesByUser . size ;
5352+
5353+ return {
5354+ poll : {
5355+ name : pollName ,
5356+ totalVotes,
5357+ results,
5358+ } ,
5359+ } ;
5360+ } catch ( error ) {
5361+ this . logger . error ( `Error decrypting poll votes: ${ error } ` ) ;
5362+ throw new InternalServerErrorException ( 'Error decrypting poll votes' , error . toString ( ) ) ;
5363+ }
5364+ }
51225365}
0 commit comments