mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-07 02:22:46 +00:00
fix(whatsapp): stop markdownToWhatsApp dropping code spans followed by a digit (#93409)
The inline-code/fence restore step matched the placeholder index with a greedy `(\d+)`, so a digit in user text immediately after a code span (e.g. `code`5) was absorbed into the index, resolved to undefined, and `?? ""` deleted both the code span and the digit. Terminate the placeholder index with the existing NUL marker so the index boundary is unambiguous. Co-authored-by: Dr Rushindra Sinha <5796457+rushindrasinha@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
committed by
GitHub
parent
f08b24e63c
commit
eccfacb02c
@@ -8,6 +8,9 @@ import { CONFIG_DIR, resolveUserPath } from "openclaw/plugin-sdk/text-utility-ru
|
||||
|
||||
const WHATSAPP_FENCE_PLACEHOLDER = "\x00FENCE";
|
||||
const WHATSAPP_INLINE_CODE_PLACEHOLDER = "\x00CODE";
|
||||
// Terminates the numeric index in a placeholder so the restore regex cannot
|
||||
// absorb a digit from adjacent user text (e.g. `code`5) into the index.
|
||||
const WHATSAPP_PLACEHOLDER_TERMINATOR = "\x00";
|
||||
|
||||
export type WebChannel = "web";
|
||||
|
||||
@@ -197,25 +200,26 @@ export function markdownToWhatsApp(text: string): string {
|
||||
const fences: string[] = [];
|
||||
let result = text.replace(/```[\s\S]*?```/g, (match) => {
|
||||
fences.push(match);
|
||||
return `${WHATSAPP_FENCE_PLACEHOLDER}${fences.length - 1}`;
|
||||
return `${WHATSAPP_FENCE_PLACEHOLDER}${fences.length - 1}${WHATSAPP_PLACEHOLDER_TERMINATOR}`;
|
||||
});
|
||||
|
||||
const inlineCodes: string[] = [];
|
||||
result = result.replace(/`[^`\n]+`/g, (match) => {
|
||||
inlineCodes.push(match);
|
||||
return `${WHATSAPP_INLINE_CODE_PLACEHOLDER}${inlineCodes.length - 1}`;
|
||||
return `${WHATSAPP_INLINE_CODE_PLACEHOLDER}${inlineCodes.length - 1}${WHATSAPP_PLACEHOLDER_TERMINATOR}`;
|
||||
});
|
||||
|
||||
result = result.replace(/\*\*(.+?)\*\*/g, "*$1*");
|
||||
result = result.replace(/__(.+?)__/g, "*$1*");
|
||||
result = result.replace(/~~(.+?)~~/g, "~$1~");
|
||||
|
||||
const terminator = escapeRegExp(WHATSAPP_PLACEHOLDER_TERMINATOR);
|
||||
result = result.replace(
|
||||
new RegExp(`${escapeRegExp(WHATSAPP_INLINE_CODE_PLACEHOLDER)}(\\d+)`, "g"),
|
||||
new RegExp(`${escapeRegExp(WHATSAPP_INLINE_CODE_PLACEHOLDER)}(\\d+)${terminator}`, "g"),
|
||||
(_, idx) => inlineCodes[Number(idx)] ?? "",
|
||||
);
|
||||
result = result.replace(
|
||||
new RegExp(`${escapeRegExp(WHATSAPP_FENCE_PLACEHOLDER)}(\\d+)`, "g"),
|
||||
new RegExp(`${escapeRegExp(WHATSAPP_FENCE_PLACEHOLDER)}(\\d+)${terminator}`, "g"),
|
||||
(_, idx) => fences[Number(idx)] ?? "",
|
||||
);
|
||||
return result;
|
||||
|
||||
@@ -41,6 +41,12 @@ describe("markdownToWhatsApp", () => {
|
||||
["returns empty string for empty input", "", ""],
|
||||
["returns plain text unchanged", "no formatting here", "no formatting here"],
|
||||
["handles bold inside a sentence", "This is **very** important", "This is *very* important"],
|
||||
// Regression: a digit immediately after an inline-code span must not be
|
||||
// absorbed into the placeholder index (which previously dropped both).
|
||||
["preserves inline code immediately followed by a digit", "`a`5", "`a`5"],
|
||||
["preserves inline code followed by a number", "`status`200 done", "`status`200 done"],
|
||||
["preserves two adjacent code+digit spans", "`x`1 and `y`2", "`x`1 and `y`2"],
|
||||
["preserves inline code with a space before a digit", "`a` 5", "`a` 5"],
|
||||
] as const)("handles markdown-to-whatsapp conversion: %s", (_name, input, expected) => {
|
||||
expect(markdownToWhatsApp(input)).toBe(expected);
|
||||
});
|
||||
@@ -50,6 +56,11 @@ describe("markdownToWhatsApp", () => {
|
||||
expect(markdownToWhatsApp(input)).toBe(input);
|
||||
});
|
||||
|
||||
it("preserves a fenced code block immediately followed by a digit", () => {
|
||||
const input = "```code```7 done";
|
||||
expect(markdownToWhatsApp(input)).toBe(input);
|
||||
});
|
||||
|
||||
it("preserves code block with formatting inside", () => {
|
||||
const input = "Before ```**bold** and ~~strike~~``` after **real bold**";
|
||||
expect(markdownToWhatsApp(input)).toBe(
|
||||
|
||||
Reference in New Issue
Block a user