From 228f2d40a2b65c7946acd6948ffd7b863e931910 Mon Sep 17 00:00:00 2001 From: brobert Date: Tue, 30 Sep 2025 00:00:13 +0200 Subject: [PATCH] feat: habilitar sync multicomunidad y persistir nombres de grupos Co-authored-by: aider (openrouter/openai/gpt-5) --- src/server.ts | 2 + src/services/group-sync.ts | 83 ++++++++++++++++++++++++-------------- 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/src/server.ts b/src/server.ts index 995bd5f..8d722ce 100644 --- a/src/server.ts +++ b/src/server.ts @@ -330,6 +330,7 @@ export class WebhookServer { .prepare(`SELECT 1 FROM allowed_groups WHERE group_id = ? LIMIT 1`) .get(remoteJid) as any; if (!exists) { + try { await GroupSyncService.ensureGroupLabelAndName(remoteJid); } catch {} try { AllowedGroups.upsertPending(remoteJid, (GroupSyncService.activeGroupsCache.get(remoteJid) || null), normalizedSenderId); } catch {} try { Metrics.inc('unknown_groups_discovered_total'); } catch {} try { @@ -347,6 +348,7 @@ export class WebhookServer { } } catch { // Si la tabla no existe por alguna razón, intentar upsert y retornar igualmente + try { await GroupSyncService.ensureGroupLabelAndName(remoteJid); } catch {} try { AllowedGroups.upsertPending(remoteJid, (GroupSyncService.activeGroupsCache.get(remoteJid) || null), normalizedSenderId); } catch {} try { Metrics.inc('unknown_groups_discovered_total'); } catch {} try { diff --git a/src/services/group-sync.ts b/src/services/group-sync.ts index 327b9f7..dd6f658 100644 --- a/src/services/group-sync.ts +++ b/src/services/group-sync.ts @@ -80,39 +80,14 @@ export class GroupSyncService { Metrics.inc('sync_runs_total'); try { - const communityId = process.env.WHATSAPP_COMMUNITY_ID; - if (!communityId) { - console.log('ℹ️ WHATSAPP_COMMUNITY_ID no definido - mostrando todas las comunidades'); - const groups = await this.fetchGroupsFromAPI(); - const communities = groups.filter(g => g.linkedParent); - // Intento best-effort de rellenar labels faltantes en allowed_groups usando la lista completa - try { (AllowedGroups as any).dbInstance = this.dbInstance; this.fillMissingAllowedGroupLabels(groups); } catch {} - return { added: 0, updated: 0 }; // No sync when just listing - } - const groups = await this.fetchGroupsFromAPI(); console.log('ℹ️ Grupos crudos de la API:', JSON.stringify(groups, null, 2)); - - const communityGroups = groups.filter((group) => { - const matches = group.linkedParent === communityId; - console.log(`ℹ️ Grupo ${group.id} (${group.subject}):`, { - linkedParent: group.linkedParent, - matchesCommunity: matches, - isCommunityItself: group.id === communityId - }); - return matches; - }); - - console.log('ℹ️ Grupos que pasaron el filtro:', communityGroups.map(g => ({ - id: g.id, - name: g.subject, - parent: g.linkedParent - }))); + console.log('ℹ️ Sin filtrar por comunidad (modo multicomunidad). Total grupos:', groups.length); const dbGroupsBefore = this.dbInstance.prepare('SELECT id, active FROM groups').all(); console.log('ℹ️ Grupos en DB antes de upsert:', dbGroupsBefore); - const result = await this.upsertGroups(communityGroups); + const result = await this.upsertGroups(groups); const dbGroupsAfter = this.dbInstance.prepare('SELECT id, active FROM groups').all(); console.log('ℹ️ Grupos en DB después de upsert:', dbGroupsAfter); @@ -368,14 +343,14 @@ export class GroupSyncService { if (existing) { const updateResult = this.dbInstance.prepare( - 'UPDATE groups SET name = ?, active = TRUE, last_verified = CURRENT_TIMESTAMP WHERE id = ?' - ).run(group.subject, group.id); + 'UPDATE groups SET name = ?, community_id = COALESCE(?, community_id), active = TRUE, last_verified = CURRENT_TIMESTAMP WHERE id = ?' + ).run(group.subject, group.linkedParent || null, group.id); console.log('Updated group:', group.id, 'result:', updateResult); updated++; } else { const insertResult = this.dbInstance.prepare( 'INSERT INTO groups (id, community_id, name, active) VALUES (?, ?, ?, TRUE)' - ).run(group.id, process.env.WHATSAPP_COMMUNITY_ID, group.subject); + ).run(group.id, group.linkedParent || null, group.subject); console.log('Added group:', group.id, 'result:', insertResult); added++; } @@ -860,6 +835,54 @@ export class GroupSyncService { return { created, reactivated, updatedName }; } + /** + * Asegura tener el nombre/label de un grupo (cache/DB/API) y lo persiste tanto en groups como en allowed_groups. + * Devuelve el nombre si se pudo resolver, o null en caso contrario. + */ + public static async ensureGroupLabelAndName(groupId: string): Promise { + try { + if (!groupId) return null; + + // 1) Cache en memoria + const cached = this.activeGroupsCache.get(groupId); + if (cached && cached.trim()) { + try { this.ensureGroupExists(groupId, cached); } catch {} + try { (AllowedGroups as any).dbInstance = this.dbInstance; AllowedGroups.upsertPending(groupId, cached, null); } catch {} + this.cacheActiveGroups(); + return cached; + } + + // 2) DB (tabla groups) + try { + const row = this.dbInstance.prepare('SELECT name FROM groups WHERE id = ?').get(groupId) as any; + const name = row?.name ? String(row.name).trim() : ''; + if (name) { + try { this.ensureGroupExists(groupId, name); } catch {} + try { (AllowedGroups as any).dbInstance = this.dbInstance; AllowedGroups.upsertPending(groupId, name, null); } catch {} + this.cacheActiveGroups(); + return name; + } + } catch {} + + // 3) API (evitar en tests) + if (process.env.NODE_ENV !== 'test') { + const groups = await this.fetchGroupsFromAPI(); + const g = groups.find((gg) => gg?.id === groupId); + const subject = g?.subject ? String(g.subject).trim() : ''; + if (subject) { + try { this.ensureGroupExists(groupId, subject); } catch {} + try { (AllowedGroups as any).dbInstance = this.dbInstance; AllowedGroups.upsertPending(groupId, subject, null); } catch {} + this.cacheActiveGroups(); + return subject; + } + } + + return null; + } catch { + return null; + } + } + /** * Sincroniza miembros para un grupo concreto (útil tras detectar un grupo nuevo). */