mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-12 13:11:10 +00:00
fix(telegram): pass parentPeer for forum topic binding inheritance (#9789)
Fixes #9545 and #9351. When a message comes from a Telegram forum topic, the peer ID includes the topic suffix (e.g., `-1001234567890:topic:99`). Users configure bindings with the base group ID, which previously did not match. This adds `parentPeer` to `resolveAgentRoute()` calls for forum groups, enabling binding inheritance from the parent group to all topics. - Extract `buildTelegramParentPeer()` helper in bot/helpers.ts - Pass parentPeer in bot-message-context.ts, bot-handlers.ts, bot-native-commands.ts, and bot.ts (reaction handler) - Add tests for forum topic routing and topic precedence
This commit is contained in:
@@ -331,6 +331,124 @@ describe("createTelegramBot", () => {
|
||||
|
||||
expect(replySpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
it("routes forum topic messages using parent group binding", async () => {
|
||||
onSpy.mockReset();
|
||||
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
||||
replySpy.mockReset();
|
||||
|
||||
// Binding specifies the base group ID without topic suffix.
|
||||
// The fix passes parentPeer to resolveAgentRoute so the binding matches
|
||||
// even when the actual peer id includes the topic suffix.
|
||||
loadConfig.mockReturnValue({
|
||||
channels: {
|
||||
telegram: {
|
||||
groupPolicy: "open",
|
||||
groups: { "*": { requireMention: false } },
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
list: [{ id: "forum-agent" }],
|
||||
},
|
||||
bindings: [
|
||||
{
|
||||
agentId: "forum-agent",
|
||||
match: {
|
||||
channel: "telegram",
|
||||
peer: { kind: "group", id: "-1001234567890" },
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
createTelegramBot({ token: "tok" });
|
||||
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
||||
|
||||
// Message comes from a forum topic (has message_thread_id and is_forum=true)
|
||||
await handler({
|
||||
message: {
|
||||
chat: {
|
||||
id: -1001234567890,
|
||||
type: "supergroup",
|
||||
title: "Forum Group",
|
||||
is_forum: true,
|
||||
},
|
||||
text: "hello from topic",
|
||||
date: 1736380800,
|
||||
message_id: 42,
|
||||
message_thread_id: 99,
|
||||
},
|
||||
me: { username: "openclaw_bot" },
|
||||
getFile: async () => ({ download: async () => new Uint8Array() }),
|
||||
});
|
||||
|
||||
expect(replySpy).toHaveBeenCalledTimes(1);
|
||||
const payload = replySpy.mock.calls[0][0];
|
||||
// Should route to forum-agent via parent peer binding inheritance
|
||||
expect(payload.SessionKey).toContain("agent:forum-agent:");
|
||||
});
|
||||
|
||||
it("prefers specific topic binding over parent group binding", async () => {
|
||||
onSpy.mockReset();
|
||||
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
||||
replySpy.mockReset();
|
||||
|
||||
// Both a specific topic binding and a parent group binding are configured.
|
||||
// The specific topic binding should take precedence.
|
||||
loadConfig.mockReturnValue({
|
||||
channels: {
|
||||
telegram: {
|
||||
groupPolicy: "open",
|
||||
groups: { "*": { requireMention: false } },
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
list: [{ id: "topic-agent" }, { id: "group-agent" }],
|
||||
},
|
||||
bindings: [
|
||||
{
|
||||
agentId: "topic-agent",
|
||||
match: {
|
||||
channel: "telegram",
|
||||
peer: { kind: "group", id: "-1001234567890:topic:99" },
|
||||
},
|
||||
},
|
||||
{
|
||||
agentId: "group-agent",
|
||||
match: {
|
||||
channel: "telegram",
|
||||
peer: { kind: "group", id: "-1001234567890" },
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
createTelegramBot({ token: "tok" });
|
||||
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
|
||||
|
||||
// Message from topic 99 - should match the specific topic binding
|
||||
await handler({
|
||||
message: {
|
||||
chat: {
|
||||
id: -1001234567890,
|
||||
type: "supergroup",
|
||||
title: "Forum Group",
|
||||
is_forum: true,
|
||||
},
|
||||
text: "hello from topic 99",
|
||||
date: 1736380800,
|
||||
message_id: 42,
|
||||
message_thread_id: 99,
|
||||
},
|
||||
me: { username: "openclaw_bot" },
|
||||
getFile: async () => ({ download: async () => new Uint8Array() }),
|
||||
});
|
||||
|
||||
expect(replySpy).toHaveBeenCalledTimes(1);
|
||||
const payload = replySpy.mock.calls[0][0];
|
||||
// Should route to topic-agent (exact match) not group-agent (parent)
|
||||
expect(payload.SessionKey).toContain("agent:topic-agent:");
|
||||
});
|
||||
|
||||
it("sends GIF replies as animations", async () => {
|
||||
onSpy.mockReset();
|
||||
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
|
||||
|
||||
Reference in New Issue
Block a user