@openclaw/zalouser 2026.5.2 → 2026.5.3-beta.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/dist/accounts-C00IMUgd.js +63 -0
- package/dist/accounts.runtime-uG7S8cXT.js +2 -0
- package/dist/api-BRwdUWuS.js +139 -0
- package/dist/api.js +7 -0
- package/dist/channel-ou_w_2j-.js +484 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-C9WxiAiR.js +25 -0
- package/dist/channel.setup-CiDeBFrn.js +10 -0
- package/dist/contract-api.js +3 -0
- package/dist/doctor-contract-DgqHp8E2.js +128 -0
- package/dist/doctor-contract-api.js +2 -0
- package/dist/index.js +27 -0
- package/dist/monitor-Cg7K_s_s.js +705 -0
- package/dist/runtime-QNU7vLgI.js +106 -0
- package/dist/runtime-api.js +22 -0
- package/dist/secret-contract-api.js +5 -0
- package/dist/security-audit-BZLhil-V.js +34 -0
- package/dist/send-BsmySxe3.js +534 -0
- package/dist/session-route-C0-Xr8bt.js +92 -0
- package/dist/setup-core-CqipqY98.js +40 -0
- package/dist/setup-entry.js +11 -0
- package/dist/setup-plugin-api.js +2 -0
- package/dist/setup-surface-NCOuKu-l.js +359 -0
- package/dist/shared-DSy8aIUx.js +120 -0
- package/dist/test-api.js +5 -0
- package/dist/zalo-js-CHCUlY3c.js +1279 -0
- package/package.json +15 -6
- package/api.ts +0 -9
- package/channel-plugin-api.ts +0 -3
- package/contract-api.ts +0 -2
- package/doctor-contract-api.ts +0 -1
- package/index.ts +0 -34
- package/runtime-api.ts +0 -67
- package/secret-contract-api.ts +0 -4
- package/setup-entry.ts +0 -9
- package/setup-plugin-api.ts +0 -2
- package/src/accounts.runtime.ts +0 -1
- package/src/accounts.test-mocks.ts +0 -14
- package/src/accounts.test.ts +0 -266
- package/src/accounts.ts +0 -131
- package/src/channel-api.ts +0 -20
- package/src/channel.adapters.ts +0 -391
- package/src/channel.directory.test.ts +0 -59
- package/src/channel.runtime.ts +0 -12
- package/src/channel.sendpayload.test.ts +0 -172
- package/src/channel.setup.test.ts +0 -33
- package/src/channel.setup.ts +0 -12
- package/src/channel.test.ts +0 -377
- package/src/channel.ts +0 -219
- package/src/config-schema.ts +0 -33
- package/src/directory.ts +0 -54
- package/src/doctor-contract.ts +0 -156
- package/src/doctor.test.ts +0 -77
- package/src/doctor.ts +0 -37
- package/src/group-policy.test.ts +0 -61
- package/src/group-policy.ts +0 -83
- package/src/message-sid.test.ts +0 -66
- package/src/message-sid.ts +0 -80
- package/src/monitor.account-scope.test.ts +0 -107
- package/src/monitor.group-gating.test.ts +0 -816
- package/src/monitor.send-mocks.ts +0 -20
- package/src/monitor.ts +0 -1044
- package/src/probe.test.ts +0 -60
- package/src/probe.ts +0 -35
- package/src/qr-temp-file.ts +0 -22
- package/src/reaction.test.ts +0 -19
- package/src/reaction.ts +0 -32
- package/src/runtime.ts +0 -9
- package/src/security-audit.test.ts +0 -80
- package/src/security-audit.ts +0 -71
- package/src/send.test.ts +0 -395
- package/src/send.ts +0 -272
- package/src/session-route.ts +0 -121
- package/src/setup-core.ts +0 -33
- package/src/setup-surface.test.ts +0 -363
- package/src/setup-surface.ts +0 -470
- package/src/setup-test-helpers.ts +0 -42
- package/src/shared.ts +0 -92
- package/src/status-issues.test.ts +0 -31
- package/src/status-issues.ts +0 -58
- package/src/test-helpers.ts +0 -26
- package/src/text-styles.test.ts +0 -203
- package/src/text-styles.ts +0 -540
- package/src/tool.test.ts +0 -212
- package/src/tool.ts +0 -210
- package/src/types.ts +0 -125
- package/src/zalo-js.credentials.test.ts +0 -465
- package/src/zalo-js.test-mocks.ts +0 -89
- package/src/zalo-js.ts +0 -1911
- package/src/zca-client.test.ts +0 -24
- package/src/zca-client.ts +0 -259
- package/src/zca-constants.ts +0 -55
- package/src/zca-js-exports.d.ts +0 -22
- package/test-api.ts +0 -21
- package/tsconfig.json +0 -16
|
@@ -0,0 +1,534 @@
|
|
|
1
|
+
import { _ as sendZaloTypingEvent, f as sendZaloDeliveredEvent, g as sendZaloTextMessage, h as sendZaloSeenEvent, m as sendZaloReaction, p as sendZaloLink, x as TextStyle } from "./zalo-js-CHCUlY3c.js";
|
|
2
|
+
//#region extensions/zalouser/src/text-styles.ts
|
|
3
|
+
const ESCAPE_SENTINEL_START = "";
|
|
4
|
+
const ESCAPE_SENTINEL_END = "";
|
|
5
|
+
const TAG_STYLE_MAP = {
|
|
6
|
+
red: TextStyle.Red,
|
|
7
|
+
orange: TextStyle.Orange,
|
|
8
|
+
yellow: TextStyle.Yellow,
|
|
9
|
+
green: TextStyle.Green,
|
|
10
|
+
small: null,
|
|
11
|
+
big: TextStyle.Big,
|
|
12
|
+
underline: TextStyle.Underline
|
|
13
|
+
};
|
|
14
|
+
const INLINE_MARKERS = [
|
|
15
|
+
{
|
|
16
|
+
pattern: /`([^`\n]+)`/g,
|
|
17
|
+
extractText: (match) => match[0],
|
|
18
|
+
literal: true
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
pattern: /\\([*_~#\\{}>+\-`])/g,
|
|
22
|
+
extractText: (match) => match[1],
|
|
23
|
+
literal: true
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
pattern: new RegExp(`\\{(${Object.keys(TAG_STYLE_MAP).join("|")})\\}(.+?)\\{/\\1\\}`, "g"),
|
|
27
|
+
extractText: (match) => match[2],
|
|
28
|
+
resolveStyles: (match) => {
|
|
29
|
+
const style = TAG_STYLE_MAP[match[1]];
|
|
30
|
+
return style ? [style] : [];
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
pattern: /(?<!\*)\*\*\*(?=\S)([^\n]*?\S)(?<!\*)\*\*\*(?!\*)/g,
|
|
35
|
+
extractText: (match) => match[1],
|
|
36
|
+
resolveStyles: () => [TextStyle.Bold, TextStyle.Italic]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
pattern: /(?<!\*)\*\*(?![\s*])([^\n]*?\S)(?<!\*)\*\*(?!\*)/g,
|
|
40
|
+
extractText: (match) => match[1],
|
|
41
|
+
resolveStyles: () => [TextStyle.Bold]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
pattern: /(?<![\w_])__(?![\s_])([^\n]*?\S)(?<!_)__(?![\w_])/g,
|
|
45
|
+
extractText: (match) => match[1],
|
|
46
|
+
resolveStyles: () => [TextStyle.Bold]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
pattern: /(?<!~)~~(?=\S)([^\n]*?\S)(?<!~)~~(?!~)/g,
|
|
50
|
+
extractText: (match) => match[1],
|
|
51
|
+
resolveStyles: () => [TextStyle.StrikeThrough]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
pattern: /(?<!\*)\*(?![\s*])([^\n]*?\S)(?<!\*)\*(?!\*)/g,
|
|
55
|
+
extractText: (match) => match[1],
|
|
56
|
+
resolveStyles: () => [TextStyle.Italic]
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
pattern: /(?<![\w_])_(?![\s_])([^\n]*?\S)(?<!_)_(?![\w_])/g,
|
|
60
|
+
extractText: (match) => match[1],
|
|
61
|
+
resolveStyles: () => [TextStyle.Italic]
|
|
62
|
+
}
|
|
63
|
+
];
|
|
64
|
+
function parseZalouserTextStyles(input) {
|
|
65
|
+
const allStyles = [];
|
|
66
|
+
const escapeMap = [];
|
|
67
|
+
const lines = input.replace(/\r\n?/g, "\n").split("\n");
|
|
68
|
+
const lineStyles = [];
|
|
69
|
+
const processedLines = [];
|
|
70
|
+
let activeFence = null;
|
|
71
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
72
|
+
const rawLine = lines[lineIndex];
|
|
73
|
+
const { text: unquotedLine, indent: baseIndent } = stripQuotePrefix(rawLine);
|
|
74
|
+
if (activeFence) {
|
|
75
|
+
const codeLine = activeFence.quoteIndent > 0 ? stripQuotePrefix(rawLine, activeFence.quoteIndent).text : rawLine;
|
|
76
|
+
if (isClosingFence(codeLine, activeFence)) {
|
|
77
|
+
activeFence = null;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
processedLines.push(escapeLiteralText(normalizeCodeBlockLeadingWhitespace(stripCodeFenceIndent(codeLine, activeFence.indent)), escapeMap));
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
let line = unquotedLine;
|
|
84
|
+
const openingFence = resolveOpeningFence(rawLine);
|
|
85
|
+
if (openingFence) {
|
|
86
|
+
const fenceLine = openingFence.quoteIndent > 0 ? unquotedLine : rawLine;
|
|
87
|
+
if (!hasClosingFence(lines, lineIndex + 1, openingFence)) {
|
|
88
|
+
processedLines.push(escapeLiteralText(fenceLine, escapeMap));
|
|
89
|
+
activeFence = openingFence;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
activeFence = openingFence;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const outputLineIndex = processedLines.length;
|
|
96
|
+
if (isIndentedCodeBlockLine(line)) {
|
|
97
|
+
if (baseIndent > 0) lineStyles.push({
|
|
98
|
+
lineIndex: outputLineIndex,
|
|
99
|
+
style: TextStyle.Indent,
|
|
100
|
+
indentSize: baseIndent
|
|
101
|
+
});
|
|
102
|
+
processedLines.push(escapeLiteralText(normalizeCodeBlockLeadingWhitespace(line), escapeMap));
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const { text: markdownLine, size: markdownPadding } = stripOptionalMarkdownPadding(line);
|
|
106
|
+
const headingMatch = markdownLine.match(/^(#{1,4})\s(.*)$/);
|
|
107
|
+
if (headingMatch) {
|
|
108
|
+
const depth = headingMatch[1].length;
|
|
109
|
+
lineStyles.push({
|
|
110
|
+
lineIndex: outputLineIndex,
|
|
111
|
+
style: TextStyle.Bold
|
|
112
|
+
});
|
|
113
|
+
if (depth === 1) lineStyles.push({
|
|
114
|
+
lineIndex: outputLineIndex,
|
|
115
|
+
style: TextStyle.Big
|
|
116
|
+
});
|
|
117
|
+
if (baseIndent > 0) lineStyles.push({
|
|
118
|
+
lineIndex: outputLineIndex,
|
|
119
|
+
style: TextStyle.Indent,
|
|
120
|
+
indentSize: baseIndent
|
|
121
|
+
});
|
|
122
|
+
processedLines.push(headingMatch[2]);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const indentMatch = markdownLine.match(/^(\s+)(.*)$/);
|
|
126
|
+
let indentLevel = 0;
|
|
127
|
+
let content = markdownLine;
|
|
128
|
+
if (indentMatch) {
|
|
129
|
+
indentLevel = clampIndent(indentMatch[1].length);
|
|
130
|
+
content = indentMatch[2];
|
|
131
|
+
}
|
|
132
|
+
const totalIndent = Math.min(5, baseIndent + indentLevel);
|
|
133
|
+
if (/^[-*+]\s\[[ xX]\]\s/.test(content)) {
|
|
134
|
+
if (totalIndent > 0) lineStyles.push({
|
|
135
|
+
lineIndex: outputLineIndex,
|
|
136
|
+
style: TextStyle.Indent,
|
|
137
|
+
indentSize: totalIndent
|
|
138
|
+
});
|
|
139
|
+
processedLines.push(content);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const orderedListMatch = content.match(/^(\d+)\.\s(.*)$/);
|
|
143
|
+
if (orderedListMatch) {
|
|
144
|
+
if (totalIndent > 0) lineStyles.push({
|
|
145
|
+
lineIndex: outputLineIndex,
|
|
146
|
+
style: TextStyle.Indent,
|
|
147
|
+
indentSize: totalIndent
|
|
148
|
+
});
|
|
149
|
+
lineStyles.push({
|
|
150
|
+
lineIndex: outputLineIndex,
|
|
151
|
+
style: TextStyle.OrderedList
|
|
152
|
+
});
|
|
153
|
+
processedLines.push(orderedListMatch[2]);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
const unorderedListMatch = content.match(/^[-*+]\s(.*)$/);
|
|
157
|
+
if (unorderedListMatch) {
|
|
158
|
+
if (totalIndent > 0) lineStyles.push({
|
|
159
|
+
lineIndex: outputLineIndex,
|
|
160
|
+
style: TextStyle.Indent,
|
|
161
|
+
indentSize: totalIndent
|
|
162
|
+
});
|
|
163
|
+
lineStyles.push({
|
|
164
|
+
lineIndex: outputLineIndex,
|
|
165
|
+
style: TextStyle.UnorderedList
|
|
166
|
+
});
|
|
167
|
+
processedLines.push(unorderedListMatch[1]);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (markdownPadding > 0) {
|
|
171
|
+
if (baseIndent > 0) lineStyles.push({
|
|
172
|
+
lineIndex: outputLineIndex,
|
|
173
|
+
style: TextStyle.Indent,
|
|
174
|
+
indentSize: baseIndent
|
|
175
|
+
});
|
|
176
|
+
processedLines.push(line);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (totalIndent > 0) {
|
|
180
|
+
lineStyles.push({
|
|
181
|
+
lineIndex: outputLineIndex,
|
|
182
|
+
style: TextStyle.Indent,
|
|
183
|
+
indentSize: totalIndent
|
|
184
|
+
});
|
|
185
|
+
processedLines.push(content);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
processedLines.push(line);
|
|
189
|
+
}
|
|
190
|
+
const segments = parseInlineSegments(processedLines.join("\n"));
|
|
191
|
+
let plainText = "";
|
|
192
|
+
for (const segment of segments) {
|
|
193
|
+
const start = plainText.length;
|
|
194
|
+
plainText += segment.text;
|
|
195
|
+
for (const style of segment.styles) allStyles.push({
|
|
196
|
+
start,
|
|
197
|
+
len: segment.text.length,
|
|
198
|
+
st: style
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
if (escapeMap.length > 0) {
|
|
202
|
+
const escapeRegex = new RegExp(`${ESCAPE_SENTINEL_START}(\\d+)${ESCAPE_SENTINEL_END}`, "g");
|
|
203
|
+
const shifts = [];
|
|
204
|
+
let cumulativeDelta = 0;
|
|
205
|
+
for (const match of plainText.matchAll(escapeRegex)) {
|
|
206
|
+
const escapeIndex = Number.parseInt(match[1], 10);
|
|
207
|
+
cumulativeDelta += match[0].length - escapeMap[escapeIndex].length;
|
|
208
|
+
shifts.push({
|
|
209
|
+
pos: (match.index ?? 0) + match[0].length,
|
|
210
|
+
delta: cumulativeDelta
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
for (const style of allStyles) {
|
|
214
|
+
let startDelta = 0;
|
|
215
|
+
let endDelta = 0;
|
|
216
|
+
const end = style.start + style.len;
|
|
217
|
+
for (const shift of shifts) {
|
|
218
|
+
if (shift.pos <= style.start) startDelta = shift.delta;
|
|
219
|
+
if (shift.pos <= end) endDelta = shift.delta;
|
|
220
|
+
}
|
|
221
|
+
style.start -= startDelta;
|
|
222
|
+
style.len -= endDelta - startDelta;
|
|
223
|
+
}
|
|
224
|
+
plainText = plainText.replace(escapeRegex, (_match, index) => escapeMap[Number.parseInt(index, 10)]);
|
|
225
|
+
}
|
|
226
|
+
const finalLines = plainText.split("\n");
|
|
227
|
+
let offset = 0;
|
|
228
|
+
for (let lineIndex = 0; lineIndex < finalLines.length; lineIndex += 1) {
|
|
229
|
+
const lineLength = finalLines[lineIndex].length;
|
|
230
|
+
if (lineLength > 0) for (const lineStyle of lineStyles) {
|
|
231
|
+
if (lineStyle.lineIndex !== lineIndex) continue;
|
|
232
|
+
if (lineStyle.style === TextStyle.Indent) allStyles.push({
|
|
233
|
+
start: offset,
|
|
234
|
+
len: lineLength,
|
|
235
|
+
st: TextStyle.Indent,
|
|
236
|
+
indentSize: lineStyle.indentSize
|
|
237
|
+
});
|
|
238
|
+
else allStyles.push({
|
|
239
|
+
start: offset,
|
|
240
|
+
len: lineLength,
|
|
241
|
+
st: lineStyle.style
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
offset += lineLength + 1;
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
text: plainText,
|
|
248
|
+
styles: allStyles
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
function clampIndent(spaceCount) {
|
|
252
|
+
return Math.min(5, Math.max(1, Math.floor(spaceCount / 2)));
|
|
253
|
+
}
|
|
254
|
+
function stripOptionalMarkdownPadding(line) {
|
|
255
|
+
const match = line.match(/^( {1,3})(?=\S)/);
|
|
256
|
+
if (!match) return {
|
|
257
|
+
text: line,
|
|
258
|
+
size: 0
|
|
259
|
+
};
|
|
260
|
+
return {
|
|
261
|
+
text: line.slice(match[1].length),
|
|
262
|
+
size: match[1].length
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function hasClosingFence(lines, startIndex, fence) {
|
|
266
|
+
for (let index = startIndex; index < lines.length; index += 1) if (isClosingFence(fence.quoteIndent > 0 ? stripQuotePrefix(lines[index], fence.quoteIndent).text : lines[index], fence)) return true;
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
function resolveOpeningFence(line) {
|
|
270
|
+
const directFence = parseFenceMarker(line);
|
|
271
|
+
if (directFence) return {
|
|
272
|
+
...directFence,
|
|
273
|
+
quoteIndent: 0
|
|
274
|
+
};
|
|
275
|
+
const quoted = stripQuotePrefix(line);
|
|
276
|
+
if (quoted.indent === 0) return null;
|
|
277
|
+
const quotedFence = parseFenceMarker(quoted.text);
|
|
278
|
+
if (!quotedFence) return null;
|
|
279
|
+
return {
|
|
280
|
+
...quotedFence,
|
|
281
|
+
quoteIndent: quoted.indent
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function stripQuotePrefix(line, maxDepth = Number.POSITIVE_INFINITY) {
|
|
285
|
+
let cursor = 0;
|
|
286
|
+
while (cursor < line.length && cursor < 3 && line[cursor] === " ") cursor += 1;
|
|
287
|
+
let removedDepth = 0;
|
|
288
|
+
let consumedCursor = cursor;
|
|
289
|
+
while (removedDepth < maxDepth && consumedCursor < line.length && line[consumedCursor] === ">") {
|
|
290
|
+
removedDepth += 1;
|
|
291
|
+
consumedCursor += 1;
|
|
292
|
+
if (line[consumedCursor] === " ") consumedCursor += 1;
|
|
293
|
+
}
|
|
294
|
+
if (removedDepth === 0) return {
|
|
295
|
+
text: line,
|
|
296
|
+
indent: 0
|
|
297
|
+
};
|
|
298
|
+
return {
|
|
299
|
+
text: line.slice(consumedCursor),
|
|
300
|
+
indent: Math.min(5, removedDepth)
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
function parseFenceMarker(line) {
|
|
304
|
+
const match = line.match(/^([ ]{0,3})(`{3,}|~{3,})(.*)$/);
|
|
305
|
+
if (!match) return null;
|
|
306
|
+
const marker = match[2];
|
|
307
|
+
const char = marker[0];
|
|
308
|
+
if (char !== "`" && char !== "~") return null;
|
|
309
|
+
return {
|
|
310
|
+
char,
|
|
311
|
+
length: marker.length,
|
|
312
|
+
indent: match[1].length
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
function isClosingFence(line, fence) {
|
|
316
|
+
const match = line.match(/^([ ]{0,3})(`{3,}|~{3,})[ \t]*$/);
|
|
317
|
+
if (!match) return false;
|
|
318
|
+
return match[2][0] === fence.char && match[2].length >= fence.length;
|
|
319
|
+
}
|
|
320
|
+
function escapeLiteralText(input, escapeMap) {
|
|
321
|
+
return input.replace(/[\\*_~{}`]/g, (ch) => {
|
|
322
|
+
const index = escapeMap.length;
|
|
323
|
+
escapeMap.push(ch);
|
|
324
|
+
return `\x01${index}\x02`;
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
function parseInlineSegments(text, inheritedStyles = []) {
|
|
328
|
+
const segments = [];
|
|
329
|
+
let cursor = 0;
|
|
330
|
+
while (cursor < text.length) {
|
|
331
|
+
const nextMatch = findNextInlineMatch(text, cursor);
|
|
332
|
+
if (!nextMatch) {
|
|
333
|
+
pushSegment(segments, text.slice(cursor), inheritedStyles);
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
if (nextMatch.match.index > cursor) pushSegment(segments, text.slice(cursor, nextMatch.match.index), inheritedStyles);
|
|
337
|
+
const combinedStyles = [...inheritedStyles, ...nextMatch.styles];
|
|
338
|
+
if (nextMatch.marker.literal) pushSegment(segments, nextMatch.text, combinedStyles);
|
|
339
|
+
else segments.push(...parseInlineSegments(nextMatch.text, combinedStyles));
|
|
340
|
+
cursor = nextMatch.match.index + nextMatch.match[0].length;
|
|
341
|
+
}
|
|
342
|
+
return segments;
|
|
343
|
+
}
|
|
344
|
+
function findNextInlineMatch(text, startIndex) {
|
|
345
|
+
let bestMatch = null;
|
|
346
|
+
for (const [priority, marker] of INLINE_MARKERS.entries()) {
|
|
347
|
+
const regex = new RegExp(marker.pattern.source, marker.pattern.flags);
|
|
348
|
+
regex.lastIndex = startIndex;
|
|
349
|
+
const match = regex.exec(text);
|
|
350
|
+
if (!match) continue;
|
|
351
|
+
if (bestMatch && (match.index > bestMatch.match.index || match.index === bestMatch.match.index && priority > bestMatch.priority)) continue;
|
|
352
|
+
bestMatch = {
|
|
353
|
+
match,
|
|
354
|
+
marker,
|
|
355
|
+
text: marker.extractText(match),
|
|
356
|
+
styles: marker.resolveStyles?.(match) ?? [],
|
|
357
|
+
priority
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
return bestMatch;
|
|
361
|
+
}
|
|
362
|
+
function pushSegment(segments, text, styles) {
|
|
363
|
+
if (!text) return;
|
|
364
|
+
const lastSegment = segments.at(-1);
|
|
365
|
+
if (lastSegment && sameStyles(lastSegment.styles, styles)) {
|
|
366
|
+
lastSegment.text += text;
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
segments.push({
|
|
370
|
+
text,
|
|
371
|
+
styles: [...styles]
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
function sameStyles(left, right) {
|
|
375
|
+
return left.length === right.length && left.every((style, index) => style === right[index]);
|
|
376
|
+
}
|
|
377
|
+
function normalizeCodeBlockLeadingWhitespace(line) {
|
|
378
|
+
return line.replace(/^[ \t]+/, (leadingWhitespace) => leadingWhitespace.replace(/\t/g, "\xA0\xA0\xA0\xA0").replace(/ /g, "\xA0"));
|
|
379
|
+
}
|
|
380
|
+
function isIndentedCodeBlockLine(line) {
|
|
381
|
+
return /^(?: {4,}|\t)/.test(line);
|
|
382
|
+
}
|
|
383
|
+
function stripCodeFenceIndent(line, indent) {
|
|
384
|
+
let consumed = 0;
|
|
385
|
+
let cursor = 0;
|
|
386
|
+
while (cursor < line.length && consumed < indent && line[cursor] === " ") {
|
|
387
|
+
cursor += 1;
|
|
388
|
+
consumed += 1;
|
|
389
|
+
}
|
|
390
|
+
return line.slice(cursor);
|
|
391
|
+
}
|
|
392
|
+
//#endregion
|
|
393
|
+
//#region extensions/zalouser/src/send.ts
|
|
394
|
+
const ZALO_TEXT_LIMIT = 2e3;
|
|
395
|
+
const DEFAULT_TEXT_CHUNK_MODE = "length";
|
|
396
|
+
async function sendMessageZalouser(threadId, text, options = {}) {
|
|
397
|
+
const prepared = options.textMode === "markdown" ? parseZalouserTextStyles(text) : {
|
|
398
|
+
text,
|
|
399
|
+
styles: options.textStyles
|
|
400
|
+
};
|
|
401
|
+
const textChunkLimit = options.textChunkLimit ?? ZALO_TEXT_LIMIT;
|
|
402
|
+
const chunks = splitStyledText(prepared.text, (prepared.styles?.length ?? 0) > 0 ? prepared.styles : void 0, textChunkLimit, options.textChunkMode);
|
|
403
|
+
let lastResult = null;
|
|
404
|
+
for (const [index, chunk] of chunks.entries()) {
|
|
405
|
+
const chunkOptions = index === 0 ? {
|
|
406
|
+
...options,
|
|
407
|
+
textStyles: chunk.styles
|
|
408
|
+
} : {
|
|
409
|
+
...options,
|
|
410
|
+
caption: void 0,
|
|
411
|
+
mediaLocalRoots: void 0,
|
|
412
|
+
mediaUrl: void 0,
|
|
413
|
+
textStyles: chunk.styles
|
|
414
|
+
};
|
|
415
|
+
const result = await sendZaloTextMessage(threadId, chunk.text, chunkOptions);
|
|
416
|
+
if (!result.ok) return result;
|
|
417
|
+
lastResult = result;
|
|
418
|
+
}
|
|
419
|
+
return lastResult ?? {
|
|
420
|
+
ok: false,
|
|
421
|
+
error: "No message content provided"
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
async function sendImageZalouser(threadId, imageUrl, options = {}) {
|
|
425
|
+
return await sendMessageZalouser(threadId, options.caption ?? "", {
|
|
426
|
+
...options,
|
|
427
|
+
caption: void 0,
|
|
428
|
+
mediaUrl: imageUrl
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
async function sendLinkZalouser(threadId, url, options = {}) {
|
|
432
|
+
return await sendZaloLink(threadId, url, options);
|
|
433
|
+
}
|
|
434
|
+
async function sendTypingZalouser(threadId, options = {}) {
|
|
435
|
+
await sendZaloTypingEvent(threadId, options);
|
|
436
|
+
}
|
|
437
|
+
async function sendReactionZalouser(params) {
|
|
438
|
+
const result = await sendZaloReaction({
|
|
439
|
+
profile: params.profile,
|
|
440
|
+
threadId: params.threadId,
|
|
441
|
+
isGroup: params.isGroup,
|
|
442
|
+
msgId: params.msgId,
|
|
443
|
+
cliMsgId: params.cliMsgId,
|
|
444
|
+
emoji: params.emoji,
|
|
445
|
+
remove: params.remove
|
|
446
|
+
});
|
|
447
|
+
return {
|
|
448
|
+
ok: result.ok,
|
|
449
|
+
error: result.error
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
async function sendDeliveredZalouser(params) {
|
|
453
|
+
await sendZaloDeliveredEvent(params);
|
|
454
|
+
}
|
|
455
|
+
async function sendSeenZalouser(params) {
|
|
456
|
+
await sendZaloSeenEvent(params);
|
|
457
|
+
}
|
|
458
|
+
function splitStyledText(text, styles, limit, mode) {
|
|
459
|
+
if (text.length === 0) return [{
|
|
460
|
+
text,
|
|
461
|
+
styles: void 0
|
|
462
|
+
}];
|
|
463
|
+
const chunks = [];
|
|
464
|
+
for (const range of splitTextRanges(text, limit, mode ?? DEFAULT_TEXT_CHUNK_MODE)) {
|
|
465
|
+
const { start, end } = range;
|
|
466
|
+
chunks.push({
|
|
467
|
+
text: text.slice(start, end),
|
|
468
|
+
styles: sliceTextStyles(styles, start, end)
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
return chunks;
|
|
472
|
+
}
|
|
473
|
+
function sliceTextStyles(styles, start, end) {
|
|
474
|
+
if (!styles || styles.length === 0) return;
|
|
475
|
+
const chunkStyles = styles.map((style) => {
|
|
476
|
+
const overlapStart = Math.max(style.start, start);
|
|
477
|
+
const overlapEnd = Math.min(style.start + style.len, end);
|
|
478
|
+
if (overlapEnd <= overlapStart) return null;
|
|
479
|
+
if (style.st === TextStyle.Indent) return {
|
|
480
|
+
start: overlapStart - start,
|
|
481
|
+
len: overlapEnd - overlapStart,
|
|
482
|
+
st: style.st,
|
|
483
|
+
indentSize: style.indentSize
|
|
484
|
+
};
|
|
485
|
+
return {
|
|
486
|
+
start: overlapStart - start,
|
|
487
|
+
len: overlapEnd - overlapStart,
|
|
488
|
+
st: style.st
|
|
489
|
+
};
|
|
490
|
+
}).filter((style) => style !== null);
|
|
491
|
+
return chunkStyles.length > 0 ? chunkStyles : void 0;
|
|
492
|
+
}
|
|
493
|
+
function splitTextRanges(text, limit, mode) {
|
|
494
|
+
if (mode === "newline") return splitTextRangesByPreferredBreaks(text, limit);
|
|
495
|
+
const ranges = [];
|
|
496
|
+
for (let start = 0; start < text.length; start += limit) ranges.push({
|
|
497
|
+
start,
|
|
498
|
+
end: Math.min(text.length, start + limit)
|
|
499
|
+
});
|
|
500
|
+
return ranges;
|
|
501
|
+
}
|
|
502
|
+
function splitTextRangesByPreferredBreaks(text, limit) {
|
|
503
|
+
const ranges = [];
|
|
504
|
+
let start = 0;
|
|
505
|
+
while (start < text.length) {
|
|
506
|
+
const maxEnd = Math.min(text.length, start + limit);
|
|
507
|
+
let end = maxEnd;
|
|
508
|
+
if (maxEnd < text.length) end = findParagraphBreak(text, start, maxEnd) ?? findLastBreak(text, "\n", start, maxEnd) ?? findLastWhitespaceBreak(text, start, maxEnd) ?? maxEnd;
|
|
509
|
+
if (end <= start) end = maxEnd;
|
|
510
|
+
ranges.push({
|
|
511
|
+
start,
|
|
512
|
+
end
|
|
513
|
+
});
|
|
514
|
+
start = end;
|
|
515
|
+
}
|
|
516
|
+
return ranges;
|
|
517
|
+
}
|
|
518
|
+
function findParagraphBreak(text, start, end) {
|
|
519
|
+
const matches = text.slice(start, end).matchAll(/\n[\t ]*\n+/g);
|
|
520
|
+
let lastMatch;
|
|
521
|
+
for (const match of matches) lastMatch = match;
|
|
522
|
+
if (!lastMatch || lastMatch.index === void 0) return;
|
|
523
|
+
return start + lastMatch.index + lastMatch[0].length;
|
|
524
|
+
}
|
|
525
|
+
function findLastBreak(text, marker, start, end) {
|
|
526
|
+
const index = text.lastIndexOf(marker, end - 1);
|
|
527
|
+
if (index < start) return;
|
|
528
|
+
return index + marker.length;
|
|
529
|
+
}
|
|
530
|
+
function findLastWhitespaceBreak(text, start, end) {
|
|
531
|
+
for (let index = end - 1; index > start; index -= 1) if (/\s/.test(text[index])) return index + 1;
|
|
532
|
+
}
|
|
533
|
+
//#endregion
|
|
534
|
+
export { sendReactionZalouser as a, sendMessageZalouser as i, sendImageZalouser as n, sendSeenZalouser as o, sendLinkZalouser as r, sendTypingZalouser as s, sendDeliveredZalouser as t };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/text-runtime";
|
|
2
|
+
import { buildChannelOutboundSessionRoute } from "openclaw/plugin-sdk/core";
|
|
3
|
+
//#region extensions/zalouser/src/session-route.ts
|
|
4
|
+
function stripZalouserTargetPrefix(raw) {
|
|
5
|
+
return raw.trim().replace(/^(zalouser|zlu):/i, "").trim();
|
|
6
|
+
}
|
|
7
|
+
function normalizeZalouserTarget(raw) {
|
|
8
|
+
const trimmed = stripZalouserTargetPrefix(raw);
|
|
9
|
+
if (!trimmed) return;
|
|
10
|
+
const lower = normalizeLowercaseStringOrEmpty(trimmed);
|
|
11
|
+
if (lower.startsWith("group:")) {
|
|
12
|
+
const id = trimmed.slice(6).trim();
|
|
13
|
+
return id ? `group:${id}` : void 0;
|
|
14
|
+
}
|
|
15
|
+
if (lower.startsWith("g:")) {
|
|
16
|
+
const id = trimmed.slice(2).trim();
|
|
17
|
+
return id ? `group:${id}` : void 0;
|
|
18
|
+
}
|
|
19
|
+
if (lower.startsWith("user:")) {
|
|
20
|
+
const id = trimmed.slice(5).trim();
|
|
21
|
+
return id ? `user:${id}` : void 0;
|
|
22
|
+
}
|
|
23
|
+
if (lower.startsWith("dm:")) {
|
|
24
|
+
const id = trimmed.slice(3).trim();
|
|
25
|
+
return id ? `user:${id}` : void 0;
|
|
26
|
+
}
|
|
27
|
+
if (lower.startsWith("u:")) {
|
|
28
|
+
const id = trimmed.slice(2).trim();
|
|
29
|
+
return id ? `user:${id}` : void 0;
|
|
30
|
+
}
|
|
31
|
+
if (/^g-\S+$/i.test(trimmed)) return `group:${trimmed}`;
|
|
32
|
+
if (/^u-\S+$/i.test(trimmed)) return `user:${trimmed}`;
|
|
33
|
+
return trimmed;
|
|
34
|
+
}
|
|
35
|
+
function parseZalouserOutboundTarget(raw) {
|
|
36
|
+
const normalized = normalizeZalouserTarget(raw);
|
|
37
|
+
if (!normalized) throw new Error("Zalouser target is required");
|
|
38
|
+
const lowered = normalizeLowercaseStringOrEmpty(normalized);
|
|
39
|
+
if (lowered.startsWith("group:")) {
|
|
40
|
+
const threadId = normalized.slice(6).trim();
|
|
41
|
+
if (!threadId) throw new Error("Zalouser group target is missing group id");
|
|
42
|
+
return {
|
|
43
|
+
threadId,
|
|
44
|
+
isGroup: true
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (lowered.startsWith("user:")) {
|
|
48
|
+
const threadId = normalized.slice(5).trim();
|
|
49
|
+
if (!threadId) throw new Error("Zalouser user target is missing user id");
|
|
50
|
+
return {
|
|
51
|
+
threadId,
|
|
52
|
+
isGroup: false
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
threadId: normalized,
|
|
57
|
+
isGroup: false
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function parseZalouserDirectoryGroupId(raw) {
|
|
61
|
+
const normalized = normalizeZalouserTarget(raw);
|
|
62
|
+
if (!normalized) throw new Error("Zalouser group target is required");
|
|
63
|
+
const lowered = normalizeLowercaseStringOrEmpty(normalized);
|
|
64
|
+
if (lowered.startsWith("group:")) {
|
|
65
|
+
const groupId = normalized.slice(6).trim();
|
|
66
|
+
if (!groupId) throw new Error("Zalouser group target is missing group id");
|
|
67
|
+
return groupId;
|
|
68
|
+
}
|
|
69
|
+
if (lowered.startsWith("user:")) throw new Error("Zalouser group members lookup requires a group target (group:<id>)");
|
|
70
|
+
return normalized;
|
|
71
|
+
}
|
|
72
|
+
function resolveZalouserOutboundSessionRoute(params) {
|
|
73
|
+
const normalized = normalizeZalouserTarget(params.target);
|
|
74
|
+
if (!normalized) return null;
|
|
75
|
+
const isGroup = (normalizeOptionalLowercaseString(normalized) ?? "").startsWith("group:");
|
|
76
|
+
const peerId = normalized.replace(/^(group|user):/i, "").trim();
|
|
77
|
+
return buildChannelOutboundSessionRoute({
|
|
78
|
+
cfg: params.cfg,
|
|
79
|
+
agentId: params.agentId,
|
|
80
|
+
channel: "zalouser",
|
|
81
|
+
accountId: params.accountId,
|
|
82
|
+
peer: {
|
|
83
|
+
kind: isGroup ? "group" : "direct",
|
|
84
|
+
id: peerId
|
|
85
|
+
},
|
|
86
|
+
chatType: isGroup ? "group" : "direct",
|
|
87
|
+
from: isGroup ? `zalouser:group:${peerId}` : `zalouser:${peerId}`,
|
|
88
|
+
to: `zalouser:${peerId}`
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
export { resolveZalouserOutboundSessionRoute as i, parseZalouserDirectoryGroupId as n, parseZalouserOutboundTarget as r, normalizeZalouserTarget as t };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import fsp from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
|
|
4
|
+
import { createDelegatedSetupWizardProxy, createPatchedAccountSetupAdapter } from "openclaw/plugin-sdk/setup-runtime";
|
|
5
|
+
//#region extensions/zalouser/src/qr-temp-file.ts
|
|
6
|
+
async function writeQrDataUrlToTempFile(qrDataUrl, profile) {
|
|
7
|
+
const base64 = (qrDataUrl.trim().match(/^data:image\/png;base64,(.+)$/i)?.[1] ?? "").trim();
|
|
8
|
+
if (!base64) return null;
|
|
9
|
+
const safeProfile = profile.replace(/[^a-zA-Z0-9_-]+/g, "-") || "default";
|
|
10
|
+
const filePath = path.join(resolvePreferredOpenClawTmpDir(), `openclaw-zalouser-qr-${safeProfile}.png`);
|
|
11
|
+
await fsp.writeFile(filePath, Buffer.from(base64, "base64"));
|
|
12
|
+
return filePath;
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region extensions/zalouser/src/setup-core.ts
|
|
16
|
+
const channel = "zalouser";
|
|
17
|
+
const zalouserSetupAdapter = createPatchedAccountSetupAdapter({
|
|
18
|
+
channelKey: channel,
|
|
19
|
+
validateInput: () => null,
|
|
20
|
+
buildPatch: () => ({})
|
|
21
|
+
});
|
|
22
|
+
function createZalouserSetupWizardProxy(loadWizard) {
|
|
23
|
+
return createDelegatedSetupWizardProxy({
|
|
24
|
+
channel,
|
|
25
|
+
loadWizard,
|
|
26
|
+
status: {
|
|
27
|
+
configuredLabel: "logged in",
|
|
28
|
+
unconfiguredLabel: "needs QR login",
|
|
29
|
+
configuredHint: "recommended · logged in",
|
|
30
|
+
unconfiguredHint: "recommended · QR login",
|
|
31
|
+
configuredScore: 1,
|
|
32
|
+
unconfiguredScore: 15
|
|
33
|
+
},
|
|
34
|
+
credentials: [],
|
|
35
|
+
delegatePrepare: true,
|
|
36
|
+
delegateFinalize: true
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { zalouserSetupAdapter as n, writeQrDataUrlToTempFile as r, createZalouserSetupWizardProxy as t };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entry-contract";
|
|
2
|
+
//#region extensions/zalouser/setup-entry.ts
|
|
3
|
+
var setup_entry_default = defineBundledChannelSetupEntry({
|
|
4
|
+
importMetaUrl: import.meta.url,
|
|
5
|
+
plugin: {
|
|
6
|
+
specifier: "./setup-plugin-api.js",
|
|
7
|
+
exportName: "zalouserSetupPlugin"
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
//#endregion
|
|
11
|
+
export { setup_entry_default as default };
|