fix(slack): truncate on code-point boundaries to avoid splitting surr… · openclaw/openclaw@1069c60
ly-wang19
·
2026-06-24
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +// Slack tests cover truncate plugin behavior. |
| 2 | +import { describe, expect, it } from "vitest"; |
| 3 | +import { truncateSlackText } from "./truncate.js"; |
| 4 | + |
| 5 | +describe("truncateSlackText", () => { |
| 6 | +it("drops a surrogate-pair emoji whole when it straddles the limit", () => { |
| 7 | +// "abc😀def": 😀 (U+1F600) sits at the cut point. Slicing by UTF-16 code unit |
| 8 | +// would keep only its high surrogate — a lone \uD83D — before the ellipsis, |
| 9 | +// which serializes to an invalid character in the Slack payload. |
| 10 | +const out = truncateSlackText("abc😀def", 5); |
| 11 | +expect(out).toBe("abc…"); |
| 12 | +// No dangling high surrogate (a high surrogate not followed by a low one). |
| 13 | +expect(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])/.test(out)).toBe(false); |
| 14 | +}); |
| 15 | + |
| 16 | +it("truncates plain BMP text unchanged", () => { |
| 17 | +expect(truncateSlackText("hello world", 5)).toBe("hell…"); |
| 18 | +}); |
| 19 | + |
| 20 | +it("keeps an emoji that fits before the cut", () => { |
| 21 | +expect(truncateSlackText("😀abcdef", 5)).toBe("😀ab…"); |
| 22 | +}); |
| 23 | + |
| 24 | +it("returns the trimmed input unchanged when it fits", () => { |
| 25 | +expect(truncateSlackText("ab😀cd", 10)).toBe("ab😀cd"); |
| 26 | +}); |
| 27 | +}); |
| Original file line number | Diff line number | Diff line change |
|---|
|
1 | 1 | // Slack plugin module implements truncate behavior. |
| 2 | +import { sliceUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime"; |
| 3 | + |
2 | 4 | export function truncateSlackText(value: string, max: number): string { |
3 | 5 | const trimmed = value.trim(); |
4 | 6 | if (trimmed.length <= max) { |
5 | 7 | return trimmed; |
6 | 8 | } |
| 9 | +// Slice on a code-point boundary so a surrogate pair (emoji / astral char) |
| 10 | +// straddling the limit is dropped whole, instead of leaving a lone surrogate |
| 11 | +// half that serializes to an invalid `\uD83D` in the Slack payload. |
7 | 12 | if (max <= 1) { |
8 | | -return trimmed.slice(0, max); |
| 13 | +return sliceUtf16Safe(trimmed, 0, max); |
9 | 14 | } |
10 | | -return `${trimmed.slice(0, max - 1)}…`; |
| 15 | +return `${sliceUtf16Safe(trimmed, 0, max - 1)}…`; |
11 | 16 | } |
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。