fix(whatsapp): elide auto-reply text on UTF-16 boundary (#96580) · openclaw/openclaw@cb8bc71
llagy009
·
2026-06-28
·
via Recent Commits to openclaw:main
File tree
extensions/whatsapp/src/auto-reply
| Original file line number | Diff line number | Diff line change |
|---|
|
1 | 1 | // Whatsapp plugin module implements util behavior. |
2 | 2 | import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime"; |
| 3 | +import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime"; |
3 | 4 | |
4 | 5 | export function elide(text?: string, limit = 400) { |
5 | 6 | if (!text) { |
@@ -8,7 +9,8 @@ export function elide(text?: string, limit = 400) {
|
8 | 9 | if (text.length <= limit) { |
9 | 10 | return text; |
10 | 11 | } |
11 | | -return `${text.slice(0, limit)}… (truncated ${text.length - limit} chars)`; |
| 12 | +const truncated = truncateUtf16Safe(text, limit); |
| 13 | +return `${truncated}… (truncated ${text.length - truncated.length} chars)`; |
12 | 14 | } |
13 | 15 | |
14 | 16 | export function markWhatsAppVisibleDeliveryError(error: unknown): unknown { |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -365,6 +365,15 @@ describe("web auto-reply util", () => {
|
365 | 365 | }); |
366 | 366 | |
367 | 367 | describe("elide", () => { |
| 368 | +const hasLoneSurrogate = (value: string): boolean => |
| 369 | +Array.from(value).some((char) => { |
| 370 | +if (char.length !== 1) { |
| 371 | +return false; |
| 372 | +} |
| 373 | +const codeUnit = char.charCodeAt(0); |
| 374 | +return codeUnit >= 0xd800 && codeUnit <= 0xdfff; |
| 375 | +}); |
| 376 | + |
368 | 377 | it("returns undefined for undefined input", () => { |
369 | 378 | expect(elide(undefined)).toBe(undefined); |
370 | 379 | }); |
@@ -376,6 +385,20 @@ describe("web auto-reply util", () => {
|
376 | 385 | it("truncates and annotates when over limit", () => { |
377 | 386 | expect(elide("abcdef", 3)).toBe("abc… (truncated 3 chars)"); |
378 | 387 | }); |
| 388 | + |
| 389 | +it("does not split surrogate pairs when the limit lands inside an emoji", () => { |
| 390 | +const output = elide("😀😀😀", 5); |
| 391 | + |
| 392 | +expect(output).toBe("😀😀… (truncated 2 chars)"); |
| 393 | +expect(hasLoneSurrogate(output ?? "")).toBe(false); |
| 394 | +}); |
| 395 | + |
| 396 | +it("keeps a complete astral character when it fits before the limit", () => { |
| 397 | +const output = elide("ab😀cd", 4); |
| 398 | + |
| 399 | +expect(output).toBe("ab😀… (truncated 2 chars)"); |
| 400 | +expect(hasLoneSurrogate(output ?? "")).toBe(false); |
| 401 | +}); |
379 | 402 | }); |
380 | 403 | |
381 | 404 | describe("isLikelyWhatsAppCryptoError", () => { |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。