@@ -543,215 +543,220 @@ export class ChatwootService {
543543 }
544544
545545 public async createConversation ( instance : InstanceDto , body : any ) {
546+ const remoteJid = body . key . remoteJid ;
547+ const cacheKey = `${ instance . instanceName } :createConversation-${ remoteJid } ` ;
548+ const lockKey = `${ instance . instanceName } :lock:createConversation-${ remoteJid } ` ;
549+ const maxWaitTime = 5000 ; // 5 secounds
550+
546551 try {
547- this . logger . verbose ( ' --- Start createConversation ---' ) ;
552+ this . logger . verbose ( ` --- Start createConversation ---` ) ;
548553 this . logger . verbose ( `Instance: ${ JSON . stringify ( instance ) } ` ) ;
549554
550- const client = await this . clientCw ( instance ) ;
551-
552- if ( ! client ) {
553- this . logger . warn ( `Client not found for instance: ${ JSON . stringify ( instance ) } ` ) ;
554- return null ;
555- }
556-
557- const cacheKey = `${ instance . instanceName } :createConversation-${ body . key . remoteJid } ` ;
558- this . logger . verbose ( `Cache key: ${ cacheKey } ` ) ;
559-
555+ // If it already exists in the cache, return conversationId
560556 if ( await this . cache . has ( cacheKey ) ) {
561- this . logger . verbose ( `Cache hit for key: ${ cacheKey } ` ) ;
562557 const conversationId = ( await this . cache . get ( cacheKey ) ) as number ;
563- this . logger . verbose ( `Cached conversation ID: ${ conversationId } ` ) ;
564- let conversationExists : conversation | boolean ;
565- try {
566- conversationExists = await client . conversations . get ( {
567- accountId : this . provider . accountId ,
568- conversationId : conversationId ,
569- } ) ;
570- this . logger . verbose ( `Conversation exists: ${ JSON . stringify ( conversationExists ) } ` ) ;
571- } catch ( error ) {
572- this . logger . error ( `Error getting conversation: ${ error } ` ) ;
573- conversationExists = false ;
574- }
575- if ( ! conversationExists ) {
576- this . logger . verbose ( 'Conversation does not exist, re-calling createConversation' ) ;
577- this . cache . delete ( cacheKey ) ;
578- return await this . createConversation ( instance , body ) ;
579- }
580-
558+ this . logger . verbose ( `Found conversation to: ${ remoteJid } , conversation ID: ${ conversationId } ` ) ;
581559 return conversationId ;
582560 }
583561
584- const isGroup = body . key . remoteJid . includes ( '@g.us' ) ;
585- this . logger . verbose ( `Is group: ${ isGroup } ` ) ;
562+ // If lock already exists, wait until release or timeout
563+ if ( await this . cache . has ( lockKey ) ) {
564+ this . logger . verbose ( `Operação de criação já em andamento para ${ remoteJid } , aguardando resultado...` ) ;
565+ const start = Date . now ( ) ;
566+ while ( await this . cache . has ( lockKey ) ) {
567+ if ( Date . now ( ) - start > maxWaitTime ) {
568+ this . logger . warn ( `Timeout aguardando lock para ${ remoteJid } ` ) ;
569+ break ;
570+ }
571+ await new Promise ( ( res ) => setTimeout ( res , 300 ) ) ;
572+ if ( await this . cache . has ( cacheKey ) ) {
573+ const conversationId = ( await this . cache . get ( cacheKey ) ) as number ;
574+ this . logger . verbose ( `Resolves creation of: ${ remoteJid } , conversation ID: ${ conversationId } ` ) ;
575+ return conversationId ;
576+ }
577+ }
578+ }
586579
587- const chatId = isGroup ? body . key . remoteJid : body . key . remoteJid . split ( '@' ) [ 0 ] ;
588- this . logger . verbose ( `Chat ID: ${ chatId } ` ) ;
580+ // Adquire lock
581+ await this . cache . set ( lockKey , true , 30 ) ;
582+ this . logger . verbose ( `Bloqueio adquirido para: ${ lockKey } ` ) ;
583+
584+ try {
585+ /*
586+ Double check after lock
587+ Utilizei uma nova verificação para evitar que outra thread execute entre o terminio do while e o set lock
588+ */
589+ if ( await this . cache . has ( cacheKey ) ) {
590+ return ( await this . cache . get ( cacheKey ) ) as number ;
591+ }
589592
590- let nameContact : string ;
593+ const client = await this . clientCw ( instance ) ;
594+ if ( ! client ) return null ;
591595
592- nameContact = ! body . key . fromMe ? body . pushName : chatId ;
593- this . logger . verbose ( `Name contact: ${ nameContact } ` ) ;
596+ const isGroup = remoteJid . includes ( '@g.us' ) ;
597+ const chatId = isGroup ? remoteJid : remoteJid . split ( '@' ) [ 0 ] ;
598+ let nameContact = ! body . key . fromMe ? body . pushName : chatId ;
599+ const filterInbox = await this . getInbox ( instance ) ;
600+ if ( ! filterInbox ) return null ;
594601
595- const filterInbox = await this . getInbox ( instance ) ;
602+ if ( isGroup ) {
603+ this . logger . verbose ( `Processing group conversation` ) ;
604+ const group = await this . waMonitor . waInstances [ instance . instanceName ] . client . groupMetadata ( chatId ) ;
605+ this . logger . verbose ( `Group metadata: ${ JSON . stringify ( group ) } ` ) ;
596606
597- if ( ! filterInbox ) {
598- this . logger . warn ( `Inbox not found for instance: ${ JSON . stringify ( instance ) } ` ) ;
599- return null ;
600- }
607+ nameContact = `${ group . subject } (GROUP)` ;
608+
609+ const picture_url = await this . waMonitor . waInstances [ instance . instanceName ] . profilePicture (
610+ body . key . participant . split ( '@' ) [ 0 ] ,
611+ ) ;
612+ this . logger . verbose ( `Participant profile picture URL: ${ JSON . stringify ( picture_url ) } ` ) ;
601613
602- if ( isGroup ) {
603- this . logger . verbose ( 'Processing group conversation' ) ;
604- const group = await this . waMonitor . waInstances [ instance . instanceName ] . client . groupMetadata ( chatId ) ;
605- this . logger . verbose ( `Group metadata: ${ JSON . stringify ( group ) } ` ) ;
614+ const findParticipant = await this . findContact ( instance , body . key . participant . split ( '@' ) [ 0 ] ) ;
615+ this . logger . verbose ( `Found participant: ${ JSON . stringify ( findParticipant ) } ` ) ;
606616
607- nameContact = `${ group . subject } (GROUP)` ;
617+ if ( findParticipant ) {
618+ if ( ! findParticipant . name || findParticipant . name === chatId ) {
619+ await this . updateContact ( instance , findParticipant . id , {
620+ name : body . pushName ,
621+ avatar_url : picture_url . profilePictureUrl || null ,
622+ } ) ;
623+ }
624+ } else {
625+ await this . createContact (
626+ instance ,
627+ body . key . participant . split ( '@' ) [ 0 ] ,
628+ filterInbox . id ,
629+ false ,
630+ body . pushName ,
631+ picture_url . profilePictureUrl || null ,
632+ body . key . participant ,
633+ ) ;
634+ }
635+ }
608636
609- const picture_url = await this . waMonitor . waInstances [ instance . instanceName ] . profilePicture (
610- body . key . participant . split ( '@' ) [ 0 ] ,
611- ) ;
612- this . logger . verbose ( `Participant profile picture URL: ${ JSON . stringify ( picture_url ) } ` ) ;
637+ const picture_url = await this . waMonitor . waInstances [ instance . instanceName ] . profilePicture ( chatId ) ;
638+ this . logger . verbose ( `Contact profile picture URL: ${ JSON . stringify ( picture_url ) } ` ) ;
613639
614- const findParticipant = await this . findContact ( instance , body . key . participant . split ( '@' ) [ 0 ] ) ;
615- this . logger . verbose ( `Found participant: ${ JSON . stringify ( findParticipant ) } ` ) ;
640+ let contact = await this . findContact ( instance , chatId ) ;
616641
617- if ( findParticipant ) {
618- if ( ! findParticipant . name || findParticipant . name === chatId ) {
619- await this . updateContact ( instance , findParticipant . id , {
620- name : body . pushName ,
621- avatar_url : picture_url . profilePictureUrl || null ,
622- } ) ;
642+ if ( contact ) {
643+ this . logger . verbose ( `Found contact: ${ JSON . stringify ( contact ) } ` ) ;
644+ if ( ! body . key . fromMe ) {
645+ const waProfilePictureFile =
646+ picture_url ?. profilePictureUrl ?. split ( '#' ) [ 0 ] . split ( '?' ) [ 0 ] . split ( '/' ) . pop ( ) || '' ;
647+ const chatwootProfilePictureFile = contact ?. thumbnail ?. split ( '#' ) [ 0 ] . split ( '?' ) [ 0 ] . split ( '/' ) . pop ( ) || '' ;
648+ const pictureNeedsUpdate = waProfilePictureFile !== chatwootProfilePictureFile ;
649+ const nameNeedsUpdate =
650+ ! contact . name ||
651+ contact . name === chatId ||
652+ ( `+${ chatId } ` . startsWith ( '+55' )
653+ ? this . getNumbers ( `+${ chatId } ` ) . some (
654+ ( v ) => contact . name === v || contact . name === v . substring ( 3 ) || contact . name === v . substring ( 1 ) ,
655+ )
656+ : false ) ;
657+
658+ this . logger . verbose ( `Picture needs update: ${ pictureNeedsUpdate } ` ) ;
659+ this . logger . verbose ( `Name needs update: ${ nameNeedsUpdate } ` ) ;
660+
661+ if ( pictureNeedsUpdate || nameNeedsUpdate ) {
662+ contact = await this . updateContact ( instance , contact . id , {
663+ ...( nameNeedsUpdate && { name : nameContact } ) ,
664+ ...( waProfilePictureFile === '' && { avatar : null } ) ,
665+ ...( pictureNeedsUpdate && { avatar_url : picture_url ?. profilePictureUrl } ) ,
666+ } ) ;
667+ }
623668 }
624669 } else {
625- await this . createContact (
670+ const jid = body . key . remoteJid ;
671+ contact = await this . createContact (
626672 instance ,
627- body . key . participant . split ( '@' ) [ 0 ] ,
673+ chatId ,
628674 filterInbox . id ,
629- false ,
630- body . pushName ,
675+ isGroup ,
676+ nameContact ,
631677 picture_url . profilePictureUrl || null ,
632- body . key . participant ,
678+ jid ,
633679 ) ;
634680 }
635- }
636-
637- const picture_url = await this . waMonitor . waInstances [ instance . instanceName ] . profilePicture ( chatId ) ;
638- this . logger . verbose ( `Contact profile picture URL: ${ JSON . stringify ( picture_url ) } ` ) ;
639-
640- let contact = await this . findContact ( instance , chatId ) ;
641- this . logger . verbose ( `Found contact: ${ JSON . stringify ( contact ) } ` ) ;
642681
643- if ( contact ) {
644- if ( ! body . key . fromMe ) {
645- const waProfilePictureFile =
646- picture_url ?. profilePictureUrl ?. split ( '#' ) [ 0 ] . split ( '?' ) [ 0 ] . split ( '/' ) . pop ( ) || '' ;
647- const chatwootProfilePictureFile = contact ?. thumbnail ?. split ( '#' ) [ 0 ] . split ( '?' ) [ 0 ] . split ( '/' ) . pop ( ) || '' ;
648- const pictureNeedsUpdate = waProfilePictureFile !== chatwootProfilePictureFile ;
649- const nameNeedsUpdate =
650- ! contact . name ||
651- contact . name === chatId ||
652- ( `+${ chatId } ` . startsWith ( '+55' )
653- ? this . getNumbers ( `+${ chatId } ` ) . some (
654- ( v ) => contact . name === v || contact . name === v . substring ( 3 ) || contact . name === v . substring ( 1 ) ,
655- )
656- : false ) ;
657-
658- this . logger . verbose ( `Picture needs update: ${ pictureNeedsUpdate } ` ) ;
659- this . logger . verbose ( `Name needs update: ${ nameNeedsUpdate } ` ) ;
660-
661- if ( pictureNeedsUpdate || nameNeedsUpdate ) {
662- contact = await this . updateContact ( instance , contact . id , {
663- ...( nameNeedsUpdate && { name : nameContact } ) ,
664- ...( waProfilePictureFile === '' && { avatar : null } ) ,
665- ...( pictureNeedsUpdate && { avatar_url : picture_url ?. profilePictureUrl } ) ,
666- } ) ;
667- }
682+ if ( ! contact ) {
683+ this . logger . warn ( `Contact not created or found` ) ;
684+ return null ;
668685 }
669- } else {
670- const jid = body . key . remoteJid ;
671- contact = await this . createContact (
672- instance ,
673- chatId ,
674- filterInbox . id ,
675- isGroup ,
676- nameContact ,
677- picture_url . profilePictureUrl || null ,
678- jid ,
679- ) ;
680- }
681-
682- if ( ! contact ) {
683- this . logger . warn ( 'Contact not created or found' ) ;
684- return null ;
685- }
686686
687- const contactId = contact ?. payload ?. id || contact ?. payload ?. contact ?. id || contact ?. id ;
688- this . logger . verbose ( `Contact ID: ${ contactId } ` ) ;
687+ const contactId = contact ?. payload ?. id || contact ?. payload ?. contact ?. id || contact ?. id ;
688+ this . logger . verbose ( `Contact ID: ${ contactId } ` ) ;
689689
690- const contactConversations = ( await client . contacts . listConversations ( {
691- accountId : this . provider . accountId ,
692- id : contactId ,
693- } ) ) as any ;
694- this . logger . verbose ( `Contact conversations: ${ JSON . stringify ( contactConversations ) } ` ) ;
690+ const contactConversations = ( await client . contacts . listConversations ( {
691+ accountId : this . provider . accountId ,
692+ id : contactId ,
693+ } ) ) as any ;
694+ this . logger . verbose ( `Contact conversations: ${ JSON . stringify ( contactConversations ) } ` ) ;
695695
696- if ( ! contactConversations || ! contactConversations . payload ) {
697- this . logger . error ( ' No conversations found or payload is undefined' ) ;
698- return null ;
699- }
696+ if ( ! contactConversations || ! contactConversations . payload ) {
697+ this . logger . error ( ` No conversations found or payload is undefined` ) ;
698+ return null ;
699+ }
700700
701- let inboxConversation = contactConversations . payload . find (
702- ( conversation ) => conversation . inbox_id == filterInbox . id ,
703- ) ;
704- if ( inboxConversation ) {
705- if ( this . provider . reopenConversation ) {
706- this . logger . verbose ( `Found conversation in reopenConversation mode: ${ JSON . stringify ( inboxConversation ) } ` ) ;
701+ let inboxConversation = contactConversations . payload . find (
702+ ( conversation ) => conversation . inbox_id == filterInbox . id ,
703+ ) ;
704+ if ( inboxConversation ) {
705+ if ( this . provider . reopenConversation ) {
706+ this . logger . verbose ( `Found conversation in reopenConversation mode: ${ JSON . stringify ( inboxConversation ) } ` ) ;
707+
708+ if ( this . provider . conversationPending && inboxConversation . status !== 'open' ) {
709+ await client . conversations . toggleStatus ( {
710+ accountId : this . provider . accountId ,
711+ conversationId : inboxConversation . id ,
712+ data : {
713+ status : 'pending' ,
714+ } ,
715+ } ) ;
716+ }
717+ } else {
718+ inboxConversation = contactConversations . payload . find (
719+ ( conversation ) => conversation . status !== 'resolved' && conversation . inbox_id == filterInbox . id ,
720+ ) ;
721+ this . logger . verbose ( `Found conversation: ${ JSON . stringify ( inboxConversation ) } ` ) ;
722+ }
707723
708- if ( this . provider . conversationPending && inboxConversation . status !== 'open' ) {
709- await client . conversations . toggleStatus ( {
710- accountId : this . provider . accountId ,
711- conversationId : inboxConversation . id ,
712- data : {
713- status : 'pending' ,
714- } ,
715- } ) ;
724+ if ( inboxConversation ) {
725+ this . logger . verbose ( `Returning existing conversation ID: ${ inboxConversation . id } ` ) ;
726+ this . cache . set ( cacheKey , inboxConversation . id ) ;
727+ return inboxConversation . id ;
716728 }
717- } else {
718- inboxConversation = contactConversations . payload . find (
719- ( conversation ) => conversation . status !== 'resolved' && conversation . inbox_id == filterInbox . id ,
720- ) ;
721- this . logger . verbose ( `Found conversation: ${ JSON . stringify ( inboxConversation ) } ` ) ;
722729 }
723730
724- if ( inboxConversation ) {
725- this . logger . verbose ( `Returning existing conversation ID: ${ inboxConversation . id } ` ) ;
726- this . cache . set ( cacheKey , inboxConversation . id ) ;
727- return inboxConversation . id ;
728- }
729- }
731+ const data = {
732+ contact_id : contactId . toString ( ) ,
733+ inbox_id : filterInbox . id . toString ( ) ,
734+ } ;
730735
731- const data = {
732- contact_id : contactId . toString ( ) ,
733- inbox_id : filterInbox . id . toString ( ) ,
734- } ;
736+ if ( this . provider . conversationPending ) {
737+ data [ 'status' ] = 'pending' ;
738+ }
735739
736- if ( this . provider . conversationPending ) {
737- data [ 'status' ] = 'pending' ;
738- }
740+ const conversation = await client . conversations . create ( {
741+ accountId : this . provider . accountId ,
742+ data,
743+ } ) ;
739744
740- const conversation = await client . conversations . create ( {
741- accountId : this . provider . accountId ,
742- data ,
743- } ) ;
745+ if ( ! conversation ) {
746+ this . logger . warn ( `Conversation not created or found` ) ;
747+ return null ;
748+ }
744749
745- if ( ! conversation ) {
746- this . logger . warn ( 'Conversation not created or found' ) ;
747- return null ;
750+ this . logger . verbose ( `New conversation created with ID: ${ conversation . id } ` ) ;
751+ this . cache . set ( cacheKey , conversation . id ) ;
752+ return conversation . id ;
753+ } finally {
754+ await this . cache . delete ( lockKey ) ;
755+ this . logger . verbose ( `Block released for: ${ lockKey } ` ) ;
748756 }
749-
750- this . logger . verbose ( `New conversation created with ID: ${ conversation . id } ` ) ;
751- this . cache . set ( cacheKey , conversation . id ) ;
752- return conversation . id ;
753757 } catch ( error ) {
754758 this . logger . error ( `Error in createConversation: ${ error } ` ) ;
759+ return null ;
755760 }
756761 }
757762
0 commit comments