fix: stabilize telegram doctor config repairs

This commit is contained in:
Peter Steinberger
2026-04-06 20:55:08 +01:00
parent ed64ce3983
commit 48f2c2097d
4 changed files with 69 additions and 3 deletions

View File

@@ -220,6 +220,52 @@ describe("telegram doctor", () => {
expect(result.changes[0]).toContain("@testuser");
});
it("warns when @username entries cannot be resolved because configured tokens are unavailable", async () => {
resolveCommandSecretRefsViaGatewayMock.mockResolvedValueOnce({
resolvedConfig: {
channels: {
telegram: {
accounts: {
inactive: {
allowFrom: ["@testuser"],
},
},
},
},
},
diagnostics: [],
targetStatesByPath: {},
hadUnresolvedTargets: false,
});
listTelegramAccountIdsMock.mockReturnValue(["inactive"]);
inspectTelegramAccountMock.mockReturnValue({
enabled: false,
token: "",
tokenSource: "env",
tokenStatus: "configured_unavailable",
config: {},
});
const result = await maybeRepairTelegramAllowFromUsernames({
channels: {
telegram: {
accounts: {
inactive: {
botToken: { source: "env", provider: "default", id: "TELEGRAM_BOT_TOKEN" },
allowFrom: ["@testuser"],
},
},
},
},
} as unknown as OpenClawConfig);
expect(result.config.channels?.telegram?.accounts?.inactive?.allowFrom).toEqual(["@testuser"]);
expect(result.changes).toEqual([
"- Telegram account inactive: failed to inspect bot token (configured but unavailable in this command path).",
"- Telegram allowFrom contains @username entries, but configured Telegram bot credentials are unavailable in this command path; cannot auto-resolve.",
]);
});
it("formats username repair warnings", () => {
const warnings = collectTelegramAllowFromUsernameWarnings({
hits: [{ path: "channels.telegram.allowFrom", entry: "@top" }],

View File

@@ -163,6 +163,7 @@ export async function maybeRepairTelegramAllowFromUsernames(cfg: OpenClawConfig)
const tokenResolutionWarnings: string[] = [];
const resolverAccountIds: string[] = [];
let sawConfiguredUnavailableToken = false;
for (const accountId of listTelegramAccountIds(resolvedConfig)) {
let inspected: ReturnType<typeof inspectTelegramAccount>;
try {
@@ -174,6 +175,7 @@ export async function maybeRepairTelegramAllowFromUsernames(cfg: OpenClawConfig)
continue;
}
if (inspected.tokenStatus === "configured_unavailable") {
sawConfiguredUnavailableToken = true;
tokenResolutionWarnings.push(
`- Telegram account ${accountId}: failed to inspect bot token (configured but unavailable in this command path).`,
);
@@ -189,7 +191,9 @@ export async function maybeRepairTelegramAllowFromUsernames(cfg: OpenClawConfig)
config: cfg,
changes: [
...tokenResolutionWarnings,
"- Telegram allowFrom contains @username entries, but no Telegram bot token is available in this command path; cannot auto-resolve.",
sawConfiguredUnavailableToken
? "- Telegram allowFrom contains @username entries, but configured Telegram bot credentials are unavailable in this command path; cannot auto-resolve."
: "- Telegram allowFrom contains @username entries, but no Telegram bot token is available in this command path; cannot auto-resolve.",
],
};
}

View File

@@ -1,9 +1,10 @@
import fs from "node:fs/promises";
import path from "node:path";
import { describe, expect, it, vi } from "vitest";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { withTempHome } from "../../test/helpers/temp-home.js";
import { resolveMatrixAccountStorageRoot } from "../plugin-sdk/matrix.js";
import * as noteModule from "../terminal/note.js";
import { setChannelPluginRegistryForTests } from "./channel-test-registry.js";
import { loadAndMaybeMigrateDoctorConfig } from "./doctor-config-flow.js";
import { runDoctorConfigWithInput } from "./doctor-config-flow.test-utils.js";
@@ -57,6 +58,19 @@ type RepairedDiscordPolicy = {
};
describe("doctor config flow", () => {
beforeEach(() => {
setChannelPluginRegistryForTests([
"discord",
"googlechat",
"imessage",
"matrix",
"slack",
"telegram",
"whatsapp",
"zalouser",
]);
});
it("preserves invalid config for doctor repairs", async () => {
const result = await runDoctorConfigWithInput({
config: {

View File

@@ -42,7 +42,9 @@ export function applyLegacyCompatibilityStep(params: {
return {
state: {
cfg: params.shouldRepair ? migrated : params.state.cfg,
// Doctor should keep using the best-effort migrated shape in memory even
// during preview mode; confirmation only controls whether we write it.
cfg: migrated,
candidate: migrated,
pendingChanges: params.state.pendingChanges || changes.length > 0,
fixHints: params.shouldRepair