From 0d29200dd7f18a2588abcf8031f84d5d52764e0c Mon Sep 17 00:00:00 2001 From: borja Date: Tue, 9 Sep 2025 10:42:04 +0200 Subject: [PATCH] feat: asegurar grupo al instante y sincronizar miembros ante eventos Co-authored-by: aider (openrouter/openai/gpt-5) --- src/server.ts | 14 +++++++--- src/services/group-sync.ts | 55 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/server.ts b/src/server.ts index 7bf3231..32a913e 100644 --- a/src/server.ts +++ b/src/server.ts @@ -270,12 +270,20 @@ export class WebhookServer { return; } - // Check if the group is active (allow DMs always) + // Check/ensure group exists (allow DMs always) if (isGroupId(data.key.remoteJid) && !GroupSyncService.isGroupActive(data.key.remoteJid)) { if (process.env.NODE_ENV !== 'test') { - console.log('⚠️ Group is not active, ignoring message'); + console.log('ℹ️ Group not active in cache — ensuring group and triggering quick members sync'); + } + try { + GroupSyncService.ensureGroupExists(data.key.remoteJid); + GroupSyncService.refreshActiveGroupsCache(); + await GroupSyncService.syncMembersForGroup(data.key.remoteJid); + } catch (e) { + if (process.env.NODE_ENV !== 'test') { + console.error('⚠️ Failed to ensure/sync group on-the-fly:', e); + } } - return; } // Forward to command service only if it's a text-ish message and starts with /t or /tarea diff --git a/src/services/group-sync.ts b/src/services/group-sync.ts index 534ae45..0991b3f 100644 --- a/src/services/group-sync.ts +++ b/src/services/group-sync.ts @@ -691,4 +691,59 @@ export class GroupSyncService { const gids = this.getActiveGroupIdsForUser(userId); return gids.filter(gid => this.isSnapshotFresh(gid)); } + + /** + * Asegura un registro de grupo activo en la base de datos (upsert idempotente). + * Si no existe, lo crea con active=1. Si existe y estaba inactivo, lo reactiva. + * Puede actualizar el nombre si se proporciona. + */ + public static ensureGroupExists(groupId: string, name?: string | null): { created: boolean; reactivated: boolean; updatedName: boolean } { + if (!groupId) return { created: false, reactivated: false, updatedName: false }; + let created = false, reactivated = false, updatedName = false; + + this.dbInstance.transaction(() => { + const row = this.dbInstance.prepare(`SELECT id, active, name FROM groups WHERE id = ?`).get(groupId) as any; + if (!row) { + const community = process.env.WHATSAPP_COMMUNITY_ID || ''; + this.dbInstance.prepare(` + INSERT INTO groups (id, community_id, name, active, last_verified) + VALUES (?, ?, ?, 1, CURRENT_TIMESTAMP) + `).run(groupId, community, name || null); + created = true; + } else { + // Reactivar si estaba inactivo y opcionalmente actualizar nombre + const shouldUpdateName = (typeof name === 'string' && name.trim().length > 0 && name !== row.name); + if (row.active !== 1 || shouldUpdateName) { + this.dbInstance.prepare(` + UPDATE groups + SET active = 1, + name = COALESCE(?, name), + last_verified = CURRENT_TIMESTAMP + WHERE id = ? + `).run(shouldUpdateName ? name : null, groupId); + reactivated = row.active !== 1; + updatedName = shouldUpdateName; + } + } + })(); + + // Actualizar caché + this.cacheActiveGroups(); + Metrics.set('active_groups', this.activeGroupsCache.size); + + return { created, reactivated, updatedName }; + } + + /** + * Sincroniza miembros para un grupo concreto (útil tras detectar un grupo nuevo). + */ + public static async syncMembersForGroup(groupId: string): Promise<{ added: number; updated: number; deactivated: number }> { + try { + const snapshot = await (this as any).fetchGroupMembersFromAPI(groupId); + return this.reconcileGroupMembers(groupId, snapshot); + } catch (e) { + console.error(`❌ Failed to sync members for group ${groupId}:`, e instanceof Error ? e.message : String(e)); + return { added: 0, updated: 0, deactivated: 0 }; + } + } }