mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 04:15:48 -06:00
fix(whatsapp): elide auto-reply text on UTF-16 boundary (#96580)
elide truncated text with String.slice(0, limit) on a UTF-16 code-unit index, so an astral character straddling the limit was cut into a lone surrogate; the truncated-char count was also computed from the fixed limit rather than the actual kept length. Truncate with truncateUtf16Safe so a surrogate pair is never split, and derive the truncated-char count from the kept length so the annotation stays accurate. Adds tests asserting no lone surrogate when the limit lands inside an emoji and that a complete astral character is kept when it fits.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
// Whatsapp plugin module implements util behavior.
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
||||
|
||||
export function elide(text?: string, limit = 400) {
|
||||
if (!text) {
|
||||
@@ -8,7 +9,8 @@ export function elide(text?: string, limit = 400) {
|
||||
if (text.length <= limit) {
|
||||
return text;
|
||||
}
|
||||
return `${text.slice(0, limit)}… (truncated ${text.length - limit} chars)`;
|
||||
const truncated = truncateUtf16Safe(text, limit);
|
||||
return `${truncated}… (truncated ${text.length - truncated.length} chars)`;
|
||||
}
|
||||
|
||||
export function markWhatsAppVisibleDeliveryError(error: unknown): unknown {
|
||||
|
||||
@@ -365,6 +365,15 @@ describe("web auto-reply util", () => {
|
||||
});
|
||||
|
||||
describe("elide", () => {
|
||||
const hasLoneSurrogate = (value: string): boolean =>
|
||||
Array.from(value).some((char) => {
|
||||
if (char.length !== 1) {
|
||||
return false;
|
||||
}
|
||||
const codeUnit = char.charCodeAt(0);
|
||||
return codeUnit >= 0xd800 && codeUnit <= 0xdfff;
|
||||
});
|
||||
|
||||
it("returns undefined for undefined input", () => {
|
||||
expect(elide(undefined)).toBe(undefined);
|
||||
});
|
||||
@@ -376,6 +385,20 @@ describe("web auto-reply util", () => {
|
||||
it("truncates and annotates when over limit", () => {
|
||||
expect(elide("abcdef", 3)).toBe("abc… (truncated 3 chars)");
|
||||
});
|
||||
|
||||
it("does not split surrogate pairs when the limit lands inside an emoji", () => {
|
||||
const output = elide("😀😀😀", 5);
|
||||
|
||||
expect(output).toBe("😀😀… (truncated 2 chars)");
|
||||
expect(hasLoneSurrogate(output ?? "")).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps a complete astral character when it fits before the limit", () => {
|
||||
const output = elide("ab😀cd", 4);
|
||||
|
||||
expect(output).toBe("ab😀… (truncated 2 chars)");
|
||||
expect(hasLoneSurrogate(output ?? "")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isLikelyWhatsAppCryptoError", () => {
|
||||
|
||||
Reference in New Issue
Block a user