fix: stabilize gate and extension boundary checks

This commit is contained in:
Peter Steinberger
2026-03-30 02:37:18 +01:00
parent 66f8fb9e9b
commit fec51572a3
10 changed files with 183 additions and 116 deletions

View File

@@ -0,0 +1,39 @@
import type { ChannelThreadingToolContext } from "openclaw/plugin-sdk/channel-contract";
import { describe, expect, it } from "vitest";
import { resolveTelegramAutoThreadId } from "./action-threading.js";
function createToolContext(
overrides: Partial<ChannelThreadingToolContext> = {},
): ChannelThreadingToolContext {
return {
currentChannelId: "tg:group:-100123",
currentThreadTs: "thread-1",
replyToMode: "all",
...overrides,
};
}
describe("resolveTelegramAutoThreadId", () => {
it("matches chats across Telegram target formats", () => {
expect(
resolveTelegramAutoThreadId({
to: "telegram:group:-100123:topic:77",
toolContext: createToolContext(),
}),
).toBe("thread-1");
expect(
resolveTelegramAutoThreadId({
to: "-100999:77",
toolContext: createToolContext(),
}),
).toBeUndefined();
expect(
resolveTelegramAutoThreadId({
to: "-100123",
toolContext: createToolContext({ currentChannelId: undefined }),
}),
).toBeUndefined();
});
});

View File

@@ -295,6 +295,67 @@ describe("registerTelegramNativeCommands", () => {
);
});
it("forwards topic-scoped binding context to Telegram plugin commands", async () => {
const commandHandlers = new Map<string, (ctx: unknown) => Promise<void>>();
pluginCommandMocks.getPluginCommandSpecs.mockReturnValue([
{
name: "plug",
description: "Plugin command",
},
] as never);
pluginCommandMocks.matchPluginCommand.mockReturnValue({
command: { key: "plug", requireAuth: false },
args: undefined,
} as never);
pluginCommandMocks.executePluginCommand.mockResolvedValue({ text: "ok" } as never);
registerTelegramNativeCommands({
...createNativeCommandTestParams(
{},
{
bot: {
api: {
setMyCommands: vi.fn().mockResolvedValue(undefined),
sendMessage: vi.fn().mockResolvedValue(undefined),
},
command: vi.fn((name: string, cb: (ctx: unknown) => Promise<void>) => {
commandHandlers.set(name, cb);
}),
} as unknown as Parameters<typeof registerTelegramNativeCommands>[0]["bot"],
},
),
});
const handler = commandHandlers.get("plug");
expect(handler).toBeTruthy();
await handler?.({
match: "",
message: {
message_id: 2,
date: Math.floor(Date.now() / 1000),
chat: {
id: -1001234567890,
type: "supergroup",
title: "Forum Group",
is_forum: true,
},
message_thread_id: 77,
from: { id: 200, username: "bob" },
},
});
expect(pluginCommandMocks.executePluginCommand).toHaveBeenCalledWith(
expect.objectContaining({
channel: "telegram",
accountId: "default",
from: "telegram:group:-1001234567890:topic:77",
to: "telegram:-1001234567890",
messageThreadId: 77,
}),
);
});
it("treats Telegram forum #General commands as topic 1 when Telegram omits topic metadata", async () => {
const commandHandlers = new Map<string, (ctx: unknown) => Promise<void>>();
const getChat = vi.fn(async () => ({ id: -1001234567890, type: "supergroup", is_forum: true }));
@@ -348,9 +409,58 @@ describe("registerTelegramNativeCommands", () => {
expect(getChat).toHaveBeenCalledWith(-1001234567890);
expect(pluginCommandMocks.executePluginCommand).toHaveBeenCalledWith(
expect.objectContaining({
accountId: "default",
from: "telegram:group:-1001234567890:topic:1",
to: "telegram:-1001234567890",
messageThreadId: 1,
}),
);
});
it("forwards direct-message binding context to Telegram plugin commands", async () => {
const commandHandlers = new Map<string, (ctx: unknown) => Promise<void>>();
pluginCommandMocks.getPluginCommandSpecs.mockReturnValue([
{
name: "plug",
description: "Plugin command",
},
] as never);
pluginCommandMocks.matchPluginCommand.mockReturnValue({
command: { key: "plug", requireAuth: false },
args: undefined,
} as never);
pluginCommandMocks.executePluginCommand.mockResolvedValue({ text: "ok" } as never);
registerTelegramNativeCommands({
...createNativeCommandTestParams(
{},
{
bot: {
api: {
setMyCommands: vi.fn().mockResolvedValue(undefined),
sendMessage: vi.fn().mockResolvedValue(undefined),
},
command: vi.fn((name: string, cb: (ctx: unknown) => Promise<void>) => {
commandHandlers.set(name, cb);
}),
} as unknown as Parameters<typeof registerTelegramNativeCommands>[0]["bot"],
},
),
});
const handler = commandHandlers.get("plug");
expect(handler).toBeTruthy();
await handler?.(createPrivateCommandContext({ chatId: 100, userId: 200 }));
expect(pluginCommandMocks.executePluginCommand).toHaveBeenCalledWith(
expect.objectContaining({
channel: "telegram",
accountId: "default",
from: "telegram:100",
to: "telegram:100",
messageThreadId: undefined,
}),
);
});
});