From 5d4771e91e8660cb62d874cff792a20a75d1f9e7 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 9 Mar 2026 11:36:36 -0700 Subject: [PATCH] telegram: use dm thread hook session for plugin commands --- src/telegram/bot-native-commands.test.ts | 54 ++++++++++++++++++++++++ src/telegram/bot-native-commands.ts | 37 ++++++++++------ 2 files changed, 79 insertions(+), 12 deletions(-) diff --git a/src/telegram/bot-native-commands.test.ts b/src/telegram/bot-native-commands.test.ts index eea0937ad0e..5fbf46ed451 100644 --- a/src/telegram/bot-native-commands.test.ts +++ b/src/telegram/bot-native-commands.test.ts @@ -270,4 +270,58 @@ describe("registerTelegramNativeCommands", () => { ); expect(sendMessage).not.toHaveBeenCalledWith(123, "Command not found."); }); + + it("uses the DM thread session key for plugin command internal sent hooks", async () => { + const commandHandlers = new Map Promise>(); + + pluginCommandMocks.getPluginCommandSpecs.mockReturnValue([ + { + name: "plug", + description: "Plugin command", + }, + ] as never); + pluginCommandMocks.matchPluginCommand.mockReturnValue({ + command: { key: "plug", requireAuth: false }, + args: undefined, + } as never); + + registerTelegramNativeCommands({ + ...buildParams({ + channels: { + telegram: { + dmPolicy: "open", + }, + }, + }), + bot: { + api: { + setMyCommands: vi.fn().mockResolvedValue(undefined), + sendMessage: vi.fn().mockResolvedValue(undefined), + }, + command: vi.fn((name: string, cb: (ctx: unknown) => Promise) => { + commandHandlers.set(name, cb); + }), + } as unknown as Parameters[0]["bot"], + }); + + const handler = commandHandlers.get("plug"); + expect(handler).toBeTruthy(); + + await handler?.({ + match: "", + message: { + message_id: 42, + date: Math.floor(Date.now() / 1000), + chat: { id: 12345, type: "private" }, + from: { id: 12345, username: "alice" }, + message_thread_id: 99, + }, + }); + + expect(deliveryMocks.deliverReplies).toHaveBeenCalledWith( + expect.objectContaining({ + sessionKeyForInternalHooks: "agent:main:main:thread:12345:99", + }), + ); + }); }); diff --git a/src/telegram/bot-native-commands.ts b/src/telegram/bot-native-commands.ts index 17958daa289..351572c663a 100644 --- a/src/telegram/bot-native-commands.ts +++ b/src/telegram/bot-native-commands.ts @@ -540,6 +540,20 @@ export const registerTelegramNativeCommands = ({ chunkMode: params.chunkMode, linkPreview: telegramCfg.linkPreview, }); + const resolveDmThreadSessionKey = (params: { + baseSessionKey: string; + chatId: string | number; + threadSpec: ReturnType; + }): string => { + const dmThreadId = params.threadSpec.scope === "dm" ? params.threadSpec.id : undefined; + if (dmThreadId == null) { + return params.baseSessionKey; + } + return resolveThreadSessionKeys({ + baseSessionKey: params.baseSessionKey, + threadId: `${params.chatId}:${dmThreadId}`, + }).sessionKey; + }; if (commandsToRegister.length > 0 || pluginCatalog.commands.length > 0) { if (typeof (bot as unknown as { command?: unknown }).command !== "function") { @@ -647,17 +661,11 @@ export const registerTelegramNativeCommands = ({ }); return; } - const baseSessionKey = route.sessionKey; - // DMs: use raw messageThreadId for thread sessions (not resolvedThreadId which is for forums) - const dmThreadId = threadSpec.scope === "dm" ? threadSpec.id : undefined; - const threadKeys = - dmThreadId != null - ? resolveThreadSessionKeys({ - baseSessionKey, - threadId: `${chatId}:${dmThreadId}`, - }) - : null; - const sessionKey = threadKeys?.sessionKey ?? baseSessionKey; + const sessionKey = resolveDmThreadSessionKey({ + baseSessionKey: route.sessionKey, + chatId, + threadSpec, + }); const { skillFilter, groupSystemPrompt } = resolveTelegramGroupPromptSettings({ groupConfig, topicConfig, @@ -833,10 +841,15 @@ export const registerTelegramNativeCommands = ({ return; } const { threadSpec, route, mediaLocalRoots, tableMode, chunkMode } = runtimeContext; + const sessionKeyForInternalHooks = resolveDmThreadSessionKey({ + baseSessionKey: route.sessionKey, + chatId, + threadSpec, + }); const deliveryBaseOptions = buildCommandDeliveryBaseOptions({ chatId, accountId: route.accountId, - sessionKeyForInternalHooks: route.sessionKey, + sessionKeyForInternalHooks, mirrorIsGroup: isGroup, mirrorGroupId: isGroup ? String(chatId) : undefined, mediaLocalRoots,