mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 12:30:22 +00:00
fix: preserve telegram exec approval topic routing
This commit is contained in:
@@ -57,6 +57,31 @@ describe("telegram native approval adapter", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("parses topic-scoped turn-source targets in the extension", async () => {
|
||||
const target = await telegramNativeApprovalAdapter.native?.resolveOriginTarget?.({
|
||||
cfg: buildConfig(),
|
||||
accountId: "default",
|
||||
approvalKind: "exec",
|
||||
request: {
|
||||
id: "req-topic-1",
|
||||
request: {
|
||||
command: "echo hi",
|
||||
turnSourceChannel: "telegram",
|
||||
turnSourceTo: "telegram:-1003841603622:topic:928",
|
||||
turnSourceAccountId: "default",
|
||||
sessionKey: "agent:main:telegram:group:-1003841603622:topic:928",
|
||||
},
|
||||
createdAtMs: 0,
|
||||
expiresAtMs: 1000,
|
||||
},
|
||||
});
|
||||
|
||||
expect(target).toEqual({
|
||||
to: "-1003841603622",
|
||||
threadId: 928,
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to the session-bound origin target for plugin approvals", async () => {
|
||||
writeStore({
|
||||
"agent:main:telegram:group:-1003841603622:topic:928": {
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
isTelegramExecApprovalClientEnabled,
|
||||
resolveTelegramExecApprovalTarget,
|
||||
} from "./exec-approvals.js";
|
||||
import { normalizeTelegramChatId } from "./targets.js";
|
||||
import { normalizeTelegramChatId, parseTelegramTarget } from "./targets.js";
|
||||
|
||||
type ApprovalRequest = ExecApprovalRequest | PluginApprovalRequest;
|
||||
type TelegramOriginTarget = { to: string; threadId?: number };
|
||||
@@ -22,15 +22,18 @@ function resolveTurnSourceTelegramOriginTarget(
|
||||
): TelegramOriginTarget | null {
|
||||
const turnSourceChannel = request.request.turnSourceChannel?.trim().toLowerCase() || "";
|
||||
const rawTurnSourceTo = request.request.turnSourceTo?.trim() || "";
|
||||
const turnSourceTo = normalizeTelegramChatId(rawTurnSourceTo) ?? rawTurnSourceTo;
|
||||
const parsedTurnSourceTarget = rawTurnSourceTo ? parseTelegramTarget(rawTurnSourceTo) : null;
|
||||
const turnSourceTo = normalizeTelegramChatId(parsedTurnSourceTarget?.chatId ?? rawTurnSourceTo);
|
||||
if (turnSourceChannel !== "telegram" || !turnSourceTo) {
|
||||
return null;
|
||||
}
|
||||
const rawThreadId =
|
||||
request.request.turnSourceThreadId ?? parsedTurnSourceTarget?.messageThreadId ?? undefined;
|
||||
const threadId =
|
||||
typeof request.request.turnSourceThreadId === "number"
|
||||
? request.request.turnSourceThreadId
|
||||
: typeof request.request.turnSourceThreadId === "string"
|
||||
? Number.parseInt(request.request.turnSourceThreadId, 10)
|
||||
typeof rawThreadId === "number"
|
||||
? rawThreadId
|
||||
: typeof rawThreadId === "string"
|
||||
? Number.parseInt(rawThreadId, 10)
|
||||
: undefined;
|
||||
return {
|
||||
to: turnSourceTo,
|
||||
|
||||
@@ -220,6 +220,42 @@ describe("telegramPlugin messaging", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("telegramPlugin threading", () => {
|
||||
it("keeps topic thread state in plugin-owned tool context", () => {
|
||||
expect(
|
||||
telegramPlugin.threading?.buildToolContext?.({
|
||||
cfg: {} as OpenClawConfig,
|
||||
accountId: "default",
|
||||
context: {
|
||||
To: "telegram:-1001:topic:77",
|
||||
MessageThreadId: 77,
|
||||
CurrentMessageId: "msg-1",
|
||||
},
|
||||
hasRepliedRef: { value: false },
|
||||
}),
|
||||
).toMatchObject({
|
||||
currentChannelId: "telegram:-1001:topic:77",
|
||||
currentThreadTs: "77",
|
||||
});
|
||||
});
|
||||
|
||||
it("parses topic thread state from target grammar when MessageThreadId is absent", () => {
|
||||
expect(
|
||||
telegramPlugin.threading?.buildToolContext?.({
|
||||
cfg: {} as OpenClawConfig,
|
||||
accountId: "default",
|
||||
context: {
|
||||
To: "telegram:-1001:topic:77",
|
||||
CurrentMessageId: "msg-1",
|
||||
},
|
||||
}),
|
||||
).toMatchObject({
|
||||
currentChannelId: "telegram:-1001:topic:77",
|
||||
currentThreadTs: "77",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("telegramPlugin duplicate token guard", () => {
|
||||
it("marks secondary account as not configured when token is shared", async () => {
|
||||
const cfg = createCfg();
|
||||
|
||||
@@ -88,6 +88,7 @@ import {
|
||||
setTelegramThreadBindingIdleTimeoutBySessionKey,
|
||||
setTelegramThreadBindingMaxAgeBySessionKey,
|
||||
} from "./thread-bindings.js";
|
||||
import { buildTelegramThreadingToolContext } from "./threading-tool-context.js";
|
||||
import { resolveTelegramToken } from "./token.js";
|
||||
|
||||
type TelegramSendFn = typeof sendMessageTelegram;
|
||||
@@ -825,6 +826,7 @@ export const telegramPlugin = createChatChannelPlugin({
|
||||
},
|
||||
threading: {
|
||||
topLevelReplyToMode: "telegram",
|
||||
buildToolContext: (params) => buildTelegramThreadingToolContext(params),
|
||||
resolveAutoThreadId: ({ to, toolContext, replyToId }) =>
|
||||
replyToId ? undefined : resolveTelegramAutoThreadId({ to, toolContext }),
|
||||
},
|
||||
|
||||
34
extensions/telegram/src/threading-tool-context.ts
Normal file
34
extensions/telegram/src/threading-tool-context.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type {
|
||||
ChannelThreadingContext,
|
||||
ChannelThreadingToolContext,
|
||||
} from "openclaw/plugin-sdk/channel-contract";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { parseTelegramTarget } from "./targets.js";
|
||||
|
||||
function resolveTelegramToolContextThreadId(context: ChannelThreadingContext): string | undefined {
|
||||
if (context.MessageThreadId != null) {
|
||||
return String(context.MessageThreadId);
|
||||
}
|
||||
const currentChannelId = context.To?.trim();
|
||||
if (!currentChannelId) {
|
||||
return undefined;
|
||||
}
|
||||
const parsedTarget = parseTelegramTarget(currentChannelId);
|
||||
return parsedTarget.messageThreadId != null ? String(parsedTarget.messageThreadId) : undefined;
|
||||
}
|
||||
|
||||
export function buildTelegramThreadingToolContext(params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
context: ChannelThreadingContext;
|
||||
hasRepliedRef?: { value: boolean };
|
||||
}): ChannelThreadingToolContext {
|
||||
void params.cfg;
|
||||
void params.accountId;
|
||||
|
||||
return {
|
||||
currentChannelId: params.context.To?.trim() || undefined,
|
||||
currentThreadTs: resolveTelegramToolContextThreadId(params.context),
|
||||
hasRepliedRef: params.hasRepliedRef,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user