feat: notifica a ADMIN_USERS al descubrir grupos (modo discover)

Co-authored-by: aider (openrouter/openai/gpt-5) <aider@aider.chat>
pull/1/head
borja 1 month ago
parent adad0a0609
commit c51cb3f124

@ -315,12 +315,34 @@ export class WebhookServer {
if (!exists) {
try { AllowedGroups.upsertPending(remoteJid, null, normalizedSenderId); } catch {}
try { Metrics.inc('unknown_groups_discovered_total'); } catch {}
try {
const notify = String(process.env.NOTIFY_ADMINS_ON_DISCOVERY || 'false').toLowerCase() === 'true';
if (notify && !isAdminCmd) {
const admins = AdminService.getAdmins();
if (admins.length > 0) {
const info = remoteJid;
const msg = `🔎 Nuevo grupo detectado: ${info}\nEstado: pending.\nUsa /admin habilitar-aquí desde el grupo o /admin allow-group ${info}.`;
await ResponseQueue.add(admins.map(a => ({ recipient: a, message: msg })));
}
}
} catch {}
if (!isAdminCmd) return;
}
} catch {
// Si la tabla no existe por alguna razón, intentar upsert y retornar igualmente
try { AllowedGroups.upsertPending(remoteJid, null, normalizedSenderId); } catch {}
try { Metrics.inc('unknown_groups_discovered_total'); } catch {}
try {
const notify = String(process.env.NOTIFY_ADMINS_ON_DISCOVERY || 'false').toLowerCase() === 'true';
if (notify && !isAdminCmd) {
const admins = AdminService.getAdmins();
if (admins.length > 0) {
const info = remoteJid;
const msg = `🔎 Nuevo grupo detectado: ${info}\nEstado: pending.\nUsa /admin habilitar-aquí desde el grupo o /admin allow-group ${info}.`;
await ResponseQueue.add(admins.map(a => ({ recipient: a, message: msg })));
}
}
} catch {}
if (!isAdminCmd) return;
}
}

@ -24,6 +24,10 @@ export class AdminService {
return set;
}
static getAdmins(): string[] {
return Array.from(this.admins());
}
private static isAdmin(userId: string | null | undefined): boolean {
const n = normalizeWhatsAppId(userId || '');
if (!n) return false;

@ -0,0 +1,91 @@
import { describe, test, expect, beforeAll, afterAll, beforeEach, afterEach } from 'bun:test';
import { Database } from 'bun:sqlite';
import { WebhookServer } from '../../../src/server';
import { initializeDatabase } from '../../../src/db';
import { ResponseQueue } from '../../../src/services/response-queue';
let testDb: Database;
let originalAdd: any;
let simulatedQueue: any[] = [];
const SimulatedResponseQueue = {
async add(responses: any[]) {
simulatedQueue.push(...responses);
},
clear() { simulatedQueue = []; },
get() { return simulatedQueue; }
};
const createTestRequest = (payload: any) =>
new Request('http://localhost:3007', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
describe('WebhookServer - notifica a ADMIN_USERS en descubrimiento (modo discover)', () => {
const envBackup = process.env;
beforeAll(() => {
testDb = new Database(':memory:');
initializeDatabase(testDb);
originalAdd = (ResponseQueue as any).add;
});
afterAll(() => {
(ResponseQueue as any).add = originalAdd;
testDb.close();
});
beforeEach(() => {
process.env = {
...envBackup,
NODE_ENV: 'test',
GROUP_GATING_MODE: 'discover',
NOTIFY_ADMINS_ON_DISCOVERY: 'true',
ADMIN_USERS: '1234567890, 5555555555'
};
SimulatedResponseQueue.clear();
(ResponseQueue as any).add = SimulatedResponseQueue.add;
WebhookServer.dbInstance = testDb;
// Limpiar tablas relevantes
testDb.exec('DELETE FROM response_queue');
testDb.exec('DELETE FROM allowed_groups');
testDb.exec('DELETE FROM users');
});
afterEach(() => {
process.env = envBackup;
});
test('registra pending y envía DMs a todos los admins', async () => {
const payload = {
event: 'messages.upsert',
instance: 'test-instance',
data: {
key: {
remoteJid: 'notify-group@g.us',
participant: '9999999999@s.whatsapp.net'
},
message: { conversation: '/t n hola' }
}
};
const res = await WebhookServer.handleRequest(createTestRequest(payload));
expect(res.status).toBe(200);
// allowed_groups debe tener el grupo en pending
const row = testDb
.query(`SELECT status FROM allowed_groups WHERE group_id = 'notify-group@g.us'`)
.get() as any;
expect(row).toBeDefined();
expect(String(row.status)).toBe('pending');
// Debe haberse encolado una notificación por cada admin
const out = SimulatedResponseQueue.get();
const recipients = out.map((r: any) => r.recipient).sort();
expect(out.length).toBe(2);
expect(recipients).toEqual(['1234567890', '5555555555']);
});
});
Loading…
Cancel
Save