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
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createGeweAccountMethod,
|
|
3
|
+
type GeweApiList,
|
|
4
|
+
type GeweApiObject,
|
|
5
|
+
} from "./gewe-account-api.js";
|
|
6
|
+
|
|
7
|
+
type GeweTagId = string | number;
|
|
8
|
+
|
|
9
|
+
type GeweSnsVisibilityParams = {
|
|
10
|
+
allowWxIds?: string[];
|
|
11
|
+
atWxIds?: string[];
|
|
12
|
+
disableWxIds?: string[];
|
|
13
|
+
privacy?: number;
|
|
14
|
+
allowTagIds?: GeweTagId[];
|
|
15
|
+
disableTagIds?: GeweTagId[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type GeweSnsImageInfo = GeweApiObject;
|
|
19
|
+
export type GeweSnsVideoInfo = GeweApiObject;
|
|
20
|
+
|
|
21
|
+
export const uploadSnsImageGewe = createGeweAccountMethod<{
|
|
22
|
+
imgUrls: string[];
|
|
23
|
+
}>("/gewe/v2/api/sns/uploadSnsImage");
|
|
24
|
+
|
|
25
|
+
export const uploadSnsVideoGewe = createGeweAccountMethod<{
|
|
26
|
+
thumbUrl: string;
|
|
27
|
+
videoUrl: string;
|
|
28
|
+
}>("/gewe/v2/api/sns/uploadSnsVideo");
|
|
29
|
+
|
|
30
|
+
export const downloadSnsVideoGewe = createGeweAccountMethod<{
|
|
31
|
+
snsXml: string;
|
|
32
|
+
}>("/gewe/v2/api/sns/downloadSnsVideo");
|
|
33
|
+
|
|
34
|
+
export const delSnsGewe = createGeweAccountMethod<{
|
|
35
|
+
snsId: string;
|
|
36
|
+
}>("/gewe/v2/api/sns/delSns");
|
|
37
|
+
|
|
38
|
+
export const sendImgSnsGewe = createGeweAccountMethod<
|
|
39
|
+
GeweSnsVisibilityParams & {
|
|
40
|
+
content?: string;
|
|
41
|
+
imgInfos: GeweSnsImageInfo[];
|
|
42
|
+
}
|
|
43
|
+
>("/gewe/v2/api/sns/sendImgSns");
|
|
44
|
+
|
|
45
|
+
export const sendTextSnsGewe = createGeweAccountMethod<
|
|
46
|
+
GeweSnsVisibilityParams & {
|
|
47
|
+
content: string;
|
|
48
|
+
}
|
|
49
|
+
>("/gewe/v2/api/sns/sendTextSns");
|
|
50
|
+
|
|
51
|
+
export const sendVideoSnsGewe = createGeweAccountMethod<
|
|
52
|
+
GeweSnsVisibilityParams & {
|
|
53
|
+
content?: string;
|
|
54
|
+
videoInfo: GeweSnsVideoInfo;
|
|
55
|
+
}
|
|
56
|
+
>("/gewe/v2/api/sns/sendVideoSns");
|
|
57
|
+
|
|
58
|
+
export const sendUrlSnsGewe = createGeweAccountMethod<
|
|
59
|
+
GeweSnsVisibilityParams & {
|
|
60
|
+
content?: string;
|
|
61
|
+
thumbUrl: string;
|
|
62
|
+
linkUrl: string;
|
|
63
|
+
title: string;
|
|
64
|
+
description: string;
|
|
65
|
+
}
|
|
66
|
+
>("/gewe/v2/api/sns/sendUrlSns");
|
|
67
|
+
|
|
68
|
+
export const strangerVisibilityEnabledGewe = createGeweAccountMethod<{
|
|
69
|
+
enabled: boolean;
|
|
70
|
+
}>("/gewe/v2/api/sns/strangerVisibilityEnabled");
|
|
71
|
+
|
|
72
|
+
export const snsDetailsGewe = createGeweAccountMethod<{
|
|
73
|
+
snsId: string;
|
|
74
|
+
}>("/gewe/v2/api/sns/snsDetails");
|
|
75
|
+
|
|
76
|
+
export const likeSnsGewe = createGeweAccountMethod<{
|
|
77
|
+
snsId: string;
|
|
78
|
+
operType: number;
|
|
79
|
+
wxid: string;
|
|
80
|
+
}>("/gewe/v2/api/sns/likeSns");
|
|
81
|
+
|
|
82
|
+
export const contactsSnsListGewe = createGeweAccountMethod<{
|
|
83
|
+
wxid: string;
|
|
84
|
+
maxId?: string | number;
|
|
85
|
+
decrypt?: boolean;
|
|
86
|
+
firstPageMd5?: string;
|
|
87
|
+
}>("/gewe/v2/api/sns/contactsSnsList");
|
|
88
|
+
|
|
89
|
+
export const snsListGewe = createGeweAccountMethod<{
|
|
90
|
+
maxId?: string | number;
|
|
91
|
+
decrypt?: boolean;
|
|
92
|
+
firstPageMd5?: string;
|
|
93
|
+
}>("/gewe/v2/api/sns/snsList");
|
|
94
|
+
|
|
95
|
+
export const snsVisibleScopeGewe = createGeweAccountMethod<{
|
|
96
|
+
option: number;
|
|
97
|
+
}>("/gewe/v2/api/sns/snsVisibleScope");
|
|
98
|
+
|
|
99
|
+
export const snsSetPrivacyGewe = createGeweAccountMethod<{
|
|
100
|
+
snsId: string;
|
|
101
|
+
open: boolean;
|
|
102
|
+
}>("/gewe/v2/api/sns/snsSetPrivacy");
|
|
103
|
+
|
|
104
|
+
export const commentSnsGewe = createGeweAccountMethod<{
|
|
105
|
+
snsId: string;
|
|
106
|
+
operType: number;
|
|
107
|
+
wxid: string;
|
|
108
|
+
commentId?: string | number;
|
|
109
|
+
content?: string;
|
|
110
|
+
}>("/gewe/v2/api/sns/commentSns");
|
|
111
|
+
|
|
112
|
+
export const forwardSnsGewe = createGeweAccountMethod<
|
|
113
|
+
Pick<GeweSnsVisibilityParams, "allowWxIds" | "atWxIds" | "disableWxIds" | "privacy"> & {
|
|
114
|
+
snsXml: string;
|
|
115
|
+
}
|
|
116
|
+
>("/gewe/v2/api/sns/forwardSns");
|
|
117
|
+
|
|
118
|
+
export const geweMomentsApi = {
|
|
119
|
+
uploadSnsImage: uploadSnsImageGewe,
|
|
120
|
+
uploadSnsVideo: uploadSnsVideoGewe,
|
|
121
|
+
downloadSnsVideo: downloadSnsVideoGewe,
|
|
122
|
+
delSns: delSnsGewe,
|
|
123
|
+
sendImgSns: sendImgSnsGewe,
|
|
124
|
+
sendTextSns: sendTextSnsGewe,
|
|
125
|
+
sendVideoSns: sendVideoSnsGewe,
|
|
126
|
+
sendUrlSns: sendUrlSnsGewe,
|
|
127
|
+
strangerVisibilityEnabled: strangerVisibilityEnabledGewe,
|
|
128
|
+
snsDetails: snsDetailsGewe,
|
|
129
|
+
likeSns: likeSnsGewe,
|
|
130
|
+
contactsSnsList: contactsSnsListGewe,
|
|
131
|
+
snsList: snsListGewe,
|
|
132
|
+
snsVisibleScope: snsVisibleScopeGewe,
|
|
133
|
+
snsSetPrivacy: snsSetPrivacyGewe,
|
|
134
|
+
commentSns: commentSnsGewe,
|
|
135
|
+
forwardSns: forwardSnsGewe,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export type GeweSnsList = GeweApiList;
|
package/src/monitor.ts
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http";
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_WEBHOOK_BODY_TIMEOUT_MS,
|
|
5
|
+
DEFAULT_WEBHOOK_MAX_BODY_BYTES,
|
|
6
|
+
readJsonBodyWithLimit,
|
|
7
|
+
type OpenClawConfig,
|
|
8
|
+
type RuntimeEnv,
|
|
9
|
+
} from "./openclaw-compat.js";
|
|
4
10
|
|
|
5
11
|
import { resolveGeweAccount } from "./accounts.js";
|
|
12
|
+
import { parseGeweJsonText } from "./api.js";
|
|
6
13
|
import { GeweDownloadQueue } from "./download-queue.js";
|
|
7
14
|
import { createGeweInboundDebouncer } from "./inbound-batch.js";
|
|
8
15
|
import { handleGeweInboundBatch } from "./inbound.js";
|
|
9
16
|
import { createGeweMediaServer, DEFAULT_MEDIA_HOST, DEFAULT_MEDIA_PATH, DEFAULT_MEDIA_PORT } from "./media-server.js";
|
|
17
|
+
import { maybeHandleGeweMediaRequest } from "./media-server.js";
|
|
10
18
|
import { getGeweRuntime } from "./runtime.js";
|
|
11
19
|
import type {
|
|
12
20
|
CoreConfig,
|
|
@@ -40,20 +48,18 @@ function isDuplicate(key: string): boolean {
|
|
|
40
48
|
return false;
|
|
41
49
|
}
|
|
42
50
|
|
|
51
|
+
export function buildGeweInboundDedupeKey(params: {
|
|
52
|
+
accountId: string;
|
|
53
|
+
message: Pick<GeweInboundMessage, "appId" | "newMessageId">;
|
|
54
|
+
}): string {
|
|
55
|
+
return `${params.accountId}:${params.message.appId}:${params.message.newMessageId}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
43
58
|
function formatError(err: unknown): string {
|
|
44
59
|
if (err instanceof Error) return err.message;
|
|
45
60
|
return typeof err === "string" ? err : JSON.stringify(err);
|
|
46
61
|
}
|
|
47
62
|
|
|
48
|
-
function readBody(req: IncomingMessage): Promise<string> {
|
|
49
|
-
return new Promise((resolve, reject) => {
|
|
50
|
-
const chunks: Buffer[] = [];
|
|
51
|
-
req.on("data", (chunk: Buffer) => chunks.push(chunk));
|
|
52
|
-
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
|
|
53
|
-
req.on("error", reject);
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
63
|
function resolveWebhookToken(req: IncomingMessage): string | undefined {
|
|
58
64
|
const headers = req.headers as Record<string, string | string[] | undefined>;
|
|
59
65
|
const candidates = [
|
|
@@ -122,7 +128,7 @@ function resolveSenderName(pushContent?: string): string | undefined {
|
|
|
122
128
|
|
|
123
129
|
function parseWebhookPayload(body: string): GeweCallbackPayload | null {
|
|
124
130
|
try {
|
|
125
|
-
const data =
|
|
131
|
+
const data = parseGeweJsonText<GeweCallbackPayload>(body);
|
|
126
132
|
return data as GeweCallbackPayload;
|
|
127
133
|
} catch {
|
|
128
134
|
return null;
|
|
@@ -172,7 +178,7 @@ export function createGeweWebhookServer(opts: GeweWebhookServerOptions): {
|
|
|
172
178
|
start: () => Promise<void>;
|
|
173
179
|
stop: () => void;
|
|
174
180
|
} {
|
|
175
|
-
const { port, host, path, secret, onMessage, onError, abortSignal } = opts;
|
|
181
|
+
const { port, host, path, mediaPath, secret, onMessage, onError, abortSignal } = opts;
|
|
176
182
|
|
|
177
183
|
const server = createServer(async (req: IncomingMessage, res: ServerResponse) => {
|
|
178
184
|
if (req.url === HEALTH_PATH) {
|
|
@@ -181,6 +187,17 @@ export function createGeweWebhookServer(opts: GeweWebhookServerOptions): {
|
|
|
181
187
|
return;
|
|
182
188
|
}
|
|
183
189
|
|
|
190
|
+
if (
|
|
191
|
+
mediaPath &&
|
|
192
|
+
(await maybeHandleGeweMediaRequest({
|
|
193
|
+
req,
|
|
194
|
+
res,
|
|
195
|
+
path: mediaPath,
|
|
196
|
+
}))
|
|
197
|
+
) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
184
201
|
if (req.url?.split("?")[0] !== path || req.method !== "POST") {
|
|
185
202
|
res.writeHead(404);
|
|
186
203
|
res.end();
|
|
@@ -195,8 +212,25 @@ export function createGeweWebhookServer(opts: GeweWebhookServerOptions): {
|
|
|
195
212
|
}
|
|
196
213
|
|
|
197
214
|
try {
|
|
198
|
-
const
|
|
199
|
-
|
|
215
|
+
const bodyResult = await readJsonBodyWithLimit(req, {
|
|
216
|
+
maxBytes: DEFAULT_WEBHOOK_MAX_BODY_BYTES,
|
|
217
|
+
timeoutMs: DEFAULT_WEBHOOK_BODY_TIMEOUT_MS,
|
|
218
|
+
emptyObjectOnEmpty: false,
|
|
219
|
+
});
|
|
220
|
+
if (!bodyResult.ok) {
|
|
221
|
+
res.writeHead(
|
|
222
|
+
bodyResult.code === "PAYLOAD_TOO_LARGE"
|
|
223
|
+
? 413
|
|
224
|
+
: bodyResult.code === "REQUEST_BODY_TIMEOUT"
|
|
225
|
+
? 408
|
|
226
|
+
: 400,
|
|
227
|
+
{ "Content-Type": "application/json" },
|
|
228
|
+
);
|
|
229
|
+
res.end(JSON.stringify({ error: bodyResult.error || "Invalid JSON payload" }));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const payload = parseWebhookPayload(bodyResult.raw);
|
|
200
234
|
if (!payload) {
|
|
201
235
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
202
236
|
res.end(JSON.stringify({ error: "Invalid JSON payload" }));
|
|
@@ -228,8 +262,20 @@ export function createGeweWebhookServer(opts: GeweWebhookServerOptions): {
|
|
|
228
262
|
});
|
|
229
263
|
|
|
230
264
|
const start = (): Promise<void> => {
|
|
231
|
-
return new Promise((resolve) => {
|
|
232
|
-
|
|
265
|
+
return new Promise((resolve, reject) => {
|
|
266
|
+
const onError = (err: Error) => {
|
|
267
|
+
reject(err);
|
|
268
|
+
};
|
|
269
|
+
server.once("error", onError);
|
|
270
|
+
try {
|
|
271
|
+
server.listen(port, host, () => {
|
|
272
|
+
server.off("error", onError);
|
|
273
|
+
resolve();
|
|
274
|
+
});
|
|
275
|
+
} catch (err) {
|
|
276
|
+
server.off("error", onError);
|
|
277
|
+
reject(err);
|
|
278
|
+
}
|
|
233
279
|
});
|
|
234
280
|
};
|
|
235
281
|
|
|
@@ -259,9 +305,12 @@ export async function monitorGeweProvider(
|
|
|
259
305
|
const core = getGeweRuntime();
|
|
260
306
|
const cfg = opts.config ?? (core.config.loadConfig() as CoreConfig);
|
|
261
307
|
const account = opts.account ?? resolveGeweAccount({ cfg, accountId: opts.accountId });
|
|
308
|
+
const fallbackLogger = core.logging.getChildLogger();
|
|
309
|
+
const formatRuntimeArgs = (args: unknown[]) =>
|
|
310
|
+
args.map((arg) => (typeof arg === "string" ? arg : String(arg))).join(" ");
|
|
262
311
|
const runtime: RuntimeEnv = opts.runtime ?? {
|
|
263
|
-
log: (
|
|
264
|
-
error: (
|
|
312
|
+
log: (...args: unknown[]) => fallbackLogger.info(formatRuntimeArgs(args)),
|
|
313
|
+
error: (...args: unknown[]) => fallbackLogger.error(formatRuntimeArgs(args)),
|
|
265
314
|
exit: () => {
|
|
266
315
|
throw new Error("Runtime exit not available");
|
|
267
316
|
},
|
|
@@ -276,6 +325,10 @@ export async function monitorGeweProvider(
|
|
|
276
325
|
const rawPath = account.config.webhookPath?.trim() || DEFAULT_WEBHOOK_PATH;
|
|
277
326
|
const path = rawPath.startsWith("/") ? rawPath : `/${rawPath}`;
|
|
278
327
|
const secret = account.config.webhookSecret?.trim() || undefined;
|
|
328
|
+
const shouldStartMedia =
|
|
329
|
+
Boolean(account.config.mediaPublicUrl) ||
|
|
330
|
+
Boolean(account.config.mediaPort || account.config.mediaHost || account.config.mediaPath);
|
|
331
|
+
const mediaPath = account.config.mediaPath ?? DEFAULT_MEDIA_PATH;
|
|
279
332
|
|
|
280
333
|
const downloadQueue = new GeweDownloadQueue({
|
|
281
334
|
minDelayMs: account.config.downloadMinDelayMs,
|
|
@@ -302,12 +355,16 @@ export async function monitorGeweProvider(
|
|
|
302
355
|
port,
|
|
303
356
|
host,
|
|
304
357
|
path,
|
|
358
|
+
mediaPath: shouldStartMedia ? mediaPath : undefined,
|
|
305
359
|
secret,
|
|
306
360
|
onMessage: async (message) => {
|
|
307
361
|
const isSelf = message.fromId === message.botWxid || message.senderId === message.botWxid;
|
|
308
362
|
if (isSelf) return;
|
|
309
363
|
|
|
310
|
-
const dedupeKey =
|
|
364
|
+
const dedupeKey = buildGeweInboundDedupeKey({
|
|
365
|
+
accountId: account.accountId,
|
|
366
|
+
message,
|
|
367
|
+
});
|
|
311
368
|
if (isDuplicate(dedupeKey)) return;
|
|
312
369
|
opts.statusSink?.({ lastInboundAt: Date.now() });
|
|
313
370
|
|
|
@@ -318,18 +375,18 @@ export async function monitorGeweProvider(
|
|
|
318
375
|
});
|
|
319
376
|
|
|
320
377
|
await webhookServer.start();
|
|
321
|
-
|
|
378
|
+
const webhookPublicUrl =
|
|
379
|
+
account.config.webhookPublicUrl?.trim() ||
|
|
380
|
+
`http://${host === "0.0.0.0" ? "localhost" : host}:${port}${path}`;
|
|
381
|
+
runtime.log?.(`[${account.accountId}] GeWe webhook server listening on ${webhookPublicUrl}`);
|
|
322
382
|
|
|
323
383
|
let mediaStop: (() => void) | undefined;
|
|
324
|
-
const shouldStartMedia =
|
|
325
|
-
Boolean(account.config.mediaPublicUrl) ||
|
|
326
|
-
Boolean(account.config.mediaPort || account.config.mediaHost || account.config.mediaPath);
|
|
327
384
|
|
|
328
385
|
if (shouldStartMedia) {
|
|
329
386
|
const mediaServer = createGeweMediaServer({
|
|
330
387
|
host: account.config.mediaHost ?? DEFAULT_MEDIA_HOST,
|
|
331
388
|
port: account.config.mediaPort ?? DEFAULT_MEDIA_PORT,
|
|
332
|
-
path:
|
|
389
|
+
path: mediaPath,
|
|
333
390
|
abortSignal: opts.abortSignal,
|
|
334
391
|
});
|
|
335
392
|
await mediaServer.start();
|
package/src/onboarding.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_ACCOUNT_ID,
|
|
3
|
+
normalizeAccountId,
|
|
4
|
+
type ChannelPlugin,
|
|
5
|
+
type OpenClawConfig,
|
|
6
|
+
type WizardPrompter,
|
|
7
|
+
} from "./openclaw-compat.js";
|
|
3
8
|
|
|
4
9
|
import type { CoreConfig, GeweAccountConfig, ResolvedGeweAccount } from "./types.js";
|
|
5
10
|
import { resolveGeweAccount, resolveDefaultGeweAccountId, listGeweAccountIds } from "./accounts.js";
|
|
@@ -265,14 +270,14 @@ export const geweOnboarding: GeweOnboardingAdapter = {
|
|
|
265
270
|
placeholder: "gewe-openclaw/outbound",
|
|
266
271
|
initialValue: existing.s3KeyPrefix,
|
|
267
272
|
});
|
|
268
|
-
const s3UrlMode = await ctx.prompter.select({
|
|
273
|
+
const s3UrlMode = (await ctx.prompter.select({
|
|
269
274
|
message: "S3 URL mode",
|
|
270
275
|
options: [
|
|
271
276
|
{ value: "public", label: "public (default)" },
|
|
272
277
|
{ value: "presigned", label: "presigned" },
|
|
273
278
|
],
|
|
274
279
|
initialValue: existing.s3UrlMode ?? "public",
|
|
275
|
-
})
|
|
280
|
+
})) as NonNullable<GeweAccountConfig["s3UrlMode"]>;
|
|
276
281
|
const s3PublicBaseUrl =
|
|
277
282
|
s3UrlMode === "public"
|
|
278
283
|
? await ctx.prompter.text({
|