feat: permitir forzar sync de grupos y completar labels faltantes

Co-authored-by: aider (openrouter/openai/gpt-5) <aider@aider.chat>
main
brobert 1 month ago
parent abfa9b73a7
commit 46d172fc12

@ -1,6 +1,7 @@
import type { Database } from 'bun:sqlite'; import type { Database } from 'bun:sqlite';
import { db } from '../db'; import { db } from '../db';
import { AllowedGroups } from './allowed-groups'; import { AllowedGroups } from './allowed-groups';
import { GroupSyncService } from './group-sync';
import { normalizeWhatsAppId, isGroupId } from '../utils/whatsapp'; import { normalizeWhatsAppId, isGroupId } from '../utils/whatsapp';
import { Metrics } from './metrics'; import { Metrics } from './metrics';
@ -43,6 +44,7 @@ export class AdminService {
'- /admin deshabilitar-aquí (alias: disable)', '- /admin deshabilitar-aquí (alias: disable)',
'- /admin allow-group <group_id@g.us> (alias: allow)', '- /admin allow-group <group_id@g.us> (alias: allow)',
'- /admin block-group <group_id@g.us> (alias: block)', '- /admin block-group <group_id@g.us> (alias: block)',
'- /admin sync-grupos (alias: group-sync, syncgroups)',
].join('\n'); ].join('\n');
} }
@ -120,6 +122,18 @@ export class AdminService {
return [{ recipient: sender, message: `✅ Grupo bloqueado: ${arg}` }]; return [{ recipient: sender, message: `✅ Grupo bloqueado: ${arg}` }];
} }
// /admin sync-grupos
if (rest === 'sync-grupos' || rest === 'group-sync' || rest === 'syncgroups') {
try { (GroupSyncService as any).dbInstance = this.dbInstance; } catch {}
try {
const r = await GroupSyncService.syncGroups(true);
return [{ recipient: sender, message: `✅ Sync de grupos ejecutado: ${r.added} añadidos, ${r.updated} actualizados.` }];
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
return [{ recipient: sender, message: `❌ Error al ejecutar sync de grupos: ${msg}` }];
}
}
// Ayuda por defecto // Ayuda por defecto
return [{ recipient: sender, message: this.help() }]; return [{ recipient: sender, message: this.help() }];
} }

@ -72,8 +72,8 @@ export class GroupSyncService {
private static _membersTimer: any = null; private static _membersTimer: any = null;
private static _membersSchedulerRunning = false; private static _membersSchedulerRunning = false;
static async syncGroups(): Promise<{ added: number; updated: number }> { static async syncGroups(force: boolean = false): Promise<{ added: number; updated: number }> {
if (!this.shouldSync()) { if (!this.shouldSync(force)) {
return { added: 0, updated: 0 }; return { added: 0, updated: 0 };
} }
const startedAt = Date.now(); const startedAt = Date.now();
@ -85,6 +85,8 @@ export class GroupSyncService {
console.log(' WHATSAPP_COMMUNITY_ID no definido - mostrando todas las comunidades'); console.log(' WHATSAPP_COMMUNITY_ID no definido - mostrando todas las comunidades');
const groups = await this.fetchGroupsFromAPI(); const groups = await this.fetchGroupsFromAPI();
const communities = groups.filter(g => g.linkedParent); 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 return { added: 0, updated: 0 }; // No sync when just listing
} }
@ -115,6 +117,9 @@ export class GroupSyncService {
const dbGroupsAfter = this.dbInstance.prepare('SELECT id, active FROM groups').all(); const dbGroupsAfter = this.dbInstance.prepare('SELECT id, active FROM groups').all();
console.log(' Grupos en DB después de upsert:', dbGroupsAfter); console.log(' Grupos en DB después de upsert:', dbGroupsAfter);
// Completar labels faltantes en allowed_groups usando todos los grupos devueltos por la API
try { (AllowedGroups as any).dbInstance = this.dbInstance; this.fillMissingAllowedGroupLabels(groups); } catch {}
// Actualizar métricas // Actualizar métricas
this.cacheActiveGroups(); this.cacheActiveGroups();
Metrics.set('active_groups', this.activeGroupsCache.size); Metrics.set('active_groups', this.activeGroupsCache.size);
@ -136,7 +141,8 @@ export class GroupSyncService {
} }
} }
private static shouldSync(): boolean { private static shouldSync(force: boolean = false): boolean {
if (force) return true;
const timeSinceLastSync = Date.now() - this.lastSyncAttempt; const timeSinceLastSync = Date.now() - this.lastSyncAttempt;
const shouldSync = timeSinceLastSync > this.SYNC_INTERVAL_MS; const shouldSync = timeSinceLastSync > this.SYNC_INTERVAL_MS;
@ -230,6 +236,47 @@ export class GroupSyncService {
console.log(`Cached ${this.activeGroupsCache.size} active groups`); console.log(`Cached ${this.activeGroupsCache.size} active groups`);
} }
// Rellena labels faltantes en allowed_groups a partir de los grupos devueltos por la API.
private static fillMissingAllowedGroupLabels(allGroups: EvolutionGroup[]): number {
try {
if (!Array.isArray(allGroups) || allGroups.length === 0) return 0;
const nameById = new Map<string, string>();
for (const g of allGroups) {
if (!g?.id) continue;
const name = String(g.subject || '').trim();
if (!name) continue;
nameById.set(String(g.id), name);
}
if (nameById.size === 0) return 0;
const rows = this.dbInstance.prepare(`
SELECT group_id AS id
FROM allowed_groups
WHERE label IS NULL OR TRIM(label) = ''
`).all() as any[];
if (!rows || rows.length === 0) return 0;
let filled = 0;
for (const r of rows) {
const id = r?.id ? String(r.id) : null;
if (!id) continue;
const label = nameById.get(id);
if (label) {
try { (AllowedGroups as any).dbInstance = this.dbInstance; AllowedGroups.upsertPending(id, label, null); } catch {}
filled++;
}
}
if (filled > 0) {
try { Metrics.inc('allowed_groups_labels_filled_total', filled); } catch {}
console.log(` Rellenadas ${filled} labels faltantes en allowed_groups`);
}
return filled;
} catch (e) {
console.warn('⚠️ No se pudieron rellenar labels faltantes en allowed_groups:', e);
return 0;
}
}
private static getActiveGroupsCount(): number { private static getActiveGroupsCount(): number {
const result = this.dbInstance.prepare('SELECT COUNT(*) as count FROM groups WHERE active = TRUE').get(); const result = this.dbInstance.prepare('SELECT COUNT(*) as count FROM groups WHERE active = TRUE').get();
return result?.count || 0; return result?.count || 0;

Loading…
Cancel
Save