@pawastation/wechat-kf 0.1.2 → 0.2.0
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 +34 -28
- package/README.zh-CN.md +34 -28
- package/dist/index.d.ts +5 -15
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/src/accounts.d.ts +2 -1
- package/dist/src/accounts.js +61 -19
- package/dist/src/accounts.js.map +1 -1
- package/dist/src/api.d.ts +31 -2
- package/dist/src/api.js +41 -13
- package/dist/src/api.js.map +1 -1
- package/dist/src/bot.d.ts +10 -8
- package/dist/src/bot.js +231 -78
- package/dist/src/bot.js.map +1 -1
- package/dist/src/channel.d.ts +7 -106
- package/dist/src/channel.js +208 -71
- package/dist/src/channel.js.map +1 -1
- package/dist/src/config-schema.d.ts +0 -6
- package/dist/src/config-schema.js +2 -7
- package/dist/src/config-schema.js.map +1 -1
- package/dist/src/constants.d.ts +20 -0
- package/dist/src/constants.js +29 -0
- package/dist/src/constants.js.map +1 -1
- package/dist/src/crypto.js +7 -6
- package/dist/src/crypto.js.map +1 -1
- package/dist/src/monitor.d.ts +27 -14
- package/dist/src/monitor.js +67 -120
- package/dist/src/monitor.js.map +1 -1
- package/dist/src/outbound.d.ts +10 -44
- package/dist/src/outbound.js +277 -92
- package/dist/src/outbound.js.map +1 -1
- package/dist/src/reply-dispatcher.d.ts +2 -6
- package/dist/src/reply-dispatcher.js +131 -32
- package/dist/src/reply-dispatcher.js.map +1 -1
- package/dist/src/runtime.d.ts +1 -119
- package/dist/src/runtime.js +2 -1
- package/dist/src/runtime.js.map +1 -1
- package/dist/src/send-utils.d.ts +13 -0
- package/dist/src/send-utils.js +56 -4
- package/dist/src/send-utils.js.map +1 -1
- package/dist/src/token.js +7 -3
- package/dist/src/token.js.map +1 -1
- package/dist/src/types.d.ts +68 -6
- package/dist/src/webhook.d.ts +16 -16
- package/dist/src/webhook.js +92 -75
- package/dist/src/webhook.js.map +1 -1
- package/dist/src/wechat-kf-directives.d.ts +132 -9
- package/dist/src/wechat-kf-directives.js +535 -24
- package/dist/src/wechat-kf-directives.js.map +1 -1
- package/index.ts +22 -12
- package/openclaw.plugin.json +1 -3
- package/package.json +3 -2
- package/dist/src/chunk-utils.d.ts +0 -18
- package/dist/src/chunk-utils.js +0 -58
- package/dist/src/chunk-utils.js.map +0 -1
package/dist/src/channel.d.ts
CHANGED
|
@@ -3,111 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Dynamically discovers kfids from webhook callbacks.
|
|
5
5
|
* Each kfid = one accountId = one independent session.
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* - "default" account: enterprise-level shared infra (loads kfIds, validates token, sets shared context)
|
|
9
|
+
* - Per-kfId accounts: wait for shared context, then start 30s polling loop
|
|
10
|
+
* - Webhook handler: registered on framework's shared gateway server (no self-managed HTTP server)
|
|
6
11
|
*/
|
|
7
|
-
import type {
|
|
8
|
-
import {
|
|
9
|
-
import type { PluginRuntime } from "./runtime.js";
|
|
10
|
-
import type { OpenClawConfig, ResolvedWechatKfAccount } from "./types.js";
|
|
11
|
-
type ChannelMeta = {
|
|
12
|
-
id: string;
|
|
13
|
-
label: string;
|
|
14
|
-
selectionLabel: string;
|
|
15
|
-
docsPath: string;
|
|
16
|
-
docsLabel: string;
|
|
17
|
-
blurb: string;
|
|
18
|
-
aliases?: string[];
|
|
19
|
-
order?: number;
|
|
20
|
-
};
|
|
21
|
-
type ChannelCapabilities = {
|
|
22
|
-
chatTypes: string[];
|
|
23
|
-
media: boolean;
|
|
24
|
-
reactions: boolean;
|
|
25
|
-
threads: boolean;
|
|
26
|
-
polls: boolean;
|
|
27
|
-
nativeCommands: boolean;
|
|
28
|
-
blockStreaming: boolean;
|
|
29
|
-
};
|
|
30
|
-
type AccountRuntimeStatus = {
|
|
31
|
-
accountId: string;
|
|
32
|
-
running: boolean;
|
|
33
|
-
lastStartAt: string | null;
|
|
34
|
-
lastStopAt: string | null;
|
|
35
|
-
lastError: string | null;
|
|
36
|
-
port: number | null;
|
|
37
|
-
};
|
|
38
|
-
type GatewayContext = {
|
|
39
|
-
cfg: OpenClawConfig;
|
|
40
|
-
runtime?: PluginRuntime;
|
|
41
|
-
abortSignal?: AbortSignal;
|
|
42
|
-
accountId: string;
|
|
43
|
-
log?: Logger;
|
|
44
|
-
setStatus?: (status: Partial<AccountRuntimeStatus>) => void;
|
|
45
|
-
};
|
|
46
|
-
type ChannelPlugin<T = unknown> = {
|
|
47
|
-
id: string;
|
|
48
|
-
meta: ChannelMeta;
|
|
49
|
-
capabilities: ChannelCapabilities;
|
|
50
|
-
agentPrompt: {
|
|
51
|
-
messageToolHints: () => string[];
|
|
52
|
-
};
|
|
53
|
-
reload: {
|
|
54
|
-
configPrefixes: string[];
|
|
55
|
-
};
|
|
56
|
-
configSchema: {
|
|
57
|
-
schema: unknown;
|
|
58
|
-
};
|
|
59
|
-
config: {
|
|
60
|
-
listAccountIds: (cfg: OpenClawConfig) => string[];
|
|
61
|
-
resolveAccount: (cfg: OpenClawConfig, accountId?: string) => T;
|
|
62
|
-
defaultAccountId: (cfg: OpenClawConfig) => string;
|
|
63
|
-
setAccountEnabled: (opts: {
|
|
64
|
-
cfg: OpenClawConfig;
|
|
65
|
-
accountId: string;
|
|
66
|
-
enabled: boolean;
|
|
67
|
-
}) => OpenClawConfig;
|
|
68
|
-
deleteAccount: (opts: {
|
|
69
|
-
cfg: OpenClawConfig;
|
|
70
|
-
accountId: string;
|
|
71
|
-
}) => OpenClawConfig;
|
|
72
|
-
isConfigured: (account: T) => boolean;
|
|
73
|
-
describeAccount: (account: T) => Record<string, unknown>;
|
|
74
|
-
resolveAllowFrom: (opts: {
|
|
75
|
-
cfg: OpenClawConfig;
|
|
76
|
-
}) => string[];
|
|
77
|
-
formatAllowFrom: (opts: {
|
|
78
|
-
allowFrom: string[];
|
|
79
|
-
}) => string[];
|
|
80
|
-
};
|
|
81
|
-
security: {
|
|
82
|
-
resolveDmPolicy: (opts: {
|
|
83
|
-
cfg: OpenClawConfig;
|
|
84
|
-
}) => Record<string, unknown>;
|
|
85
|
-
collectWarnings: (opts: {
|
|
86
|
-
cfg: OpenClawConfig;
|
|
87
|
-
}) => string[];
|
|
88
|
-
};
|
|
89
|
-
setup: {
|
|
90
|
-
resolveAccountId: (cfg: OpenClawConfig, accountId?: string) => string;
|
|
91
|
-
applyAccountConfig: (opts: {
|
|
92
|
-
cfg: OpenClawConfig;
|
|
93
|
-
accountId: string;
|
|
94
|
-
}) => OpenClawConfig;
|
|
95
|
-
};
|
|
96
|
-
outbound: typeof wechatKfOutbound;
|
|
97
|
-
status: {
|
|
98
|
-
defaultRuntime: AccountRuntimeStatus;
|
|
99
|
-
buildChannelSummary: (opts: {
|
|
100
|
-
snapshot: Record<string, unknown>;
|
|
101
|
-
}) => Record<string, unknown>;
|
|
102
|
-
buildAccountSnapshot: (opts: {
|
|
103
|
-
account: T;
|
|
104
|
-
runtime: Partial<AccountRuntimeStatus> | null;
|
|
105
|
-
}) => Record<string, unknown>;
|
|
106
|
-
};
|
|
107
|
-
gateway: {
|
|
108
|
-
_started: boolean;
|
|
109
|
-
startAccount: (ctx: GatewayContext) => Promise<void>;
|
|
110
|
-
};
|
|
111
|
-
};
|
|
12
|
+
import type { ChannelPlugin } from "openclaw/plugin-sdk";
|
|
13
|
+
import type { ResolvedWechatKfAccount } from "./types.js";
|
|
112
14
|
export declare const wechatKfPlugin: ChannelPlugin<ResolvedWechatKfAccount>;
|
|
113
|
-
export {};
|
package/dist/src/channel.js
CHANGED
|
@@ -3,24 +3,34 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Dynamically discovers kfids from webhook callbacks.
|
|
5
5
|
* Each kfid = one accountId = one independent session.
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* - "default" account: enterprise-level shared infra (loads kfIds, validates token, sets shared context)
|
|
9
|
+
* - Per-kfId accounts: wait for shared context, then start 30s polling loop
|
|
10
|
+
* - Webhook handler: registered on framework's shared gateway server (no self-managed HTTP server)
|
|
6
11
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import { deleteKfId, disableKfId, enableKfId, getChannelConfig, listAccountIds, resolveAccount } from "./accounts.js";
|
|
12
|
+
import { formatPairingApproveHint, PAIRING_APPROVED_MESSAGE } from "openclaw/plugin-sdk";
|
|
13
|
+
import { deleteKfId, disableKfId, enableKfId, getChannelConfig, listAccountIds, loadKfIds, resolveAccount, } from "./accounts.js";
|
|
14
|
+
import { sendTextMessage } from "./api.js";
|
|
15
|
+
import { handleWebhookEvent } from "./bot.js";
|
|
9
16
|
import { wechatKfConfigSchema } from "./config-schema.js";
|
|
10
|
-
import {
|
|
17
|
+
import { CHANNEL_ID, CONFIG_KEY, DEFAULT_WEBHOOK_PATH, defaultStateDir, formatError, logTag } from "./constants.js";
|
|
18
|
+
import { clearSharedContext, getPairingKfId, getSharedContext, setSharedContext, waitForSharedContext, } from "./monitor.js";
|
|
11
19
|
import { wechatKfOutbound } from "./outbound.js";
|
|
20
|
+
import { getRuntime } from "./runtime.js";
|
|
21
|
+
import { getAccessToken } from "./token.js";
|
|
12
22
|
const meta = {
|
|
13
|
-
id:
|
|
23
|
+
id: CHANNEL_ID,
|
|
14
24
|
label: "WeChat KF",
|
|
15
25
|
selectionLabel: "WeChat Customer Service (微信客服)",
|
|
16
|
-
docsPath:
|
|
17
|
-
docsLabel:
|
|
26
|
+
docsPath: `/channels/${CHANNEL_ID}`,
|
|
27
|
+
docsLabel: CHANNEL_ID,
|
|
18
28
|
blurb: "WeCom Customer Service (企业微信客服) API channel — let WeChat users chat with your agent.",
|
|
19
29
|
aliases: ["wxkf"],
|
|
20
30
|
order: 80,
|
|
21
31
|
};
|
|
22
32
|
export const wechatKfPlugin = {
|
|
23
|
-
id:
|
|
33
|
+
id: CHANNEL_ID,
|
|
24
34
|
meta: { ...meta },
|
|
25
35
|
capabilities: {
|
|
26
36
|
chatTypes: ["direct"],
|
|
@@ -33,22 +43,51 @@ export const wechatKfPlugin = {
|
|
|
33
43
|
},
|
|
34
44
|
agentPrompt: {
|
|
35
45
|
messageToolHints: () => [
|
|
36
|
-
"- WeChat KF:
|
|
37
|
-
"-
|
|
38
|
-
"-
|
|
39
|
-
"-
|
|
46
|
+
"- WeChat KF is direct-message only (1:1). Omit `target` to reply to current conversation.",
|
|
47
|
+
"- Markdown (bold, italic, headings, lists, code blocks) is auto-converted to Unicode-styled plain text.",
|
|
48
|
+
"- Long replies are auto-chunked at ~2000 chars; write naturally without manually splitting.",
|
|
49
|
+
"- WeChat content security may block messages that combine numbered lists (1. 2. 3.) with security/credential topics (passwords, API keys, secrets). Use bullet points or conversational prose for such topics.",
|
|
50
|
+
"- Outbound media: can send image (jpg/png/gif/bmp), video (mp4), and file attachments.",
|
|
51
|
+
"- Voice messages require AMR format (\u22642MB, \u226460s). Other audio formats (mp3, wav, ogg) are sent as file attachments, not playable voice.",
|
|
52
|
+
"- When generating or saving files for sending, prefer the agent workspace directory over /tmp.",
|
|
53
|
+
"- Users may send: text, images, voice, video, files, locations, links, mini-programs, menu selections, forwarded chat history, \u89c6\u9891\u53f7 content, business cards, \u89c6\u9891\u53f7\u5546\u54c1, \u89c6\u9891\u53f7\u8ba2\u5355, or \u7b14\u8bb0.",
|
|
54
|
+
"",
|
|
55
|
+
"### WeChat Rich Messages",
|
|
56
|
+
"Embed directives in your reply to send rich messages (one per message). Text around the directive is sent as separate text messages.",
|
|
57
|
+
"Thumbnail fields: pass an image URL or file path (auto-uploaded to WeChat), or a media_id from a prior message (used as-is).",
|
|
58
|
+
"",
|
|
59
|
+
"**Link Card**: `[[wechat_link: Title | Description | https://url | thumbnail]]`",
|
|
60
|
+
" title + url required; desc + thumbnail optional. Thumbnail needed for rich card; without one, sent as plain text.",
|
|
61
|
+
"",
|
|
62
|
+
"**Location**: `[[wechat_location: Place Name | Address | latitude | longitude]]`",
|
|
63
|
+
" name + coordinates required; address optional (omit for 3-field format: `name | lat | lng`).",
|
|
64
|
+
"",
|
|
65
|
+
"**Mini Program**: `[[wechat_miniprogram: appid | Title | pages/path | thumbnail]]`",
|
|
66
|
+
" appid + title + pagepath required; thumbnail recommended. Without one, falls back to plain text.",
|
|
67
|
+
"",
|
|
68
|
+
"**Menu**: `[[wechat_menu: Question text | Option1, Option2, Option3 | Footer text]]`",
|
|
69
|
+
" Presents clickable options to the user. Options required (comma-separated); header and footer both optional.",
|
|
70
|
+
" Advanced item types: `view(url, label)` = URL link, `mini(appid, pagepath, label)` = mini program,",
|
|
71
|
+
" `text(content)` = plain text row, `click(id, label)` = click with explicit ID.",
|
|
72
|
+
"",
|
|
73
|
+
"**Business Card**: `[[wechat_business_card: USERID]]`",
|
|
74
|
+
" Sends a WeCom member's contact card. Requires \u300c\u5ba2\u6237\u8054\u7cfb\u300d permission. Customer must have actively messaged within 48h (menu clicks don't count). Max 1 card per 48h window.",
|
|
75
|
+
"",
|
|
76
|
+
"**Acquisition Link (\u83b7\u5ba2\u94fe\u63a5)**: `[[wechat_ca_link: https://work.weixin.qq.com/ca/...]]`",
|
|
77
|
+
" Sends a \u83b7\u5ba2\u94fe\u63a5 as a rich card. The URL must be a valid acquisition link.",
|
|
78
|
+
"",
|
|
79
|
+
'**Raw Message (experimental)**: `[[wechat_raw: {"msgtype":"xxx", "xxx": {...}}]]`',
|
|
80
|
+
" Sends an arbitrary message via the WeChat KF API. The JSON must include a `msgtype` field plus the type-specific payload.",
|
|
81
|
+
" Use this to echo back or test undocumented message types. Check the raw JSON shown for unknown inbound types.",
|
|
40
82
|
],
|
|
41
83
|
},
|
|
42
|
-
reload: { configPrefixes: [
|
|
84
|
+
reload: { configPrefixes: [CONFIG_KEY] },
|
|
43
85
|
configSchema: { schema: wechatKfConfigSchema },
|
|
44
86
|
config: {
|
|
45
87
|
listAccountIds: (cfg) => listAccountIds(cfg),
|
|
46
|
-
resolveAccount: (cfg, accountId) => resolveAccount(cfg, accountId),
|
|
88
|
+
resolveAccount: (cfg, accountId) => resolveAccount(cfg, accountId ?? undefined),
|
|
47
89
|
defaultAccountId: (cfg) => listAccountIds(cfg)[0] ?? "default",
|
|
48
90
|
setAccountEnabled: ({ cfg, accountId, enabled }) => {
|
|
49
|
-
// Dynamic accounts — toggle via in-memory disabled set (persisted to disk).
|
|
50
|
-
// Fire-and-forget: the async persist is best-effort; the in-memory state
|
|
51
|
-
// takes effect immediately so the framework sees the change right away.
|
|
52
91
|
if (enabled) {
|
|
53
92
|
void enableKfId(accountId);
|
|
54
93
|
}
|
|
@@ -58,41 +97,34 @@ export const wechatKfPlugin = {
|
|
|
58
97
|
return cfg;
|
|
59
98
|
},
|
|
60
99
|
deleteAccount: ({ cfg, accountId }) => {
|
|
61
|
-
// Remove from discovered set and add to disabled set so it won't come
|
|
62
|
-
// back from future webhook callbacks. Fire-and-forget for persistence.
|
|
63
100
|
void deleteKfId(accountId);
|
|
64
101
|
return cfg;
|
|
65
102
|
},
|
|
66
|
-
isConfigured: (account) => account.configured,
|
|
67
|
-
describeAccount: (account) => ({
|
|
103
|
+
isConfigured: (account, _cfg) => account.configured,
|
|
104
|
+
describeAccount: (account, _cfg) => ({
|
|
68
105
|
accountId: account.accountId,
|
|
69
106
|
enabled: account.enabled,
|
|
70
107
|
configured: account.configured,
|
|
71
|
-
corpId: account.corpId,
|
|
72
|
-
openKfId: account.openKfId,
|
|
73
108
|
}),
|
|
74
109
|
resolveAllowFrom: ({ cfg }) => {
|
|
75
110
|
const config = getChannelConfig(cfg);
|
|
76
111
|
return (config.allowFrom ?? []).map(String);
|
|
77
112
|
},
|
|
78
|
-
formatAllowFrom: ({ allowFrom }) => allowFrom.map((e) => e.trim()).filter(Boolean),
|
|
113
|
+
formatAllowFrom: ({ allowFrom, }) => allowFrom.map((e) => String(e).trim()).filter(Boolean),
|
|
79
114
|
},
|
|
80
115
|
security: {
|
|
81
|
-
resolveDmPolicy: ({ cfg }) => {
|
|
116
|
+
resolveDmPolicy: ({ cfg, }) => {
|
|
82
117
|
const config = getChannelConfig(cfg);
|
|
83
118
|
const policy = config.dmPolicy ?? "open";
|
|
84
119
|
return {
|
|
85
120
|
policy,
|
|
86
121
|
allowFrom: config.allowFrom ?? [],
|
|
87
|
-
allowFromPath:
|
|
88
|
-
approveHint:
|
|
89
|
-
"To approve a WeChat KF user, add their external_userid to the allowlist:",
|
|
90
|
-
" openclaw config set channels.wechat-kf.allowFrom '[\"{userid}\"]'",
|
|
91
|
-
].join("\n"),
|
|
122
|
+
allowFromPath: `${CONFIG_KEY}.allowFrom`,
|
|
123
|
+
approveHint: formatPairingApproveHint(CHANNEL_ID),
|
|
92
124
|
normalizeEntry: (raw) => raw.replace(/^user:/i, "").trim(),
|
|
93
125
|
};
|
|
94
126
|
},
|
|
95
|
-
collectWarnings: ({ cfg }) => {
|
|
127
|
+
collectWarnings: ({ cfg, }) => {
|
|
96
128
|
const config = getChannelConfig(cfg);
|
|
97
129
|
const policy = config.dmPolicy ?? "open";
|
|
98
130
|
if (policy === "open") {
|
|
@@ -101,13 +133,29 @@ export const wechatKfPlugin = {
|
|
|
101
133
|
return [];
|
|
102
134
|
},
|
|
103
135
|
},
|
|
136
|
+
pairing: {
|
|
137
|
+
idLabel: "wechatKfExternalUserId",
|
|
138
|
+
normalizeAllowEntry: (entry) => entry.replace(/^(wechat-kf|user):/i, "").trim(),
|
|
139
|
+
notifyApproval: async ({ id }) => {
|
|
140
|
+
const shared = getSharedContext();
|
|
141
|
+
if (!shared)
|
|
142
|
+
return;
|
|
143
|
+
const { corpId, appSecret } = shared;
|
|
144
|
+
if (!corpId || !appSecret)
|
|
145
|
+
return;
|
|
146
|
+
const kfId = getPairingKfId(id) ?? listAccountIds(shared.botCtx.cfg).find((a) => a !== "default");
|
|
147
|
+
if (!kfId)
|
|
148
|
+
return;
|
|
149
|
+
await sendTextMessage(corpId, appSecret, id, kfId, PAIRING_APPROVED_MESSAGE);
|
|
150
|
+
},
|
|
151
|
+
},
|
|
104
152
|
setup: {
|
|
105
|
-
resolveAccountId: (
|
|
106
|
-
applyAccountConfig: ({ cfg }) => {
|
|
153
|
+
resolveAccountId: ({ accountId }) => accountId ?? "default",
|
|
154
|
+
applyAccountConfig: ({ cfg, accountId: _accountId, input: _input, }) => {
|
|
107
155
|
const config = getChannelConfig(cfg);
|
|
108
156
|
return {
|
|
109
157
|
...cfg,
|
|
110
|
-
channels: { ...(cfg.channels ?? {}),
|
|
158
|
+
channels: { ...(cfg.channels ?? {}), [CHANNEL_ID]: { ...config, enabled: true } },
|
|
111
159
|
};
|
|
112
160
|
},
|
|
113
161
|
},
|
|
@@ -119,63 +167,152 @@ export const wechatKfPlugin = {
|
|
|
119
167
|
lastStartAt: null,
|
|
120
168
|
lastStopAt: null,
|
|
121
169
|
lastError: null,
|
|
122
|
-
port: null,
|
|
123
170
|
},
|
|
124
|
-
buildChannelSummary: ({ snapshot }) => ({
|
|
171
|
+
buildChannelSummary: ({ snapshot, }) => ({
|
|
125
172
|
configured: snapshot.configured ?? false,
|
|
126
173
|
running: snapshot.running ?? false,
|
|
127
174
|
lastStartAt: snapshot.lastStartAt ?? null,
|
|
128
175
|
lastError: snapshot.lastError ?? null,
|
|
129
|
-
port: snapshot.port ?? null,
|
|
130
176
|
}),
|
|
131
177
|
buildAccountSnapshot: ({ account, runtime, }) => ({
|
|
132
178
|
accountId: account.accountId,
|
|
133
179
|
enabled: account.enabled,
|
|
134
180
|
configured: account.configured,
|
|
135
|
-
corpId: account.corpId,
|
|
136
181
|
running: runtime?.running ?? false,
|
|
137
182
|
lastStartAt: runtime?.lastStartAt ?? null,
|
|
138
183
|
lastError: runtime?.lastError ?? null,
|
|
139
|
-
port: runtime?.port ?? null,
|
|
140
184
|
}),
|
|
141
185
|
},
|
|
142
186
|
gateway: {
|
|
143
|
-
/** Track whether the account is currently started to prevent duplicate launches. */
|
|
144
|
-
_started: false,
|
|
145
187
|
startAccount: async (ctx) => {
|
|
146
|
-
const self = wechatKfPlugin.gateway;
|
|
147
|
-
// Idempotency guard — skip if already started
|
|
148
|
-
if (self._started) {
|
|
149
|
-
ctx.log?.info("[wechat-kf] startAccount: already running, skipping duplicate call");
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
188
|
const config = getChannelConfig(ctx.cfg);
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
stateDir
|
|
165
|
-
|
|
166
|
-
|
|
189
|
+
const pluginRuntime = getRuntime();
|
|
190
|
+
const stateDir = pluginRuntime.state?.resolveStateDir?.() ?? defaultStateDir();
|
|
191
|
+
if (ctx.accountId === "default") {
|
|
192
|
+
// ── "default" account: enterprise-level shared infrastructure ──
|
|
193
|
+
try {
|
|
194
|
+
const { corpId, appSecret, token, encodingAESKey } = config;
|
|
195
|
+
const webhookPath = config.webhookPath ?? DEFAULT_WEBHOOK_PATH;
|
|
196
|
+
if (!corpId || !appSecret || !token || !encodingAESKey) {
|
|
197
|
+
throw new Error(`${logTag()} missing required config fields (corpId, appSecret, token, encodingAESKey)`);
|
|
198
|
+
}
|
|
199
|
+
// Load previously discovered kfids
|
|
200
|
+
await loadKfIds(stateDir);
|
|
201
|
+
// Validate access token on startup (best-effort)
|
|
202
|
+
try {
|
|
203
|
+
await getAccessToken(corpId, appSecret);
|
|
204
|
+
ctx.log?.info(`${logTag()} access_token validated`);
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
ctx.log?.warn(`${logTag()} access_token validation failed (will retry on first message): ${err}`);
|
|
208
|
+
}
|
|
209
|
+
const botCtx = { cfg: ctx.cfg, runtime: ctx.runtime, stateDir, log: ctx.log };
|
|
210
|
+
setSharedContext({
|
|
211
|
+
callbackToken: token,
|
|
212
|
+
encodingAESKey,
|
|
213
|
+
corpId,
|
|
214
|
+
appSecret,
|
|
215
|
+
webhookPath,
|
|
216
|
+
botCtx,
|
|
217
|
+
});
|
|
218
|
+
ctx.setStatus({ accountId: ctx.accountId, running: true, lastStartAt: Date.now() });
|
|
219
|
+
ctx.log?.info(`${logTag()} shared context ready (webhook path: ${webhookPath})`);
|
|
220
|
+
// Block until abort — framework expects long-lived promise
|
|
221
|
+
await new Promise((resolve) => {
|
|
222
|
+
if (ctx.abortSignal.aborted) {
|
|
223
|
+
resolve();
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
ctx.abortSignal.addEventListener("abort", () => resolve(), { once: true });
|
|
227
|
+
});
|
|
228
|
+
// Shutdown
|
|
229
|
+
clearSharedContext();
|
|
230
|
+
ctx.setStatus({
|
|
231
|
+
accountId: ctx.accountId,
|
|
232
|
+
running: false,
|
|
233
|
+
lastStopAt: Date.now(),
|
|
234
|
+
});
|
|
235
|
+
ctx.log?.info(`${logTag()} shared context cleared, default account stopped`);
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
clearSharedContext();
|
|
239
|
+
ctx.setStatus({
|
|
240
|
+
accountId: ctx.accountId,
|
|
241
|
+
running: false,
|
|
242
|
+
lastError: formatError(err),
|
|
243
|
+
lastStopAt: Date.now(),
|
|
244
|
+
});
|
|
245
|
+
ctx.log?.error(`${logTag()} default account failed: ${formatError(err)}`);
|
|
246
|
+
throw err;
|
|
247
|
+
}
|
|
167
248
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
249
|
+
else {
|
|
250
|
+
// ── Per-kfId account: polling loop ──
|
|
251
|
+
let pollTimer = null;
|
|
252
|
+
try {
|
|
253
|
+
// Wait for the "default" account to set shared context
|
|
254
|
+
const shared = await waitForSharedContext(ctx.abortSignal);
|
|
255
|
+
ctx.setStatus({ accountId: ctx.accountId, running: true, lastStartAt: Date.now() });
|
|
256
|
+
ctx.log?.info(`${logTag(ctx.accountId)} polling started`);
|
|
257
|
+
// Start 30s polling loop
|
|
258
|
+
const POLL_INTERVAL_MS = 30_000;
|
|
259
|
+
let polling = false;
|
|
260
|
+
pollTimer = setInterval(async () => {
|
|
261
|
+
if (ctx.abortSignal.aborted)
|
|
262
|
+
return;
|
|
263
|
+
if (polling)
|
|
264
|
+
return;
|
|
265
|
+
polling = true;
|
|
266
|
+
try {
|
|
267
|
+
ctx.log?.debug?.(`${logTag(ctx.accountId)} polling sync_msg...`);
|
|
268
|
+
await handleWebhookEvent(shared.botCtx, ctx.accountId, "");
|
|
269
|
+
}
|
|
270
|
+
catch (err) {
|
|
271
|
+
ctx.log?.error(`${logTag(ctx.accountId)} poll error: ${formatError(err)}`);
|
|
272
|
+
}
|
|
273
|
+
finally {
|
|
274
|
+
polling = false;
|
|
275
|
+
}
|
|
276
|
+
}, POLL_INTERVAL_MS);
|
|
277
|
+
// Block until abort
|
|
278
|
+
await new Promise((resolve) => {
|
|
279
|
+
if (ctx.abortSignal.aborted) {
|
|
280
|
+
resolve();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
ctx.abortSignal.addEventListener("abort", () => resolve(), { once: true });
|
|
284
|
+
});
|
|
285
|
+
// Cleanup
|
|
286
|
+
if (pollTimer) {
|
|
287
|
+
clearInterval(pollTimer);
|
|
288
|
+
pollTimer = null;
|
|
289
|
+
}
|
|
290
|
+
ctx.setStatus({
|
|
291
|
+
accountId: ctx.accountId,
|
|
292
|
+
running: false,
|
|
293
|
+
lastStopAt: Date.now(),
|
|
294
|
+
});
|
|
295
|
+
ctx.log?.info(`${logTag(ctx.accountId)} polling stopped`);
|
|
296
|
+
}
|
|
297
|
+
catch (err) {
|
|
298
|
+
if (pollTimer) {
|
|
299
|
+
clearInterval(pollTimer);
|
|
300
|
+
pollTimer = null;
|
|
301
|
+
}
|
|
302
|
+
ctx.setStatus({
|
|
303
|
+
accountId: ctx.accountId,
|
|
304
|
+
running: false,
|
|
305
|
+
lastError: formatError(err),
|
|
306
|
+
lastStopAt: Date.now(),
|
|
307
|
+
});
|
|
308
|
+
// AbortError is expected when the signal fires before shared context is ready
|
|
309
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
310
|
+
ctx.log?.info(`${logTag(ctx.accountId)} aborted before shared context ready`);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
ctx.log?.error(`${logTag(ctx.accountId)} startAccount failed: ${formatError(err)}`);
|
|
314
|
+
throw err;
|
|
315
|
+
}
|
|
179
316
|
}
|
|
180
317
|
},
|
|
181
318
|
},
|
package/dist/src/channel.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel.js","sourceRoot":"","sources":["../../src/channel.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"channel.js","sourceRoot":"","sources":["../../src/channel.ts"],"names":[],"mappings":"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"}
|
|
@@ -31,12 +31,6 @@ export declare const wechatKfConfigSchema: {
|
|
|
31
31
|
minLength: number;
|
|
32
32
|
maxLength: number;
|
|
33
33
|
};
|
|
34
|
-
webhookPort: {
|
|
35
|
-
type: "integer";
|
|
36
|
-
minimum: number;
|
|
37
|
-
maximum: number;
|
|
38
|
-
default: number;
|
|
39
|
-
};
|
|
40
34
|
webhookPath: {
|
|
41
35
|
type: "string";
|
|
42
36
|
default: string;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* Flat enterprise-level credentials. No per-account config needed —
|
|
8
8
|
* kfids are discovered dynamically from webhook callbacks.
|
|
9
9
|
*/
|
|
10
|
+
import { DEFAULT_WEBHOOK_PATH } from "./constants.js";
|
|
10
11
|
export const wechatKfConfigSchema = {
|
|
11
12
|
type: "object",
|
|
12
13
|
properties: {
|
|
@@ -20,13 +21,7 @@ export const wechatKfConfigSchema = {
|
|
|
20
21
|
minLength: 43,
|
|
21
22
|
maxLength: 43,
|
|
22
23
|
},
|
|
23
|
-
|
|
24
|
-
type: "integer",
|
|
25
|
-
minimum: 1,
|
|
26
|
-
maximum: 65535,
|
|
27
|
-
default: 9999,
|
|
28
|
-
},
|
|
29
|
-
webhookPath: { type: "string", default: "/wechat-kf" },
|
|
24
|
+
webhookPath: { type: "string", default: DEFAULT_WEBHOOK_PATH },
|
|
30
25
|
dmPolicy: {
|
|
31
26
|
type: "string",
|
|
32
27
|
enum: ["open", "pairing", "allowlist", "disabled"],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-schema.js","sourceRoot":"","sources":["../../src/config-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,IAAI,EAAE,QAAiB;IACvB,UAAU,EAAE;QACV,OAAO,EAAE,EAAE,IAAI,EAAE,SAAkB,EAAE;QACrC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,WAAW,EAAE,sBAAsB,EAAE;QACxE,SAAS,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,WAAW,EAAE,8BAA8B,EAAE;QACnF,KAAK,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,WAAW,EAAE,wBAAwB,EAAE;QACzE,cAAc,EAAE;YACd,IAAI,EAAE,QAAiB;YACvB,WAAW,EAAE,6BAA6B;YAC1C,SAAS,EAAE,EAAE;YACb,SAAS,EAAE,EAAE;SACd;QACD,WAAW,EAAE
|
|
1
|
+
{"version":3,"file":"config-schema.js","sourceRoot":"","sources":["../../src/config-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,IAAI,EAAE,QAAiB;IACvB,UAAU,EAAE;QACV,OAAO,EAAE,EAAE,IAAI,EAAE,SAAkB,EAAE;QACrC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,WAAW,EAAE,sBAAsB,EAAE;QACxE,SAAS,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,WAAW,EAAE,8BAA8B,EAAE;QACnF,KAAK,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,WAAW,EAAE,wBAAwB,EAAE;QACzE,cAAc,EAAE;YACd,IAAI,EAAE,QAAiB;YACvB,WAAW,EAAE,6BAA6B;YAC1C,SAAS,EAAE,EAAE;YACb,SAAS,EAAE,EAAE;SACd;QACD,WAAW,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,OAAO,EAAE,oBAAoB,EAAE;QACvE,QAAQ,EAAE;YACR,IAAI,EAAE,QAAiB;YACvB,IAAI,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,CAAU;YAC3D,OAAO,EAAE,MAAM;SAChB;QACD,SAAS,EAAE,EAAE,IAAI,EAAE,OAAgB,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAiB,EAAE,EAAE;KAC1E;CACF,CAAC"}
|
package/dist/src/constants.d.ts
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
/** Channel identifier — single source of truth for the plugin ID string. */
|
|
2
|
+
export declare const CHANNEL_ID: "wechat-kf";
|
|
3
|
+
/** Default webhook path registered on the framework's shared gateway. */
|
|
4
|
+
export declare const DEFAULT_WEBHOOK_PATH: string;
|
|
5
|
+
/** Config key prefix for this channel in OpenClaw config. */
|
|
6
|
+
export declare const CONFIG_KEY: string;
|
|
7
|
+
/** Build a log-tag prefix: `[wechat-kf]` or `[wechat-kf:kfId]`. */
|
|
8
|
+
export declare function logTag(kfId?: string): string;
|
|
9
|
+
/** Default state directory for cursor and kfid persistence. */
|
|
10
|
+
export declare function defaultStateDir(): string;
|
|
11
|
+
/** Cursor file name for a given kfId. */
|
|
12
|
+
export declare function cursorFileName(kfId: string): string;
|
|
13
|
+
/** Persisted file name for discovered kfids. */
|
|
14
|
+
export declare const KFIDS_FILE: string;
|
|
15
|
+
/** Persisted file name for disabled kfids. */
|
|
16
|
+
export declare const DISABLED_KFIDS_FILE: string;
|
|
1
17
|
/** WeChat KF text message character limit */
|
|
2
18
|
export declare const WECHAT_TEXT_CHUNK_LIMIT = 2000;
|
|
3
19
|
/** Timeout for token fetch requests (ms) */
|
|
@@ -17,3 +33,7 @@ export declare const TOKEN_EXPIRED_CODES: Set<number>;
|
|
|
17
33
|
export declare const WECHAT_MSG_LIMIT_ERRCODE = 95026;
|
|
18
34
|
/** Timeout for downloading media from external HTTP URLs (ms) */
|
|
19
35
|
export declare const MEDIA_DOWNLOAD_TIMEOUT_MS = 60000;
|
|
36
|
+
/** Max age (seconds) for inbound messages. Messages older than this are skipped. */
|
|
37
|
+
export declare const MAX_MESSAGE_AGE_S = 300;
|
|
38
|
+
/** Format an unknown caught value for log messages (no stack traces). */
|
|
39
|
+
export declare function formatError(err: unknown): string;
|