From e7500a0961a779874bec1b407b3ec86e19f6b2f5 Mon Sep 17 00:00:00 2001 From: Nimrod Gutman Date: Sat, 14 Mar 2026 13:04:55 +0200 Subject: [PATCH] fix(telegram): unblock btw side questions --- .../telegram/src/sequential-key.test.ts | 14 ++++++++++ extensions/telegram/src/sequential-key.ts | 11 ++++++++ src/auto-reply/reply/btw-command.ts | 26 +++++++++++++++++++ src/auto-reply/reply/commands-btw.ts | 6 ++--- 4 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 src/auto-reply/reply/btw-command.ts diff --git a/extensions/telegram/src/sequential-key.test.ts b/extensions/telegram/src/sequential-key.test.ts index 7dc09af2596..d06e1c547a3 100644 --- a/extensions/telegram/src/sequential-key.test.ts +++ b/extensions/telegram/src/sequential-key.test.ts @@ -60,6 +60,20 @@ describe("getTelegramSequentialKey", () => { "telegram:123:control", ], [{ message: mockMessage({ chat: mockChat({ id: 123 }), text: "/status" }) }, "telegram:123"], + [ + { message: mockMessage({ chat: mockChat({ id: 123 }), text: "/btw what is the time?" }) }, + "telegram:123:btw:1", + ], + [ + { + me: { username: "openclaw_bot" } as never, + message: mockMessage({ + chat: mockChat({ id: 123 }), + text: "/btw@openclaw_bot what is the time?", + }), + }, + "telegram:123:btw:1", + ], [ { message: mockMessage({ chat: mockChat({ id: 123 }), text: "stop" }) }, "telegram:123:control", diff --git a/extensions/telegram/src/sequential-key.ts b/extensions/telegram/src/sequential-key.ts index 7bf22f5e8e1..334c18dc485 100644 --- a/extensions/telegram/src/sequential-key.ts +++ b/extensions/telegram/src/sequential-key.ts @@ -1,5 +1,6 @@ import { type Message, type UserFromGetMe } from "@grammyjs/types"; import { isAbortRequestText } from "../../../src/auto-reply/reply/abort.js"; +import { isBtwRequestText } from "../../../src/auto-reply/reply/btw-command.js"; import { resolveTelegramForumThreadId } from "./bot/helpers.js"; export type TelegramSequentialKeyContext = { @@ -41,6 +42,16 @@ export function getTelegramSequentialKey(ctx: TelegramSequentialKeyContext): str } return "telegram:control"; } + if (isBtwRequestText(rawText, botUsername ? { botUsername } : undefined)) { + const messageId = msg?.message_id; + if (typeof chatId === "number" && typeof messageId === "number") { + return `telegram:${chatId}:btw:${messageId}`; + } + if (typeof chatId === "number") { + return `telegram:${chatId}:btw`; + } + return "telegram:btw"; + } const isGroup = msg?.chat?.type === "group" || msg?.chat?.type === "supergroup"; const messageThreadId = msg?.message_thread_id; const isForum = msg?.chat?.is_forum; diff --git a/src/auto-reply/reply/btw-command.ts b/src/auto-reply/reply/btw-command.ts new file mode 100644 index 00000000000..6f1a5be76de --- /dev/null +++ b/src/auto-reply/reply/btw-command.ts @@ -0,0 +1,26 @@ +import { normalizeCommandBody, type CommandNormalizeOptions } from "../commands-registry.js"; + +const BTW_COMMAND_RE = /^\/btw(?::|\s|$)/i; + +export function isBtwRequestText(text?: string, options?: CommandNormalizeOptions): boolean { + if (!text) { + return false; + } + const normalized = normalizeCommandBody(text, options).trim(); + return BTW_COMMAND_RE.test(normalized); +} + +export function extractBtwQuestion( + text?: string, + options?: CommandNormalizeOptions, +): string | null { + if (!text) { + return null; + } + const normalized = normalizeCommandBody(text, options).trim(); + const match = normalized.match(/^\/btw(?:\s+(.*))?$/i); + if (!match) { + return null; + } + return match[1]?.trim() ?? ""; +} diff --git a/src/auto-reply/reply/commands-btw.ts b/src/auto-reply/reply/commands-btw.ts index 957435ed2e7..1e1c545fc8e 100644 --- a/src/auto-reply/reply/commands-btw.ts +++ b/src/auto-reply/reply/commands-btw.ts @@ -1,4 +1,5 @@ import { runBtwSideQuestion } from "../../agents/btw.js"; +import { extractBtwQuestion } from "./btw-command.js"; import { rejectUnauthorizedCommand } from "./command-gates.js"; import type { CommandHandler } from "./commands-types.js"; @@ -8,8 +9,8 @@ export const handleBtwCommand: CommandHandler = async (params, allowTextCommands if (!allowTextCommands) { return null; } - const match = params.command.commandBodyNormalized.match(/^\/btw(?:\s+(.*))?$/i); - if (!match) { + const question = extractBtwQuestion(params.command.commandBodyNormalized); + if (question === null) { return null; } const unauthorized = rejectUnauthorizedCommand(params, "/btw"); @@ -17,7 +18,6 @@ export const handleBtwCommand: CommandHandler = async (params, allowTextCommands return unauthorized; } - const question = match[1]?.trim() ?? ""; if (!question) { return { shouldContinue: false,