feat: asegurar grupo al instante y sincronizar miembros ante eventos

Co-authored-by: aider (openrouter/openai/gpt-5) <aider@aider.chat>
pull/1/head
borja 2 months ago
parent e8e319afb0
commit 0d29200dd7

@ -270,12 +270,20 @@ export class WebhookServer {
return; 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 (isGroupId(data.key.remoteJid) && !GroupSyncService.isGroupActive(data.key.remoteJid)) {
if (process.env.NODE_ENV !== 'test') { 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 // Forward to command service only if it's a text-ish message and starts with /t or /tarea

@ -691,4 +691,59 @@ export class GroupSyncService {
const gids = this.getActiveGroupIdsForUser(userId); const gids = this.getActiveGroupIdsForUser(userId);
return gids.filter(gid => this.isSnapshotFresh(gid)); 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 };
}
}
} }

Loading…
Cancel
Save