gewe-openclaw 2026.3.13 → 2026.3.23
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/README.md +455 -3
- package/index.ts +39 -1
- package/package.json +12 -1
- package/skills/gewe-agent-tools/SKILL.md +113 -0
- package/skills/gewe-channel-rules/SKILL.md +7 -0
- package/src/accounts.ts +51 -5
- package/src/api-tools.ts +1264 -0
- package/src/api.ts +37 -2
- package/src/binary-command.ts +65 -0
- package/src/channel-actions.ts +536 -0
- package/src/channel-allowlist.ts +150 -0
- package/src/channel-directory.ts +419 -0
- package/src/channel-status.ts +186 -0
- package/src/channel.ts +155 -58
- package/src/config-edit.ts +94 -0
- package/src/config-schema.ts +78 -3
- package/src/contacts-api.ts +113 -0
- package/src/delivery.ts +502 -62
- package/src/directory-cache.ts +164 -0
- package/src/gewe-account-api.ts +27 -0
- package/src/group-allowlist-tool.ts +242 -0
- package/src/group-binding-tool.ts +154 -0
- package/src/group-binding.ts +405 -0
- package/src/groups-api.ts +146 -0
- package/src/inbound-batch.ts +5 -2
- package/src/inbound.ts +248 -41
- package/src/media-server.ts +73 -93
- package/src/moments-api.ts +138 -0
- package/src/monitor.ts +81 -24
- package/src/onboarding.ts +9 -4
- package/src/openclaw-compat.ts +1070 -0
- package/src/pairing-store.ts +478 -0
- package/src/personal-api.ts +45 -0
- package/src/policy.ts +130 -22
- package/src/quote-context-cache.ts +97 -0
- package/src/reply-options.ts +101 -2
- package/src/s3.ts +1 -1
- package/src/send.ts +235 -16
- package/src/setup-wizard-types.ts +162 -0
- package/src/setup-wizard.ts +464 -0
- package/src/silk.ts +2 -1
- package/src/state-paths.ts +55 -14
- package/src/types.ts +66 -7
- package/src/xml.ts +158 -0
package/src/xml.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
|
|
1
3
|
function decodeEntities(value: string): string {
|
|
2
4
|
return value
|
|
3
5
|
.replace(/&/g, "&")
|
|
@@ -23,6 +25,93 @@ function stripCdata(value: string): string {
|
|
|
23
25
|
return trimmed;
|
|
24
26
|
}
|
|
25
27
|
|
|
28
|
+
export type GeweQuoteDetails = {
|
|
29
|
+
title?: string;
|
|
30
|
+
referType?: number;
|
|
31
|
+
svrid?: string;
|
|
32
|
+
fromUsr?: string;
|
|
33
|
+
chatUsr?: string;
|
|
34
|
+
displayName?: string;
|
|
35
|
+
content?: string;
|
|
36
|
+
msgSource?: string;
|
|
37
|
+
partialText?: GeweQuotePartialText;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type GeweQuotePartialText = {
|
|
41
|
+
start?: string;
|
|
42
|
+
end?: string;
|
|
43
|
+
startIndex?: number;
|
|
44
|
+
endIndex?: number;
|
|
45
|
+
quoteMd5?: string;
|
|
46
|
+
text?: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
function parseOptionalNumber(value?: string): number | undefined {
|
|
50
|
+
if (!value) return undefined;
|
|
51
|
+
const parsed = Number.parseInt(value, 10);
|
|
52
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function mapQuoteTypeLabel(type?: number): string {
|
|
56
|
+
switch (type) {
|
|
57
|
+
case 1:
|
|
58
|
+
return "文本";
|
|
59
|
+
case 3:
|
|
60
|
+
return "图片";
|
|
61
|
+
case 6:
|
|
62
|
+
return "文件";
|
|
63
|
+
case 43:
|
|
64
|
+
return "视频";
|
|
65
|
+
case 49:
|
|
66
|
+
return "卡片";
|
|
67
|
+
default:
|
|
68
|
+
return "消息";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function looksLikeXmlFragment(value?: string): boolean {
|
|
73
|
+
const trimmed = value?.trim();
|
|
74
|
+
if (!trimmed) return false;
|
|
75
|
+
return trimmed.startsWith("<") && trimmed.endsWith(">");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function md5Hex(value: string): string {
|
|
79
|
+
return crypto.createHash("md5").update(value, "utf8").digest("hex");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function resolvePartialQuoteText(params: {
|
|
83
|
+
content?: string;
|
|
84
|
+
partialText?: GeweQuotePartialText;
|
|
85
|
+
}): string | undefined {
|
|
86
|
+
const content = params.content?.trim();
|
|
87
|
+
const partialText = params.partialText;
|
|
88
|
+
if (!content || !partialText) return undefined;
|
|
89
|
+
if (looksLikeXmlFragment(content) || /<img[\s>]/i.test(content)) return undefined;
|
|
90
|
+
|
|
91
|
+
const start = partialText.start?.trim();
|
|
92
|
+
const end = partialText.end?.trim();
|
|
93
|
+
const quoteMd5 = partialText.quoteMd5?.trim().toLowerCase();
|
|
94
|
+
const rawText = partialText.text?.trim();
|
|
95
|
+
if (rawText && (!quoteMd5 || md5Hex(rawText) === quoteMd5)) {
|
|
96
|
+
return rawText;
|
|
97
|
+
}
|
|
98
|
+
if (!start || !end) return undefined;
|
|
99
|
+
|
|
100
|
+
const step = Math.max(start.length, 1);
|
|
101
|
+
for (let startPos = content.indexOf(start); startPos >= 0; startPos = content.indexOf(start, startPos + step)) {
|
|
102
|
+
const endStep = Math.max(end.length, 1);
|
|
103
|
+
for (let endPos = content.indexOf(end, startPos); endPos >= 0; endPos = content.indexOf(end, endPos + endStep)) {
|
|
104
|
+
const candidate = content.slice(startPos, endPos + end.length);
|
|
105
|
+
if (!candidate) continue;
|
|
106
|
+
if (!quoteMd5 || md5Hex(candidate) === quoteMd5) {
|
|
107
|
+
return candidate;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
26
115
|
export function extractXmlTag(xml: string, tag: string): string | undefined {
|
|
27
116
|
const re = new RegExp(`<${tag}>([\\s\\S]*?)<\\/${tag}>`, "i");
|
|
28
117
|
const match = re.exec(xml);
|
|
@@ -57,3 +146,72 @@ export function extractFileName(xml: string): string | undefined {
|
|
|
57
146
|
if (title) return title.trim();
|
|
58
147
|
return undefined;
|
|
59
148
|
}
|
|
149
|
+
|
|
150
|
+
export function extractQuoteDetails(xml: string): GeweQuoteDetails | undefined {
|
|
151
|
+
if (!/<refermsg>/i.test(xml)) return undefined;
|
|
152
|
+
const referXml = extractXmlTag(xml, "refermsg");
|
|
153
|
+
if (!referXml) return undefined;
|
|
154
|
+
const partialTextXml = extractXmlTag(referXml, "partialtext");
|
|
155
|
+
const partialText = partialTextXml
|
|
156
|
+
? ({
|
|
157
|
+
start: extractXmlTag(partialTextXml, "start"),
|
|
158
|
+
end: extractXmlTag(partialTextXml, "end"),
|
|
159
|
+
startIndex: parseOptionalNumber(extractXmlTag(partialTextXml, "startindex")),
|
|
160
|
+
endIndex: parseOptionalNumber(extractXmlTag(partialTextXml, "endindex")),
|
|
161
|
+
quoteMd5: extractXmlTag(partialTextXml, "quotemd5")?.toLowerCase(),
|
|
162
|
+
} as GeweQuotePartialText)
|
|
163
|
+
: undefined;
|
|
164
|
+
const details = {
|
|
165
|
+
title: extractXmlTag(xml, "title"),
|
|
166
|
+
referType: parseOptionalNumber(extractXmlTag(referXml, "type")),
|
|
167
|
+
svrid: extractXmlTag(referXml, "svrid"),
|
|
168
|
+
fromUsr: extractXmlTag(referXml, "fromusr"),
|
|
169
|
+
chatUsr: extractXmlTag(referXml, "chatusr"),
|
|
170
|
+
displayName: extractXmlTag(referXml, "displayname"),
|
|
171
|
+
content: extractXmlTag(referXml, "content"),
|
|
172
|
+
msgSource: extractXmlTag(referXml, "msgsource"),
|
|
173
|
+
partialText:
|
|
174
|
+
partialText &&
|
|
175
|
+
Object.fromEntries(
|
|
176
|
+
Object.entries({
|
|
177
|
+
...partialText,
|
|
178
|
+
text: resolvePartialQuoteText({
|
|
179
|
+
content: extractXmlTag(referXml, "content"),
|
|
180
|
+
partialText,
|
|
181
|
+
}),
|
|
182
|
+
}).filter(([, value]) => value !== undefined),
|
|
183
|
+
),
|
|
184
|
+
};
|
|
185
|
+
return Object.fromEntries(
|
|
186
|
+
Object.entries(details).filter(([, value]) => value !== undefined),
|
|
187
|
+
) as GeweQuoteDetails;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function extractQuoteSummary(xml: string):
|
|
191
|
+
| {
|
|
192
|
+
body: string;
|
|
193
|
+
quoteLabel: string;
|
|
194
|
+
}
|
|
195
|
+
| undefined {
|
|
196
|
+
const details = extractQuoteDetails(xml);
|
|
197
|
+
if (!details) return undefined;
|
|
198
|
+
const quoteLabel = mapQuoteTypeLabel(details.referType);
|
|
199
|
+
const parts = [];
|
|
200
|
+
const partialReferContent = details.partialText?.text?.trim();
|
|
201
|
+
const referContent = details.content?.trim();
|
|
202
|
+
const safeReferContent =
|
|
203
|
+
partialReferContent ||
|
|
204
|
+
(referContent && !looksLikeXmlFragment(referContent) && !/<img[\s>]/i.test(referContent)
|
|
205
|
+
? referContent
|
|
206
|
+
: undefined);
|
|
207
|
+
parts.push(
|
|
208
|
+
safeReferContent ? `[引用:${quoteLabel}] ${safeReferContent}` : `[引用:${quoteLabel}]`,
|
|
209
|
+
);
|
|
210
|
+
if (details.title?.trim()) {
|
|
211
|
+
parts.push(details.title.trim());
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
body: parts.join("\n").trim(),
|
|
215
|
+
quoteLabel,
|
|
216
|
+
};
|
|
217
|
+
}
|