fix: keep Telegram native commands on runtime snapshot (#53179) (thanks @nimbleenigma)

* fix(telegram): use runtime snapshot for native commands

* fix: keep Telegram native commands on runtime snapshot (#53179) (thanks @nimbleenigma)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
This commit is contained in:
nimbleenigma
2026-03-25 01:48:54 -04:00
committed by GitHub
parent 57e2223eec
commit abec3ed645
3 changed files with 60 additions and 4 deletions

View File

@@ -16,6 +16,7 @@ import {
} from "openclaw/plugin-sdk/command-auth";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import type { ChannelGroupPolicy } from "openclaw/plugin-sdk/config-runtime";
import { getRuntimeConfigSnapshot } from "openclaw/plugin-sdk/config-runtime";
import { resolveMarkdownTableMode } from "openclaw/plugin-sdk/config-runtime";
import {
normalizeTelegramCommandName,
@@ -645,6 +646,7 @@ export const registerTelegramNativeCommands = ({
}
const { threadSpec, route, mediaLocalRoots, tableMode, chunkMode } = runtimeContext;
const threadParams = buildTelegramThreadParams(threadSpec) ?? {};
const executionCfg = getRuntimeConfigSnapshot() ?? cfg;
const commandDefinition = findCommandByNativeName(command.name, "telegram");
const rawText = ctx.match?.trim() ?? "";
@@ -774,7 +776,7 @@ export const registerTelegramNativeCommands = ({
});
await recordInboundSessionMetaSafe({
cfg: runtimeCfg,
cfg: executionCfg,
agentId: route.agentId,
sessionKey: ctxPayload.SessionKey ?? route.sessionKey,
ctx: ctxPayload,
@@ -794,7 +796,7 @@ export const registerTelegramNativeCommands = ({
};
const { onModelSelected, ...replyPipeline } = createChannelReplyPipeline({
cfg: runtimeCfg,
cfg: executionCfg,
agentId: route.agentId,
channel: "telegram",
accountId: route.accountId,
@@ -802,13 +804,13 @@ export const registerTelegramNativeCommands = ({
await telegramDeps.dispatchReplyWithBufferedBlockDispatcher({
ctx: ctxPayload,
cfg: runtimeCfg,
cfg: executionCfg,
dispatcherOptions: {
...replyPipeline,
deliver: async (payload, _info) => {
if (
shouldSuppressLocalTelegramExecApprovalPrompt({
cfg: runtimeCfg,
cfg: executionCfg,
accountId: route.accountId,
payload,
})

View File

@@ -1589,6 +1589,59 @@ describe("createTelegramBot", () => {
).toBe(false);
});
it("keeps native DM commands on the startup-resolved config when fresh reads contain SecretRefs", async () => {
onSpy.mockClear();
sendMessageSpy.mockClear();
commandSpy.mockClear();
replySpy.mockClear();
replySpy.mockResolvedValue({ text: "response" });
const startupConfig = {
commands: { native: true },
channels: {
telegram: {
dmPolicy: "pairing" as const,
botToken: "resolved-token",
},
},
};
createTelegramBot({
token: "tok",
config: startupConfig,
});
loadConfig.mockReturnValue({
commands: { native: true },
channels: {
telegram: {
dmPolicy: "pairing",
botToken: { source: "env", provider: "default", id: "TELEGRAM_BOT_TOKEN" },
},
},
});
readChannelAllowFromStore.mockResolvedValueOnce(["12345"]);
const handler = commandSpy.mock.calls.find((call) => call[0] === "status")?.[1] as
| ((ctx: Record<string, unknown>) => Promise<void>)
| undefined;
if (!handler) {
throw new Error("status command handler missing");
}
await handler({
message: {
chat: { id: 12345, type: "private" },
from: { id: 12345, username: "testuser" },
text: "/status",
date: 1736380800,
message_id: 42,
},
match: "",
});
expect(replySpy).toHaveBeenCalledTimes(1);
});
it("blocks native DM commands for unpaired users", async () => {
onSpy.mockClear();
sendMessageSpy.mockClear();