From 1f3abeb6551e6cd413c6a5e4059444797eb6def1 Mon Sep 17 00:00:00 2001 From: shum Date: Sat, 12 Sep 2026 16:38:41 +0000 Subject: [PATCH] support bot: report unverifiable ids in dry run --- apps/simplex-support-bot/README.md | 4 +++- apps/simplex-support-bot/bot.test.ts | 10 ++++++---- apps/simplex-support-bot/src/dryRun.ts | 22 ++++++++++++++++------ 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/apps/simplex-support-bot/README.md b/apps/simplex-support-bot/README.md index c5f21ff97b..058446f334 100644 --- a/apps/simplex-support-bot/README.md +++ b/apps/simplex-support-bot/README.md @@ -139,7 +139,9 @@ FAIL database: 21 migration(s) pending, a real start would apply them: 20260507 `--allow-migrations` lets it apply them first (`applied 21 migration(s): …`) and then run the remaining checks — which makes it a migration rehearsal, so point it at a restored copy unless you mean to migrate for real. The bot must be stopped either way; migrating under a running instance is what breaks things, and the upgrade is one-way. -Exit code is 0 when every line is `ok`, 1 otherwise. It verifies the database connection, that no migration is pending, that the persisted team group and Grok user still exist (so neither would be recreated), that every `--broadcasters` / `--auto-add-team-members` id resolves to a contact with the expected display name, and that `--context-file` parses. +Lines are `ok`, `FAIL`, or `?`. A `?` means the check could not be made offline: `getChatPreviews` — the only chat query that works with the chat stopped — filters on `contacts.contact_used`, so a contact whose direct chat was never used is invisible here even though the bot finds it at startup. Those ids are reported, not failed. + +Exit code is 0 when no line is `FAIL`, 1 otherwise. It verifies the database connection, that no migration is pending, that the persisted team group and Grok user still exist (so neither would be recreated), that every `--broadcasters` / `--auto-add-team-members` id resolves to a contact with the expected display name, and that `--context-file` parses. ## Troubleshooting diff --git a/apps/simplex-support-bot/bot.test.ts b/apps/simplex-support-bot/bot.test.ts index fb8c2ee977..92eefb6722 100644 --- a/apps/simplex-support-bot/bot.test.ts +++ b/apps/simplex-support-bot/bot.test.ts @@ -2852,14 +2852,16 @@ describe("Dry Run", () => { expect(await dryRun(chat, config(), {})).toBe(true) }) - test("team group id not in database → fails", async () => { + // Absence is unverifiable with the chat stopped: getChatPreviews filters on + // contacts.contact_used, so these ids are reported, not failed. + test("team group id not in chat previews → reported, still passes", async () => { const chat = mkChat([groupChat(7, "Support Team")]) - expect(await dryRun(chat, config(), {teamGroupId: 1})).toBe(false) + expect(await dryRun(chat, config(), {teamGroupId: 1})).toBe(true) }) - test("broadcaster id missing → fails", async () => { + test("broadcaster id not in chat previews → reported, still passes", async () => { const chat = mkChat([groupChat(1, "Support Team")]) - expect(await dryRun(chat, config({broadcasters: [{id: 3, name: "alice"}]}), {teamGroupId: 1})).toBe(false) + expect(await dryRun(chat, config({broadcasters: [{id: 3, name: "alice"}]}), {teamGroupId: 1})).toBe(true) }) test("broadcaster name mismatch → fails even though the id exists", async () => { diff --git a/apps/simplex-support-bot/src/dryRun.ts b/apps/simplex-support-bot/src/dryRun.ts index 28c90fafee..2ef1db98f6 100644 --- a/apps/simplex-support-bot/src/dryRun.ts +++ b/apps/simplex-support-bot/src/dryRun.ts @@ -26,6 +26,10 @@ export async function dryRun(chat: api.ChatApi, config: Config, state: DryRunSta results.push(ok) console.log(`${ok ? "ok " : "FAIL"} ${msg}`) } + // Absence cannot be proven with the chat stopped: getChatPreviews filters on + // contacts.contact_used, so a contact whose direct chat was never used is + // invisible here even though the bot finds it at startup. + const unverified = (msg: string): void => console.log(`? ${msg}`) if (config.contextFile) { try { @@ -53,7 +57,11 @@ export async function dryRun(chat: api.ChatApi, config: Config, state: DryRunSta if (state.teamGroupId !== undefined) { const group = await findGroup(chat, user.userId, config.teamGroup.name, state.teamGroupId) - check(!!group, `team group ${state.teamGroupId}: ${group ? group.groupProfile.displayName : "not found, would be created"}`) + if (!group) { + unverified(`team group ${state.teamGroupId} not in chat previews, the bot verifies it at startup`) + } else { + check(true, `team group ${state.teamGroupId}: ${group.groupProfile.displayName}`) + } if (group && group.groupProfile.displayName !== config.teamGroup.name) { check(true, `team group name differs from --team-group, profile would be updated to "${config.teamGroup.name}"`) } @@ -63,11 +71,12 @@ export async function dryRun(chat: api.ChatApi, config: Config, state: DryRunSta if (state.grokContactId !== undefined) { const contact = await findContact(chat, user.userId, GROK_PROFILE_NAME, state.grokContactId) - check(!!contact, `Grok contact ${state.grokContactId}: ${contact ? contact.profile.displayName : "not found, would reconnect"}`) + if (contact) check(true, `Grok contact ${state.grokContactId}: ${contact.profile.displayName}`) + else unverified(`Grok contact ${state.grokContactId} not in chat previews, the bot verifies it at startup`) } - await checkContacts(chat, user.userId, "team member", config.teamMembers, check) - await checkContacts(chat, user.userId, "broadcaster", config.broadcasters, check) + await checkContacts(chat, user.userId, "team member", config.teamMembers, check, unverified) + await checkContacts(chat, user.userId, "broadcaster", config.broadcasters, check, unverified) return results.every(Boolean) } @@ -77,12 +86,13 @@ async function checkContacts( userId: number, role: string, contacts: IdName[], - check: (ok: boolean, msg: string) => void + check: (ok: boolean, msg: string) => void, + unverified: (msg: string) => void ): Promise { for (const {id, name} of contacts) { const contact = await findContact(chat, userId, name, id) if (!contact) { - check(false, `${role} ${id}:${name}: not found, the bot would exit`) + unverified(`${role} ${id}:${name} not in chat previews, the bot verifies it at startup`) } else { const match = contact.profile.displayName === name check(match, `${role} ${id}:${name}${match ? "" : ` has display name "${contact.profile.displayName}", the bot would exit`}`)