diff --git a/extensions/telegram/src/bot-core.raw-update-log.test.ts b/extensions/telegram/src/bot-core.raw-update-log.test.ts index aa621e53c57..cec23a5133b 100644 --- a/extensions/telegram/src/bot-core.raw-update-log.test.ts +++ b/extensions/telegram/src/bot-core.raw-update-log.test.ts @@ -1,8 +1,8 @@ // Telegram tests cover bot core.raw update log plugin behavior. import { describe, expect, it } from "vitest"; -import { stringifyTelegramRawUpdateForLog } from "./raw-update-log.js"; +import { formatTelegramRawUpdateForLog } from "./raw-update-log.js"; -describe("stringifyTelegramRawUpdateForLog", () => { +describe("formatTelegramRawUpdateForLog", () => { it("redacts private Telegram raw update fields before verbose logging", () => { const update = { update_id: 98765, @@ -45,7 +45,7 @@ describe("stringifyTelegramRawUpdateForLog", () => { }, }; - const rawLog = stringifyTelegramRawUpdateForLog(update); + const rawLog = formatTelegramRawUpdateForLog(update); expect(rawLog).toContain('"update_id":98765'); expect(rawLog).toContain('"message_id":44'); @@ -138,7 +138,7 @@ describe("stringifyTelegramRawUpdateForLog", () => { }, }; - const rawLog = stringifyTelegramRawUpdateForLog(update); + const rawLog = formatTelegramRawUpdateForLog(update); expect(rawLog).toContain('"update_id":45678'); expect(rawLog).toContain('"message_id":99'); @@ -176,7 +176,7 @@ describe("stringifyTelegramRawUpdateForLog", () => { it("truncates long raw update strings without splitting UTF-16 surrogate pairs", () => { const prefix = "a".repeat(499); - const rawLog = stringifyTelegramRawUpdateForLog({ + const rawLog = formatTelegramRawUpdateForLog({ update_id: 123, diagnostic: `${prefix}\uD83D\uDE80tail`, }); @@ -188,4 +188,12 @@ describe("stringifyTelegramRawUpdateForLog", () => { expect(rawLog).not.toContain("\\ud83d"); expect(rawLog).not.toContain("\\ude80"); }); + + it("truncates the complete raw update log without splitting surrogate pairs", () => { + const keyPrefix = "x".repeat(7997); + const rawLog = formatTelegramRawUpdateForLog({ [`${keyPrefix}😀tail`]: 1 }); + + expect(rawLog).toBe(`{"${keyPrefix}...`); + expect(rawLog).not.toMatch(/[\uD800-\uDFFF]/u); + }); }); diff --git a/extensions/telegram/src/bot-core.ts b/extensions/telegram/src/bot-core.ts index 8171a0b80b0..b1fb087b3d4 100644 --- a/extensions/telegram/src/bot-core.ts +++ b/extensions/telegram/src/bot-core.ts @@ -56,7 +56,7 @@ import { } from "./group-history-window.js"; import { TELEGRAM_TEXT_CHUNK_LIMIT } from "./outbound-adapter.js"; import { registerTelegramOutboundGroupHistoryRecorder } from "./outbound-message-context.js"; -import { stringifyTelegramRawUpdateForLog } from "./raw-update-log.js"; +import { formatTelegramRawUpdateForLog } from "./raw-update-log.js"; import { TELEGRAM_RICH_TEXT_LIMIT } from "./rich-message.js"; import { createTelegramSendChatActionHandler } from "./sendchataction-401-backoff.js"; import { getTelegramSequentialKey } from "./sequential-key.js"; @@ -241,15 +241,11 @@ export function createTelegramBotCore( bot.use(botRuntime.sequentialize(getTelegramSequentialKey)); const rawUpdateLogger = createSubsystemLogger("gateway/channels/telegram/raw-update"); - const MAX_RAW_UPDATE_CHARS = 8000; bot.use(async (ctx, next) => { if (shouldLogVerbose()) { try { - const raw = stringifyTelegramRawUpdateForLog(ctx.update); - const preview = - raw.length > MAX_RAW_UPDATE_CHARS ? `${raw.slice(0, MAX_RAW_UPDATE_CHARS)}...` : raw; - rawUpdateLogger.debug(`telegram update: ${preview}`); + rawUpdateLogger.debug(`telegram update: ${formatTelegramRawUpdateForLog(ctx.update)}`); } catch (err) { rawUpdateLogger.debug(`telegram update log failed: ${String(err)}`); } diff --git a/extensions/telegram/src/raw-update-log.ts b/extensions/telegram/src/raw-update-log.ts index 6dc0977916f..de58da40dd5 100644 --- a/extensions/telegram/src/raw-update-log.ts +++ b/extensions/telegram/src/raw-update-log.ts @@ -3,6 +3,7 @@ import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime"; const MAX_RAW_UPDATE_STRING = 500; const MAX_RAW_UPDATE_ARRAY = 20; +const MAX_RAW_UPDATE_CHARS = 8000; const REDACTED_TELEGRAM_FIELD = "[redacted]"; const TELEGRAM_RAW_UPDATE_ALWAYS_REDACT_KEYS = new Set([ "added_to_attachment_menu", @@ -74,7 +75,7 @@ function isTelegramUserObject(value: Record): boolean { ); } -export function stringifyTelegramRawUpdateForLog(update: unknown): string { +export function formatTelegramRawUpdateForLog(update: unknown): string { const seen = new WeakSet(); const transform = (value: unknown, key = "", parentKey?: string): unknown => { if (shouldRedactTelegramRawUpdateValue(key, parentKey)) { @@ -109,5 +110,8 @@ export function stringifyTelegramRawUpdateForLog(update: unknown): string { } return value; }; - return JSON.stringify(transform(update ?? null)); + const raw = JSON.stringify(transform(update ?? null)); + return raw.length > MAX_RAW_UPDATE_CHARS + ? `${truncateUtf16Safe(raw, MAX_RAW_UPDATE_CHARS)}...` + : raw; }