import { and, eq, notInArray, sql } from "drizzle-orm"; import type { WASocket } from "@whiskeysockets/baileys"; import { whatsappGroups } from "@cmbot/db"; import { db } from "../db.js"; import { logger } from "../logger.js"; export async function syncGroupsForAccount( accountId: string, socket: WASocket, ): Promise<{ synced: number; removed: number }> { const meta = await socket.groupFetchAllParticipating(); const entries = Object.values(meta); const liveJids = entries.map((g) => g.id); // Remove DB rows for groups that are no longer in the live participant list // (group was deleted, bot was removed, etc.). Only run the delete when we // got at least one live group back — an empty result is more likely a // transient WA fetch failure than a genuine "all groups gone" signal, and // we don't want to nuke valid data on a hiccup. let removed: { id: string }[] = []; if (liveJids.length > 0) { removed = await db .delete(whatsappGroups) .where( and( eq(whatsappGroups.accountId, accountId), notInArray(whatsappGroups.waGroupJid, liveJids), ), ) .returning({ id: whatsappGroups.id }); } if (entries.length === 0) { logger.info( { accountId }, "group-sync: empty fetch — skipping delete sweep (treating as transient)", ); return { synced: 0, removed: 0 }; } const rows = entries.map((g) => ({ accountId, waGroupJid: g.id, name: g.subject ?? "(no subject)", participantCount: g.participants?.length ?? 0, isArchived: false, lastSyncedAt: new Date(), })); await db .insert(whatsappGroups) .values(rows) .onConflictDoUpdate({ target: [whatsappGroups.accountId, whatsappGroups.waGroupJid], set: { name: sql`excluded.name`, participantCount: sql`excluded.participant_count`, lastSyncedAt: sql`excluded.last_synced_at`, }, }); logger.info( { accountId, count: rows.length, removed: removed.length }, "group-sync: synced", ); return { synced: rows.length, removed: removed.length }; }