@zxh126/pi-qqbot 2.4.1 → 2.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/util/chunk.ts CHANGED
@@ -1,42 +1,107 @@
1
- export function splitText(text: string, maxBytes = 1900): string[] {
2
- if (Buffer.byteLength(text, "utf8") <= maxBytes) return text ? [text] : [];
3
- const parts: string[] = [];
4
- let current = "";
5
- let currentBytes = 0;
6
- for (const seg of text.split(/(?<=\n)/)) {
7
- const segBytes = Buffer.byteLength(seg, "utf8");
8
- if (segBytes > maxBytes) {
9
- if (current) {
10
- parts.push(current);
11
- current = "";
12
- currentBytes = 0;
13
- }
14
- let buf = "";
15
- let bufBytes = 0;
16
- for (const ch of seg) {
17
- const chBytes = Buffer.byteLength(ch, "utf8");
18
- if (bufBytes + chBytes > maxBytes) {
19
- parts.push(buf);
20
- buf = ch;
21
- bufBytes = chBytes;
22
- } else {
23
- buf += ch;
24
- bufBytes += chBytes;
25
- }
26
- }
27
- current = buf;
28
- currentBytes = bufBytes;
29
- continue;
30
- }
31
- if (currentBytes + segBytes > maxBytes) {
32
- parts.push(current);
33
- current = seg;
34
- currentBytes = segBytes;
35
- } else {
36
- current += seg;
37
- currentBytes += segBytes;
38
- }
39
- }
40
- if (current) parts.push(current);
41
- return parts;
42
- }
1
+ /**
2
+ * Markdown 文本切分器:QQ 单条消息有字节上限(默认 1900),需要按行切分,
3
+ * 并保持 GFM 表格、代码块的完整性(长表格不拦腰截断,否则 md 渲染出破表)。
4
+ */
5
+ const CODE_FENCE = /^```/;
6
+ const GFM_TABLE_LINE = /^\|.+\|$/;
7
+
8
+ export function splitText(text: string, maxBytes = 1900): string[] {
9
+ if (Buffer.byteLength(text, "utf8") <= maxBytes) return text ? [text] : [];
10
+ const parts: string[] = [];
11
+ let current = "";
12
+ let currentBytes = 0;
13
+ let inCodeBlock = false;
14
+ let tableLines: string[] = [];
15
+
16
+ const flushCurrent = () => {
17
+ if (!current) return;
18
+ parts.push(current);
19
+ current = "";
20
+ currentBytes = 0;
21
+ };
22
+ /** 单行超过上限:按字符硬切(罕见,保持旧行为兜底) */
23
+ const pushCharSplit = (line: string) => {
24
+ let buf = "";
25
+ let bufBytes = 0;
26
+ for (const ch of line) {
27
+ const chBytes = Buffer.byteLength(ch, "utf8");
28
+ if (bufBytes + chBytes > maxBytes) {
29
+ parts.push(buf);
30
+ buf = ch;
31
+ bufBytes = chBytes;
32
+ } else {
33
+ buf += ch;
34
+ bufBytes += chBytes;
35
+ }
36
+ }
37
+ if (buf) parts.push(buf);
38
+ };
39
+ const appendLine = (line: string) => {
40
+ const lineBytes = Buffer.byteLength(line, "utf8");
41
+ if (!current) {
42
+ if (lineBytes > maxBytes) {
43
+ pushCharSplit(line);
44
+ return;
45
+ }
46
+ current = line;
47
+ currentBytes = lineBytes;
48
+ return;
49
+ }
50
+ const joinBytes = 1; // 拼接的换行符
51
+ if (currentBytes + joinBytes + lineBytes > maxBytes) {
52
+ flushCurrent();
53
+ if (lineBytes > maxBytes) {
54
+ pushCharSplit(line);
55
+ return;
56
+ }
57
+ current = line;
58
+ currentBytes = lineBytes;
59
+ } else {
60
+ current += "\n" + line;
61
+ currentBytes += joinBytes + lineBytes;
62
+ }
63
+ };
64
+ /** 表格整块提交:先尽力并入 current,放不下就整块另起一条消息 */
65
+ const flushTable = () => {
66
+ if (tableLines.length === 0) return;
67
+ const block = tableLines.join("\n");
68
+ tableLines = [];
69
+ const blockBytes = Buffer.byteLength(block, "utf8");
70
+ if (!current) {
71
+ current = block;
72
+ currentBytes = blockBytes;
73
+ return;
74
+ }
75
+ if (currentBytes + 1 + blockBytes > maxBytes) {
76
+ flushCurrent();
77
+ current = block;
78
+ currentBytes = blockBytes;
79
+ } else {
80
+ current += "\n" + block;
81
+ currentBytes += 1 + blockBytes;
82
+ }
83
+ };
84
+
85
+ for (const rawLine of text.split("\n")) {
86
+ const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
87
+ if (CODE_FENCE.test(line)) {
88
+ flushTable();
89
+ inCodeBlock = !inCodeBlock;
90
+ appendLine(line);
91
+ continue;
92
+ }
93
+ if (inCodeBlock) {
94
+ appendLine(line);
95
+ continue;
96
+ }
97
+ if (GFM_TABLE_LINE.test(line)) {
98
+ tableLines.push(line);
99
+ continue;
100
+ }
101
+ flushTable();
102
+ appendLine(line);
103
+ }
104
+ flushTable();
105
+ if (current) parts.push(current);
106
+ return parts.length > 0 ? parts : [text];
107
+ }