mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-26 16:06:16 +00:00
fix: scope Telegram pairing code blocks (#52784) (thanks @sumukhj1219)
* Telegram: format pairing challenge for easier copy * test: restore Telegram pairing chatId assertion * fix: scope Telegram pairing code blocks (#52784) (thanks @sumukhj1219) --------- Co-authored-by: Ayaan Zaidi <hi@obviy.us>
This commit is contained in:
@@ -51,6 +51,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Feishu/startup: keep `requireMention` enforcement strict when bot identity startup probes fail, raise the startup bot-info timeout to 30s, and add cancellable background identity recovery so mention-gated groups recover without noisy fallback. (#43788) Thanks @lefarcen.
|
||||
- Plugins: enforce terminal hook decision semantics for tool/message guards (#54241) Thanks @joshavant.
|
||||
- Telegram/outbound errors: preserve actionable 403 membership/block/kick details and treat `bot not a member` as a permanent delivery failure so Telegram sends stop retrying doomed chats. (#53635) Thanks @w-sss.
|
||||
- Telegram/pairing: render pairing codes and approval commands as Telegram-only code blocks while keeping shared pairing replies plain text for other channels. (#52784) Thanks @sumukhj1219.
|
||||
|
||||
## 2026.3.23
|
||||
|
||||
|
||||
@@ -500,10 +500,10 @@ describe("createTelegramBot", () => {
|
||||
const pairingText = String(sendMessageSpy.mock.calls[0]?.[1]);
|
||||
expect(pairingText, testCase.name).toContain(`Your Telegram user id: ${senderId}`);
|
||||
expect(pairingText, testCase.name).toContain("Pairing code:");
|
||||
const code = pairingText.match(/Pairing code:\s*([A-Z2-9]{8})/)?.[1];
|
||||
expect(code, testCase.name).toBeDefined();
|
||||
expect(pairingText, testCase.name).toContain(`openclaw pairing approve telegram ${code}`);
|
||||
expect(pairingText, testCase.name).not.toContain("<code>");
|
||||
expect(pairingText, testCase.name).toContain("openclaw pairing approve telegram");
|
||||
expect(sendMessageSpy.mock.calls[0]?.[2], testCase.name).toEqual(
|
||||
expect.objectContaining({ parse_mode: "HTML" }),
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -546,7 +546,12 @@ describe("createTelegramBot", () => {
|
||||
expect(getFileSpy).not.toHaveBeenCalled();
|
||||
expect(fetchSpy).not.toHaveBeenCalled();
|
||||
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
|
||||
expect(String(sendMessageSpy.mock.calls[0]?.[1])).toContain("Pairing code:");
|
||||
const pairingText = String(sendMessageSpy.mock.calls[0]?.[1]);
|
||||
expect(pairingText).toContain("Pairing code:");
|
||||
expect(pairingText).toContain("<pre><code>");
|
||||
expect(sendMessageSpy.mock.calls[0]?.[2]).toEqual(
|
||||
expect.objectContaining({ parse_mode: "HTML" }),
|
||||
);
|
||||
expect(replySpy).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
fetchSpy.mockRestore();
|
||||
@@ -633,7 +638,12 @@ describe("createTelegramBot", () => {
|
||||
expect(getFileSpy).not.toHaveBeenCalled();
|
||||
expect(fetchSpy).not.toHaveBeenCalled();
|
||||
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
|
||||
expect(String(sendMessageSpy.mock.calls[0]?.[1])).toContain("Pairing code:");
|
||||
const pairingText = String(sendMessageSpy.mock.calls[0]?.[1]);
|
||||
expect(pairingText).toContain("Pairing code:");
|
||||
expect(pairingText).toContain("<pre><code>");
|
||||
expect(sendMessageSpy.mock.calls[0]?.[2]).toEqual(
|
||||
expect.objectContaining({ parse_mode: "HTML" }),
|
||||
);
|
||||
expect(replySpy).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
fetchSpy.mockRestore();
|
||||
|
||||
@@ -119,8 +119,12 @@ describe("enforceTelegramDmAccess", () => {
|
||||
});
|
||||
|
||||
expect(allowed).toBe(false);
|
||||
expect(createChannelPairingChallengeIssuerMock).toHaveBeenCalledTimes(1);
|
||||
expect(sendMessage).toHaveBeenCalledWith(42, "Pairing code: 123456");
|
||||
expect(sendMessage).toHaveBeenCalledTimes(1);
|
||||
const [firstCall] = sendMessage.mock.calls as Array<unknown[]>;
|
||||
expect(firstCall?.[0]).toBe(42);
|
||||
const sentText = String(firstCall?.[1] ?? "");
|
||||
expect(sentText).toContain("Pairing code:");
|
||||
expect(firstCall?.[2]).toEqual(expect.objectContaining({ parse_mode: "HTML" }));
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
chatId: "42",
|
||||
|
||||
@@ -6,6 +6,7 @@ import { upsertChannelPairingRequest } from "openclaw/plugin-sdk/conversation-ru
|
||||
import { logVerbose } from "openclaw/plugin-sdk/runtime-env";
|
||||
import { withTelegramApiErrorLogging } from "./api-logging.js";
|
||||
import { resolveSenderAllowMatch, type NormalizedAllowFrom } from "./bot-access.js";
|
||||
import { renderTelegramHtmlText } from "./format.js";
|
||||
|
||||
type TelegramDmAccessLogger = {
|
||||
info: (obj: Record<string, unknown>, msg: string) => void;
|
||||
@@ -113,9 +114,10 @@ export async function enforceTelegramDmAccess(params: {
|
||||
);
|
||||
},
|
||||
sendPairingReply: async (text) => {
|
||||
const html = renderTelegramHtmlText(text);
|
||||
await withTelegramApiErrorLogging({
|
||||
operation: "sendMessage",
|
||||
fn: () => bot.api.sendMessage(chatId, text),
|
||||
fn: () => bot.api.sendMessage(chatId, html, { parse_mode: "HTML" }),
|
||||
});
|
||||
},
|
||||
onReplyError: (err) => {
|
||||
|
||||
@@ -52,12 +52,14 @@ describe("buildPairingReply", () => {
|
||||
it(`formats pairing reply for ${testCase.channel}`, () => {
|
||||
const text = buildPairingReply(testCase);
|
||||
expect(text).toContain(testCase.idLine);
|
||||
expect(text).toContain(`Pairing code: ${testCase.code}`);
|
||||
expect(text).toContain("Pairing code:");
|
||||
expect(text).toContain(`\n\`\`\`\n${testCase.code}\n\`\`\`\n`);
|
||||
// CLI commands should respect OPENCLAW_PROFILE when set (most tests run with isolated profile)
|
||||
const commandRe = new RegExp(
|
||||
`(?:openclaw|openclaw) --profile isolated pairing approve ${testCase.channel} ${testCase.code}`,
|
||||
);
|
||||
expect(text).toMatch(commandRe);
|
||||
expect(text).toContain("\n```\n");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -7,14 +7,20 @@ export function buildPairingReply(params: {
|
||||
code: string;
|
||||
}): string {
|
||||
const { channel, idLine, code } = params;
|
||||
const approveCommand = formatCliCommand(`openclaw pairing approve ${channel} ${code}`);
|
||||
return [
|
||||
"OpenClaw: access not configured.",
|
||||
"",
|
||||
idLine,
|
||||
"",
|
||||
`Pairing code: ${code}`,
|
||||
"Pairing code:",
|
||||
"```",
|
||||
code,
|
||||
"```",
|
||||
"",
|
||||
"Ask the bot owner to approve with:",
|
||||
formatCliCommand(`openclaw pairing approve ${channel} ${code}`),
|
||||
"```",
|
||||
approveCommand,
|
||||
"```",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user