@@ -263,6 +263,10 @@ export class BaileysStartupService extends ChannelStartupService {
263263 private isDeleting = false ; // Flag to prevent reconnection during deletion
264264 private logBaileys = this . configService . get < Log > ( 'LOG' ) . BAILEYS ;
265265 private eventProcessingQueue : Promise < void > = Promise . resolve ( ) ;
266+ private labelAssociationSnapshotCollector ?: {
267+ observedLabelState : boolean ;
268+ labelsByChatId : Map < string , Set < string > > ;
269+ } ;
266270 private _lastStream515At = 0 ;
267271
268272 // Cumulative history sync counters (reset on new sync or completion)
@@ -2046,6 +2050,9 @@ export class BaileysStartupService extends ChannelStartupService {
20462050
20472051 private readonly labelHandle = {
20482052 [ Events . LABELS_EDIT ] : async ( label : Label ) => {
2053+ if ( this . labelAssociationSnapshotCollector ) {
2054+ this . labelAssociationSnapshotCollector . observedLabelState = true ;
2055+ }
20492056 this . sendDataWebhook ( Events . LABELS_EDIT , { ...label , instance : this . instance . name } ) ;
20502057
20512058 const labelsRepository = await this . prismaRepository . label . findMany ( { where : { instanceId : this . instanceId } } ) ;
@@ -2090,6 +2097,8 @@ export class BaileysStartupService extends ChannelStartupService {
20902097 const chatId = data . association . chatId ;
20912098 const labelId = data . association . labelId ;
20922099
2100+ this . collectLabelAssociationSnapshot ( data ) ;
2101+
20932102 if ( data . type === 'add' ) {
20942103 await this . addLabel ( labelId , instanceId , chatId ) ;
20952104 } else if ( data . type === 'remove' ) {
@@ -2229,13 +2238,13 @@ export class BaileysStartupService extends ChannelStartupService {
22292238
22302239 if ( events [ Events . LABELS_ASSOCIATION ] ) {
22312240 const payload = events [ Events . LABELS_ASSOCIATION ] ;
2232- this . labelHandle [ Events . LABELS_ASSOCIATION ] ( payload , database ) ;
2241+ await this . labelHandle [ Events . LABELS_ASSOCIATION ] ( payload , database ) ;
22332242 return ;
22342243 }
22352244
22362245 if ( events [ Events . LABELS_EDIT ] ) {
22372246 const payload = events [ Events . LABELS_EDIT ] ;
2238- this . labelHandle [ Events . LABELS_EDIT ] ( payload ) ;
2247+ await this . labelHandle [ Events . LABELS_EDIT ] ( payload ) ;
22392248 return ;
22402249 }
22412250 }
@@ -4751,10 +4760,28 @@ export class BaileysStartupService extends ChannelStartupService {
47514760 }
47524761
47534762 public async syncLabels ( ) : Promise < LabelDto [ ] > {
4754- // Force Baileys to re-download label app state from WhatsApp (incremental)
4755- // Using true for isLatest = incremental sync (safe, no disconnect)
4756- // Using false would download full snapshot and may cause disconnection
4757- await this . client . resyncAppState ( [ 'regular' ] , true ) ;
4763+ const collector = {
4764+ observedLabelState : false ,
4765+ labelsByChatId : new Map < string , Set < string > > ( ) ,
4766+ } ;
4767+
4768+ this . labelAssociationSnapshotCollector = collector ;
4769+ try {
4770+ await this . instance . authState . state . keys . set ( { 'app-state-sync-version' : { regular : null } } ) ;
4771+ await this . client . resyncAppState ( [ 'regular' ] , true ) ;
4772+
4773+ await this . eventProcessingQueue . catch ( ( ) => undefined ) ;
4774+
4775+ if ( collector . observedLabelState ) {
4776+ await this . replaceChatLabelsFromSnapshot ( this . instanceId , collector . labelsByChatId ) ;
4777+ } else {
4778+ this . logger . warn (
4779+ 'labels sync snapshot finished without label state mutations; keeping existing chat label projection' ,
4780+ ) ;
4781+ }
4782+ } finally {
4783+ this . labelAssociationSnapshotCollector = undefined ;
4784+ }
47584785
47594786 // Wait for LABELS_EDIT and LABELS_ASSOCIATION events to be processed
47604787 await new Promise ( ( resolve ) => setTimeout ( resolve , 3000 ) ) ;
@@ -4763,6 +4790,65 @@ export class BaileysStartupService extends ChannelStartupService {
47634790 return this . fetchLabels ( ) ;
47644791 }
47654792
4793+ private collectLabelAssociationSnapshot ( data : { association : LabelAssociation ; type : 'remove' | 'add' } ) {
4794+ const collector = this . labelAssociationSnapshotCollector ;
4795+ if ( ! collector ) {
4796+ return ;
4797+ }
4798+
4799+ collector . observedLabelState = true ;
4800+ if ( data . association . type !== 'label_jid' ) {
4801+ return ;
4802+ }
4803+
4804+ const chatId = data . association . chatId ;
4805+ const labelId = data . association . labelId ;
4806+ const labels = collector . labelsByChatId . get ( chatId ) ?? new Set < string > ( ) ;
4807+
4808+ if ( data . type === 'add' ) {
4809+ labels . add ( labelId ) ;
4810+ } else {
4811+ labels . delete ( labelId ) ;
4812+ }
4813+
4814+ if ( labels . size ) {
4815+ collector . labelsByChatId . set ( chatId , labels ) ;
4816+ } else {
4817+ collector . labelsByChatId . delete ( chatId ) ;
4818+ }
4819+ }
4820+
4821+ private async replaceChatLabelsFromSnapshot ( instanceId : string , labelsByChatId : Map < string , Set < string > > ) {
4822+ await this . prismaRepository . $transaction ( async ( transaction ) => {
4823+ await transaction . chat . updateMany ( {
4824+ where : { instanceId } ,
4825+ data : { labels : [ ] } ,
4826+ } ) ;
4827+
4828+ for ( const [ chatId , labelSet ] of labelsByChatId ) {
4829+ const labels = [ ...labelSet ] . sort ( ) ;
4830+ if ( ! labels . length ) {
4831+ continue ;
4832+ }
4833+
4834+ await transaction . chat . upsert ( {
4835+ where : {
4836+ instanceId_remoteJid : {
4837+ instanceId,
4838+ remoteJid : chatId ,
4839+ } ,
4840+ } ,
4841+ update : { labels } ,
4842+ create : {
4843+ id : cuid ( ) ,
4844+ instanceId,
4845+ remoteJid : chatId ,
4846+ labels,
4847+ } ,
4848+ } ) ;
4849+ }
4850+ } ) ;
4851+ }
47664852
47674853 public async handleLabel ( data : HandleLabelDto ) {
47684854 const whatsappContact = await this . whatsappNumber ( { numbers : [ data . number ] } ) ;
0 commit comments