fix(bot): drop removed groups during sync

Previously syncGroupsForAccount only upserted, so groups removed from
WhatsApp (deleted, bot was kicked, etc.) lingered in the DB.

Now compute the diff: any whatsapp_groups row for this account whose
wa_group_jid is not in the live fetch result is deleted. Skip the delete
sweep when the fetch returns empty — that's more likely transient than
a genuine "every group gone" signal, and we don't want to nuke valid
data on a hiccup.

Return shape gains a `removed` count alongside `synced`.
This commit is contained in:
yiekheng 2026-05-09 17:08:11 +08:00
parent 83d9bf6e9b
commit 9062ba7e7f

View File

@ -1,4 +1,4 @@
import { sql } from "drizzle-orm"; import { and, eq, notInArray, sql } from "drizzle-orm";
import type { WASocket } from "@whiskeysockets/baileys"; import type { WASocket } from "@whiskeysockets/baileys";
import { whatsappGroups } from "@cmbot/db"; import { whatsappGroups } from "@cmbot/db";
import { db } from "../db.js"; import { db } from "../db.js";
@ -7,13 +7,35 @@ import { logger } from "../logger.js";
export async function syncGroupsForAccount( export async function syncGroupsForAccount(
accountId: string, accountId: string,
socket: WASocket, socket: WASocket,
): Promise<{ synced: number }> { ): Promise<{ synced: number; removed: number }> {
const meta = await socket.groupFetchAllParticipating(); const meta = await socket.groupFetchAllParticipating();
const entries = Object.values(meta); 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) { if (entries.length === 0) {
logger.info({ accountId }, "group-sync: no groups"); logger.info(
return { synced: 0 }; { accountId },
"group-sync: empty fetch — skipping delete sweep (treating as transient)",
);
return { synced: 0, removed: 0 };
} }
const rows = entries.map((g) => ({ const rows = entries.map((g) => ({
@ -37,6 +59,9 @@ export async function syncGroupsForAccount(
}, },
}); });
logger.info({ accountId, count: rows.length }, "group-sync: synced"); logger.info(
return { synced: rows.length }; { accountId, count: rows.length, removed: removed.length },
"group-sync: synced",
);
return { synced: rows.length, removed: removed.length };
} }