@yaoqi10012/wechat-kf 0.3.1
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/LICENSE +21 -0
- package/dist/accounts.d.ts +38 -0
- package/dist/accounts.js +247 -0
- package/dist/accounts.js.map +1 -0
- package/dist/api.d.ts +58 -0
- package/dist/api.js +200 -0
- package/dist/api.js.map +1 -0
- package/dist/bot.d.ts +37 -0
- package/dist/bot.js +672 -0
- package/dist/bot.js.map +1 -0
- package/dist/channel.d.ts +14 -0
- package/dist/channel.js +320 -0
- package/dist/channel.js.map +1 -0
- package/dist/config-schema.d.ts +56 -0
- package/dist/config-schema.js +39 -0
- package/dist/config-schema.js.map +1 -0
- package/dist/constants.d.ts +41 -0
- package/dist/constants.js +51 -0
- package/dist/constants.js.map +1 -0
- package/dist/crypto.d.ts +18 -0
- package/dist/crypto.js +81 -0
- package/dist/crypto.js.map +1 -0
- package/dist/fs-utils.d.ts +7 -0
- package/dist/fs-utils.js +13 -0
- package/dist/fs-utils.js.map +1 -0
- package/dist/monitor.d.ts +31 -0
- package/dist/monitor.js +80 -0
- package/dist/monitor.js.map +1 -0
- package/dist/outbound.d.ts +32 -0
- package/dist/outbound.js +411 -0
- package/dist/outbound.js.map +1 -0
- package/dist/reply-dispatcher.d.ts +36 -0
- package/dist/reply-dispatcher.js +216 -0
- package/dist/reply-dispatcher.js.map +1 -0
- package/dist/runtime.d.ts +12 -0
- package/dist/runtime.js +23 -0
- package/dist/runtime.js.map +1 -0
- package/dist/send-utils.d.ts +52 -0
- package/dist/send-utils.js +217 -0
- package/dist/send-utils.js.map +1 -0
- package/dist/token.d.ts +8 -0
- package/dist/token.js +61 -0
- package/dist/token.js.map +1 -0
- package/dist/types.d.ts +236 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/unicode-format.d.ts +26 -0
- package/dist/unicode-format.js +157 -0
- package/dist/unicode-format.js.map +1 -0
- package/dist/webhook.d.ts +22 -0
- package/dist/webhook.js +148 -0
- package/dist/webhook.js.map +1 -0
- package/dist/wechat-kf-directives.d.ts +157 -0
- package/dist/wechat-kf-directives.js +576 -0
- package/dist/wechat-kf-directives.js.map +1 -0
- package/openclaw.plugin.json +31 -0
- package/package.json +92 -0
package/dist/crypto.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WeCom message encryption/decryption
|
|
3
|
+
*
|
|
4
|
+
* - Signature: SHA1(sort([token, timestamp, nonce, encrypt]))
|
|
5
|
+
* - Encryption: AES-256-CBC, key = Base64Decode(EncodingAESKey + "="), iv = key[0:16]
|
|
6
|
+
* - Plaintext format: random(16B) + msg_len(4B network order) + msg + receiveid
|
|
7
|
+
*/
|
|
8
|
+
import { createCipheriv, createDecipheriv, createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
9
|
+
import { logTag } from "./constants.js";
|
|
10
|
+
export function deriveAesKey(encodingAESKey) {
|
|
11
|
+
if (encodingAESKey.length !== 43) {
|
|
12
|
+
throw new Error(`${logTag()} EncodingAESKey must be 43 characters, got ${encodingAESKey.length}`);
|
|
13
|
+
}
|
|
14
|
+
const key = Buffer.from(`${encodingAESKey}=`, "base64");
|
|
15
|
+
if (key.length !== 32) {
|
|
16
|
+
throw new Error(`${logTag()} derived AES key must be 32 bytes, got ${key.length}`);
|
|
17
|
+
}
|
|
18
|
+
return key;
|
|
19
|
+
}
|
|
20
|
+
/** SHA1 signature verification */
|
|
21
|
+
export function computeSignature(token, timestamp, nonce, encrypt) {
|
|
22
|
+
const items = [token, timestamp, nonce, encrypt].sort();
|
|
23
|
+
return createHash("sha1").update(items.join("")).digest("hex");
|
|
24
|
+
}
|
|
25
|
+
export function verifySignature(token, timestamp, nonce, encrypt, expectedSignature) {
|
|
26
|
+
const actual = Buffer.from(computeSignature(token, timestamp, nonce, encrypt), "utf8");
|
|
27
|
+
const expected = Buffer.from(expectedSignature, "utf8");
|
|
28
|
+
if (actual.length !== expected.length)
|
|
29
|
+
return false;
|
|
30
|
+
return timingSafeEqual(actual, expected);
|
|
31
|
+
}
|
|
32
|
+
/** Decrypt an encrypted message from WeChat callback */
|
|
33
|
+
export function decrypt(encodingAESKey, encrypted) {
|
|
34
|
+
const aesKey = deriveAesKey(encodingAESKey);
|
|
35
|
+
const iv = aesKey.subarray(0, 16);
|
|
36
|
+
const decipher = createDecipheriv("aes-256-cbc", aesKey, iv);
|
|
37
|
+
decipher.setAutoPadding(false);
|
|
38
|
+
const decrypted = Buffer.concat([decipher.update(encrypted, "base64"), decipher.final()]);
|
|
39
|
+
// Remove PKCS#7 padding — validate ALL N padding bytes equal N
|
|
40
|
+
const pad = decrypted[decrypted.length - 1];
|
|
41
|
+
if (pad < 1 || pad > 32 || pad > decrypted.length) {
|
|
42
|
+
throw new Error(`${logTag()} invalid PKCS#7 padding`);
|
|
43
|
+
}
|
|
44
|
+
for (let i = 1; i <= pad; i++) {
|
|
45
|
+
if (decrypted[decrypted.length - i] !== pad) {
|
|
46
|
+
throw new Error(`${logTag()} invalid PKCS#7 padding`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const content = decrypted.subarray(0, decrypted.length - pad);
|
|
50
|
+
// Parse: random(16) + msg_len(4, big-endian) + msg + receiverId
|
|
51
|
+
if (content.length < 20) {
|
|
52
|
+
throw new Error(`${logTag()} decrypted content too short`);
|
|
53
|
+
}
|
|
54
|
+
const msgLen = content.readUInt32BE(16);
|
|
55
|
+
if (msgLen < 0 || 20 + msgLen > content.length) {
|
|
56
|
+
throw new Error(`${logTag()} invalid message length in decrypted content`);
|
|
57
|
+
}
|
|
58
|
+
const message = content.subarray(20, 20 + msgLen).toString("utf8");
|
|
59
|
+
const receiverId = content.subarray(20 + msgLen).toString("utf8");
|
|
60
|
+
return { message, receiverId };
|
|
61
|
+
}
|
|
62
|
+
/** Encrypt a message for WeChat callback response */
|
|
63
|
+
export function encrypt(encodingAESKey, message, receiverId) {
|
|
64
|
+
const aesKey = deriveAesKey(encodingAESKey);
|
|
65
|
+
const iv = aesKey.subarray(0, 16);
|
|
66
|
+
const random = randomBytes(16);
|
|
67
|
+
const msgBuf = Buffer.from(message, "utf8");
|
|
68
|
+
const receiverBuf = Buffer.from(receiverId, "utf8");
|
|
69
|
+
const msgLenBuf = Buffer.alloc(4);
|
|
70
|
+
msgLenBuf.writeUInt32BE(msgBuf.length, 0);
|
|
71
|
+
const plaintext = Buffer.concat([random, msgLenBuf, msgBuf, receiverBuf]);
|
|
72
|
+
// PKCS#7 padding to 32-byte blocks
|
|
73
|
+
const blockSize = 32;
|
|
74
|
+
const padLen = blockSize - (plaintext.length % blockSize);
|
|
75
|
+
const padding = Buffer.alloc(padLen, padLen);
|
|
76
|
+
const padded = Buffer.concat([plaintext, padding]);
|
|
77
|
+
const cipher = createCipheriv("aes-256-cbc", aesKey, iv);
|
|
78
|
+
cipher.setAutoPadding(false);
|
|
79
|
+
return Buffer.concat([cipher.update(padded), cipher.final()]).toString("base64");
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-system utilities — atomic write via temp file + rename.
|
|
3
|
+
*
|
|
4
|
+
* On POSIX systems, `rename()` within the same filesystem is atomic,
|
|
5
|
+
* so the target file is never left in a partially-written state.
|
|
6
|
+
*/
|
|
7
|
+
export declare function atomicWriteFile(filePath: string, content: string, encoding?: BufferEncoding): Promise<void>;
|
package/dist/fs-utils.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-system utilities — atomic write via temp file + rename.
|
|
3
|
+
*
|
|
4
|
+
* On POSIX systems, `rename()` within the same filesystem is atomic,
|
|
5
|
+
* so the target file is never left in a partially-written state.
|
|
6
|
+
*/
|
|
7
|
+
import { rename, writeFile } from "node:fs/promises";
|
|
8
|
+
export async function atomicWriteFile(filePath, content, encoding = "utf8") {
|
|
9
|
+
const tmpPath = `${filePath}.tmp`;
|
|
10
|
+
await writeFile(tmpPath, content, encoding);
|
|
11
|
+
await rename(tmpPath, filePath); // atomic on the same filesystem
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=fs-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs-utils.js","sourceRoot":"","sources":["../src/fs-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAErD,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,QAAgB,EAChB,OAAe,EACf,WAA2B,MAAM;IAEjC,MAAM,OAAO,GAAG,GAAG,QAAQ,MAAM,CAAC;IAClC,MAAM,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC5C,MAAM,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,gCAAgC;AACnE,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared context manager for WeChat KF plugin
|
|
3
|
+
*
|
|
4
|
+
* Provides a rendezvous point between the "default" account (which sets up
|
|
5
|
+
* enterprise-level shared infrastructure) and per-kfId accounts (which need
|
|
6
|
+
* the shared crypto config and BotContext to start polling).
|
|
7
|
+
*/
|
|
8
|
+
import type { BotContext } from "./bot.js";
|
|
9
|
+
export type SharedContext = {
|
|
10
|
+
callbackToken: string;
|
|
11
|
+
encodingAESKey: string;
|
|
12
|
+
corpId: string;
|
|
13
|
+
appSecret: string;
|
|
14
|
+
webhookPath: string;
|
|
15
|
+
botCtx: BotContext;
|
|
16
|
+
};
|
|
17
|
+
export declare function setPairingKfId(externalUserId: string, openKfId: string): void;
|
|
18
|
+
export declare function getPairingKfId(externalUserId: string): string | undefined;
|
|
19
|
+
/** Set the shared context. Resolves any pending waitForSharedContext calls. */
|
|
20
|
+
export declare function setSharedContext(ctx: SharedContext): void;
|
|
21
|
+
/** Get the shared context, or null if not yet set. */
|
|
22
|
+
export declare function getSharedContext(): SharedContext | null;
|
|
23
|
+
/**
|
|
24
|
+
* Wait until the shared context is set.
|
|
25
|
+
* Rejects if the signal aborts before the context is ready.
|
|
26
|
+
*/
|
|
27
|
+
export declare function waitForSharedContext(signal?: AbortSignal): Promise<SharedContext>;
|
|
28
|
+
/** Clear the shared context (used during shutdown). */
|
|
29
|
+
export declare function clearSharedContext(): void;
|
|
30
|
+
/** Reset all module-level state. @internal For testing only. */
|
|
31
|
+
export declare function _reset(): void;
|
package/dist/monitor.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared context manager for WeChat KF plugin
|
|
3
|
+
*
|
|
4
|
+
* Provides a rendezvous point between the "default" account (which sets up
|
|
5
|
+
* enterprise-level shared infrastructure) and per-kfId accounts (which need
|
|
6
|
+
* the shared crypto config and BotContext to start polling).
|
|
7
|
+
*/
|
|
8
|
+
// ── Module-level state ──
|
|
9
|
+
let sharedCtx = null;
|
|
10
|
+
let readyResolve = null;
|
|
11
|
+
let readyPromise = null;
|
|
12
|
+
/** Cache: externalUserId → openKfId for pairing approval notifications. */
|
|
13
|
+
const pairingKfIdCache = new Map();
|
|
14
|
+
export function setPairingKfId(externalUserId, openKfId) {
|
|
15
|
+
pairingKfIdCache.set(externalUserId, openKfId);
|
|
16
|
+
}
|
|
17
|
+
export function getPairingKfId(externalUserId) {
|
|
18
|
+
return pairingKfIdCache.get(externalUserId);
|
|
19
|
+
}
|
|
20
|
+
function ensureReadyPromise() {
|
|
21
|
+
if (!readyPromise) {
|
|
22
|
+
readyPromise = new Promise((resolve) => {
|
|
23
|
+
readyResolve = resolve;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return readyPromise;
|
|
27
|
+
}
|
|
28
|
+
/** Set the shared context. Resolves any pending waitForSharedContext calls. */
|
|
29
|
+
export function setSharedContext(ctx) {
|
|
30
|
+
sharedCtx = ctx;
|
|
31
|
+
// Resolve waiting callers
|
|
32
|
+
if (readyResolve) {
|
|
33
|
+
readyResolve();
|
|
34
|
+
readyResolve = null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/** Get the shared context, or null if not yet set. */
|
|
38
|
+
export function getSharedContext() {
|
|
39
|
+
return sharedCtx;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Wait until the shared context is set.
|
|
43
|
+
* Rejects if the signal aborts before the context is ready.
|
|
44
|
+
*/
|
|
45
|
+
export function waitForSharedContext(signal) {
|
|
46
|
+
// Already available — fast path
|
|
47
|
+
if (sharedCtx)
|
|
48
|
+
return Promise.resolve(sharedCtx);
|
|
49
|
+
// Already aborted
|
|
50
|
+
if (signal?.aborted) {
|
|
51
|
+
return Promise.reject(new DOMException("The operation was aborted.", "AbortError"));
|
|
52
|
+
}
|
|
53
|
+
const ready = ensureReadyPromise();
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
const onReady = () => {
|
|
56
|
+
signal?.removeEventListener("abort", onAbort);
|
|
57
|
+
resolve(sharedCtx);
|
|
58
|
+
};
|
|
59
|
+
const onAbort = () => {
|
|
60
|
+
reject(new DOMException("The operation was aborted.", "AbortError"));
|
|
61
|
+
};
|
|
62
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
63
|
+
ready.then(onReady);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/** Clear the shared context (used during shutdown). */
|
|
67
|
+
export function clearSharedContext() {
|
|
68
|
+
sharedCtx = null;
|
|
69
|
+
readyPromise = null;
|
|
70
|
+
readyResolve = null;
|
|
71
|
+
pairingKfIdCache.clear();
|
|
72
|
+
}
|
|
73
|
+
/** Reset all module-level state. @internal For testing only. */
|
|
74
|
+
export function _reset() {
|
|
75
|
+
sharedCtx = null;
|
|
76
|
+
readyResolve = null;
|
|
77
|
+
readyPromise = null;
|
|
78
|
+
pairingKfIdCache.clear();
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=monitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monitor.js","sourceRoot":"","sources":["../src/monitor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH,2BAA2B;AAE3B,IAAI,SAAS,GAAyB,IAAI,CAAC;AAC3C,IAAI,YAAY,GAAwB,IAAI,CAAC;AAC7C,IAAI,YAAY,GAAyB,IAAI,CAAC;AAE9C,2EAA2E;AAC3E,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAkB,CAAC;AAEnD,MAAM,UAAU,cAAc,CAAC,cAAsB,EAAE,QAAgB;IACrE,gBAAgB,CAAC,GAAG,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,cAAsB;IACnD,OAAO,gBAAgB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,kBAAkB;IACzB,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,YAAY,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAC3C,YAAY,GAAG,OAAO,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,gBAAgB,CAAC,GAAkB;IACjD,SAAS,GAAG,GAAG,CAAC;IAChB,0BAA0B;IAC1B,IAAI,YAAY,EAAE,CAAC;QACjB,YAAY,EAAE,CAAC;QACf,YAAY,GAAG,IAAI,CAAC;IACtB,CAAC;AACH,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,gBAAgB;IAC9B,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAoB;IACvD,gCAAgC;IAChC,IAAI,SAAS;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEjD,kBAAkB;IAClB,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;QACpB,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,4BAA4B,EAAE,YAAY,CAAC,CAAC,CAAC;IACtF,CAAC;IAED,MAAM,KAAK,GAAG,kBAAkB,EAAE,CAAC;IAEnC,OAAO,IAAI,OAAO,CAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACpD,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9C,OAAO,CAAC,SAAU,CAAC,CAAC;QACtB,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,MAAM,CAAC,IAAI,YAAY,CAAC,4BAA4B,EAAE,YAAY,CAAC,CAAC,CAAC;QACvE,CAAC,CAAC;QAEF,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,kBAAkB;IAChC,SAAS,GAAG,IAAI,CAAC;IACjB,YAAY,GAAG,IAAI,CAAC;IACpB,YAAY,GAAG,IAAI,CAAC;IACpB,gBAAgB,CAAC,KAAK,EAAE,CAAC;AAC3B,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,MAAM;IACpB,SAAS,GAAG,IAAI,CAAC;IACjB,YAAY,GAAG,IAAI,CAAC;IACpB,YAAY,GAAG,IAAI,CAAC;IACpB,gBAAgB,CAAC,KAAK,EAAE,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outbound message adapter for WeChat KF (framework-driven direct delivery)
|
|
3
|
+
*
|
|
4
|
+
* Responsibility:
|
|
5
|
+
* This module implements the OpenClaw `ChannelPlugin.outbound` interface and
|
|
6
|
+
* is called by the framework when the agent produces a final reply.
|
|
7
|
+
*
|
|
8
|
+
* Text is first converted from markdown to Unicode formatting (formatText),
|
|
9
|
+
* then chunked by UTF-8 byte length via `chunkTextByUtf8Bytes` (WeChat API
|
|
10
|
+
* enforces a 2048-byte limit on text.content). Framework auto-chunking is
|
|
11
|
+
* disabled (`chunker: null`) because it would chunk *before* formatting,
|
|
12
|
+
* causing post-format expansion to exceed the limit.
|
|
13
|
+
*
|
|
14
|
+
* For media, the framework's `loadWebMedia` handles all URL formats
|
|
15
|
+
* (HTTP, file://, local paths, MEDIA: prefix, ~), then the buffer is
|
|
16
|
+
* uploaded to WeChat and sent using `uploadAndSendMedia` from `send-utils.ts`.
|
|
17
|
+
*
|
|
18
|
+
* WeChat KF session limits:
|
|
19
|
+
* The API enforces a 48-hour / 5-message limit per session window.
|
|
20
|
+
* Once a customer sends a message, the agent may reply with up to 5 messages
|
|
21
|
+
* within 48 hours. After that, sending returns errcode 95026.
|
|
22
|
+
* This module detects that error and logs a clear warning rather than
|
|
23
|
+
* propagating a generic failure.
|
|
24
|
+
*
|
|
25
|
+
* Counterpart:
|
|
26
|
+
* `reply-dispatcher.ts` handles the *other* outbound path: typing-aware
|
|
27
|
+
* streaming replies dispatched internally by `bot.ts`.
|
|
28
|
+
*
|
|
29
|
+
* accountId = openKfId (dynamically discovered)
|
|
30
|
+
*/
|
|
31
|
+
import type { ChannelOutboundAdapter } from "openclaw/plugin-sdk";
|
|
32
|
+
export declare const wechatKfOutbound: ChannelOutboundAdapter;
|