support bot: report unverifiable ids in dry run

This commit is contained in:
shum
2026-09-12 16:39:12 +00:00
parent 79de190012
commit 1f3abeb655
3 changed files with 25 additions and 11 deletions
+3 -1
View File
@@ -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
+6 -4
View File
@@ -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 () => {
+16 -6
View File
@@ -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<void> {
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`}`)