yiekheng 1e3173424a fix(bot): pin Baileys to latest WA Web version + handle smart quotes
Two pairing-flow fixes after live test:
- Connection Failure during pairing: Baileys announced a stale WhatsApp Web
  version that the server rejected before the QR was emitted. Pull the
  current version via fetchLatestBaileysVersion() at session start.
- Telegram mobile auto-converts straight quotes to curly quotes, so labels
  like /pair "test 1" arrived as “test 1” and the curly quotes were never
  stripped. Extend the quote-stripping regex on /pair, /unpair, /groups.
2026-05-09 16:28:01 +08:00

57 lines
1.7 KiB
TypeScript

import type { Context } from "grammy";
import { rm } from "node:fs/promises";
import { join } from "node:path";
import { eq } from "drizzle-orm";
import { whatsappAccounts } from "@cmbot/db";
import { db } from "../../db.js";
import { env } from "../../env.js";
import { sessionManager } from "../../whatsapp/session-manager.js";
import { writeAuditLog } from "../../audit.js";
export async function handleUnpair(ctx: Context): Promise<void> {
const text = ctx.message?.text ?? "";
const label = text
.replace(/^\/unpair\s*/, "")
.trim()
.replace(/^["'“”‘’]|["'“”‘’]$/g, "");
if (!label) {
await ctx.reply('Usage: /unpair "Account Label"');
return;
}
const operatorId = ctx.from?.id;
if (!operatorId) return;
const operatorRow = await db.query.operators.findFirst({
where: (o, { eq }) => eq(o.telegramUserId, operatorId),
});
if (!operatorRow) return;
const account = await db.query.whatsappAccounts.findFirst({
where: (a, { eq, and }) => and(eq(a.operatorId, operatorRow.id), eq(a.label, label)),
});
if (!account) {
await ctx.reply(`No account labelled "${label}".`);
return;
}
await sessionManager.stop(account.id);
await rm(join(env.SESSIONS_DIR, account.id), { recursive: true, force: true });
await db
.update(whatsappAccounts)
.set({ status: "logged_out", phoneNumber: null })
.where(eq(whatsappAccounts.id, account.id));
await writeAuditLog(db, {
operatorId: operatorRow.id,
source: "telegram",
action: "account.unpaired",
targetType: "whatsapp_account",
targetId: account.id,
payload: { label },
});
await ctx.reply(`🗑 "${label}" unpaired. Session files deleted.`);
}