@wecom/wecom-openclaw-plugin 2026.3.31-beta.0 → 2026.4.1-beta.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/README.md +4 -251
- package/dist/cjs/index.js +45 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/src/channel.js +339 -0
- package/dist/cjs/src/channel.js.map +1 -0
- package/dist/cjs/src/const.js +137 -0
- package/dist/cjs/src/const.js.map +1 -0
- package/dist/cjs/src/dm-policy.js +89 -0
- package/dist/cjs/src/dm-policy.js.map +1 -0
- package/dist/cjs/src/group-policy.js +133 -0
- package/dist/cjs/src/group-policy.js.map +1 -0
- package/dist/cjs/src/mcp/interceptors/biz-error.js +70 -0
- package/dist/cjs/src/mcp/interceptors/biz-error.js.map +1 -0
- package/dist/cjs/src/mcp/interceptors/index.js +73 -0
- package/dist/cjs/src/mcp/interceptors/index.js.map +1 -0
- package/dist/cjs/src/mcp/interceptors/msg-media.js +159 -0
- package/dist/cjs/src/mcp/interceptors/msg-media.js.map +1 -0
- package/dist/cjs/src/mcp/interceptors/smartpage-create.js +157 -0
- package/dist/cjs/src/mcp/interceptors/smartpage-create.js.map +1 -0
- package/dist/cjs/src/mcp/interceptors/smartpage-export.js +93 -0
- package/dist/cjs/src/mcp/interceptors/smartpage-export.js.map +1 -0
- package/dist/cjs/src/mcp/schema.js +104 -0
- package/dist/cjs/src/mcp/schema.js.map +1 -0
- package/dist/cjs/src/mcp/tool.js +208 -0
- package/dist/cjs/src/mcp/tool.js.map +1 -0
- package/dist/cjs/src/mcp/transport.js +417 -0
- package/dist/cjs/src/mcp/transport.js.map +1 -0
- package/dist/cjs/src/media-handler.js +129 -0
- package/dist/cjs/src/media-handler.js.map +1 -0
- package/dist/cjs/src/media-uploader.js +284 -0
- package/dist/cjs/src/media-uploader.js.map +1 -0
- package/dist/cjs/src/message-parser.js +134 -0
- package/dist/cjs/src/message-parser.js.map +1 -0
- package/dist/cjs/src/message-sender.js +89 -0
- package/dist/cjs/src/message-sender.js.map +1 -0
- package/dist/cjs/src/monitor.js +991 -0
- package/dist/cjs/src/monitor.js.map +1 -0
- package/dist/cjs/src/onboarding.js +182 -0
- package/dist/cjs/src/onboarding.js.map +1 -0
- package/dist/cjs/src/openclaw-compat.js +400 -0
- package/dist/cjs/src/openclaw-compat.js.map +1 -0
- package/dist/cjs/src/reqid-store.js +88 -0
- package/dist/cjs/src/reqid-store.js.map +1 -0
- package/dist/cjs/src/runtime.js +16 -0
- package/dist/cjs/src/runtime.js.map +1 -0
- package/dist/cjs/src/state-dir-resolve.js +35 -0
- package/dist/cjs/src/state-dir-resolve.js.map +1 -0
- package/dist/cjs/src/state-manager.js +158 -0
- package/dist/cjs/src/state-manager.js.map +1 -0
- package/dist/cjs/src/template-card-parser.js +641 -0
- package/dist/cjs/src/template-card-parser.js.map +1 -0
- package/dist/cjs/src/timeout.js +42 -0
- package/dist/cjs/src/timeout.js.map +1 -0
- package/dist/cjs/src/utils.js +60 -0
- package/dist/cjs/src/utils.js.map +1 -0
- package/dist/cjs/src/version.js +26 -0
- package/dist/cjs/src/version.js.map +1 -0
- package/dist/esm/index.js +41 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/src/channel.js +337 -0
- package/dist/esm/src/channel.js.map +1 -0
- package/dist/{src → esm/src}/const.js +30 -77
- package/dist/esm/src/const.js.map +1 -0
- package/dist/{src → esm/src}/dm-policy.js +9 -6
- package/dist/esm/src/dm-policy.js.map +1 -0
- package/dist/{src → esm/src}/group-policy.js +7 -3
- package/dist/esm/src/group-policy.js.map +1 -0
- package/dist/{src → esm/src}/mcp/interceptors/biz-error.js +6 -2
- package/dist/esm/src/mcp/interceptors/biz-error.js.map +1 -0
- package/dist/{src → esm/src}/mcp/interceptors/index.js +10 -6
- package/dist/esm/src/mcp/interceptors/index.js.map +1 -0
- package/dist/{src → esm/src}/mcp/interceptors/msg-media.js +10 -6
- package/dist/esm/src/mcp/interceptors/msg-media.js.map +1 -0
- package/dist/{src → esm/src}/mcp/interceptors/smartpage-create.js +6 -2
- package/dist/esm/src/mcp/interceptors/smartpage-create.js.map +1 -0
- package/dist/{src → esm/src}/mcp/interceptors/smartpage-export.js +6 -2
- package/dist/esm/src/mcp/interceptors/smartpage-export.js.map +1 -0
- package/dist/{src → esm/src}/mcp/schema.js +4 -1
- package/dist/esm/src/mcp/schema.js.map +1 -0
- package/dist/{src → esm/src}/mcp/tool.js +8 -4
- package/dist/esm/src/mcp/tool.js.map +1 -0
- package/dist/{src → esm/src}/mcp/transport.js +16 -15
- package/dist/esm/src/mcp/transport.js.map +1 -0
- package/dist/{src → esm/src}/media-handler.js +10 -6
- package/dist/esm/src/media-handler.js.map +1 -0
- package/dist/{src → esm/src}/media-uploader.js +10 -64
- package/dist/esm/src/media-uploader.js.map +1 -0
- package/dist/{src → esm/src}/message-parser.js +4 -1
- package/dist/esm/src/message-parser.js.map +1 -0
- package/dist/{src → esm/src}/message-sender.js +11 -46
- package/dist/esm/src/message-sender.js.map +1 -0
- package/dist/{src → esm/src}/monitor.js +282 -151
- package/dist/esm/src/monitor.js.map +1 -0
- package/dist/{src → esm/src}/onboarding.js +26 -22
- package/dist/esm/src/onboarding.js.map +1 -0
- package/dist/{src → esm/src}/openclaw-compat.js +27 -143
- package/dist/esm/src/openclaw-compat.js.map +1 -0
- package/dist/{src → esm/src}/reqid-store.js +7 -4
- package/dist/esm/src/reqid-store.js.map +1 -0
- package/dist/{src → esm/src}/runtime.js +5 -2
- package/dist/esm/src/runtime.js.map +1 -0
- package/dist/esm/src/state-dir-resolve.js +13 -0
- package/dist/esm/src/state-dir-resolve.js.map +1 -0
- package/dist/{src → esm/src}/state-manager.js +16 -84
- package/dist/esm/src/state-manager.js.map +1 -0
- package/dist/{src → esm/src}/template-card-parser.js +8 -17
- package/dist/esm/src/template-card-parser.js.map +1 -0
- package/dist/{src → esm/src}/timeout.js +5 -2
- package/dist/esm/src/timeout.js.map +1 -0
- package/dist/{src → esm/src}/utils.js +10 -24
- package/dist/esm/src/utils.js.map +1 -0
- package/dist/esm/src/version.js +23 -0
- package/dist/esm/src/version.js.map +1 -0
- package/dist/esm/types/index.d.ts +9 -0
- package/dist/{src → esm/types/src}/const.d.ts +0 -46
- package/dist/{src → esm/types/src}/interface.d.ts +2 -0
- package/dist/{src → esm/types/src}/message-sender.d.ts +0 -18
- package/dist/{src → esm/types/src}/openclaw-compat.d.ts +0 -49
- package/dist/esm/types/src/state-dir-resolve.d.ts +2 -0
- package/dist/{src → esm/types/src}/utils.d.ts +0 -39
- package/dist/esm/types/src/version.d.ts +2 -0
- package/dist/index.d.ts +4 -2
- package/package.json +7 -15
- package/dist/index.js +0 -113
- package/dist/src/accounts.d.ts +0 -57
- package/dist/src/accounts.js +0 -222
- package/dist/src/agent/api-client.d.ts +0 -94
- package/dist/src/agent/api-client.js +0 -292
- package/dist/src/agent/handler.d.ts +0 -63
- package/dist/src/agent/handler.js +0 -532
- package/dist/src/agent/index.d.ts +0 -5
- package/dist/src/agent/index.js +0 -5
- package/dist/src/agent/webhook.d.ts +0 -23
- package/dist/src/agent/webhook.js +0 -226
- package/dist/src/agent/xml.d.ts +0 -21
- package/dist/src/agent/xml.js +0 -41
- package/dist/src/channel.js +0 -564
- package/dist/src/chat-queue.d.ts +0 -31
- package/dist/src/chat-queue.js +0 -45
- package/dist/src/http.d.ts +0 -26
- package/dist/src/http.js +0 -93
- package/dist/src/interface.js +0 -4
- package/dist/src/mcp/index.js +0 -6
- package/dist/src/mcp/interceptors/types.js +0 -7
- package/dist/src/shared/command-auth.d.ts +0 -23
- package/dist/src/shared/command-auth.js +0 -73
- package/dist/src/shared/xml-parser.d.ts +0 -46
- package/dist/src/shared/xml-parser.js +0 -210
- package/dist/src/target.d.ts +0 -35
- package/dist/src/target.js +0 -67
- package/dist/src/template-card-manager.d.ts +0 -55
- package/dist/src/template-card-manager.js +0 -205
- package/dist/src/types/account.d.ts +0 -29
- package/dist/src/types/account.js +0 -4
- package/dist/src/types/config.d.ts +0 -80
- package/dist/src/types/config.js +0 -7
- package/dist/src/types/constants.d.ts +0 -42
- package/dist/src/types/constants.js +0 -42
- package/dist/src/types/index.d.ts +0 -7
- package/dist/src/types/index.js +0 -5
- package/dist/src/types/message.d.ts +0 -238
- package/dist/src/types/message.js +0 -5
- package/dist/src/version.d.ts +0 -2
- package/dist/src/version.js +0 -24
- package/dist/src/webhook/command-auth.d.ts +0 -47
- package/dist/src/webhook/command-auth.js +0 -96
- package/dist/src/webhook/gateway.d.ts +0 -36
- package/dist/src/webhook/gateway.js +0 -227
- package/dist/src/webhook/handler.d.ts +0 -18
- package/dist/src/webhook/handler.js +0 -420
- package/dist/src/webhook/helpers.d.ts +0 -156
- package/dist/src/webhook/helpers.js +0 -833
- package/dist/src/webhook/http.d.ts +0 -26
- package/dist/src/webhook/http.js +0 -94
- package/dist/src/webhook/index.d.ts +0 -11
- package/dist/src/webhook/index.js +0 -14
- package/dist/src/webhook/media.d.ts +0 -29
- package/dist/src/webhook/media.js +0 -88
- package/dist/src/webhook/monitor.d.ts +0 -59
- package/dist/src/webhook/monitor.js +0 -1147
- package/dist/src/webhook/state.d.ts +0 -220
- package/dist/src/webhook/state.js +0 -491
- package/dist/src/webhook/target.d.ts +0 -41
- package/dist/src/webhook/target.js +0 -132
- package/dist/src/webhook/types.d.ts +0 -347
- package/dist/src/webhook/types.js +0 -33
- /package/dist/{src → esm/types/src}/channel.d.ts +0 -0
- /package/dist/{src → esm/types/src}/dm-policy.d.ts +0 -0
- /package/dist/{src → esm/types/src}/group-policy.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/index.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/interceptors/biz-error.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/interceptors/index.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/interceptors/msg-media.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/interceptors/smartpage-create.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/interceptors/smartpage-export.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/interceptors/types.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/schema.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/tool.d.ts +0 -0
- /package/dist/{src → esm/types/src}/mcp/transport.d.ts +0 -0
- /package/dist/{src → esm/types/src}/media-handler.d.ts +0 -0
- /package/dist/{src → esm/types/src}/media-uploader.d.ts +0 -0
- /package/dist/{src → esm/types/src}/message-parser.d.ts +0 -0
- /package/dist/{src → esm/types/src}/monitor.d.ts +0 -0
- /package/dist/{src → esm/types/src}/onboarding.d.ts +0 -0
- /package/dist/{src → esm/types/src}/reqid-store.d.ts +0 -0
- /package/dist/{src → esm/types/src}/runtime.d.ts +0 -0
- /package/dist/{src → esm/types/src}/state-manager.d.ts +0 -0
- /package/dist/{src → esm/types/src}/template-card-parser.d.ts +0 -0
- /package/dist/{src → esm/types/src}/timeout.d.ts +0 -0
package/dist/src/channel.js
DELETED
|
@@ -1,564 +0,0 @@
|
|
|
1
|
-
import { buildAccountScopedDmSecurityPolicy } from "./openclaw-compat.js";
|
|
2
|
-
import { formatPairingApproveHint, DEFAULT_ACCOUNT_ID } from './openclaw-compat.js';
|
|
3
|
-
import { getWeComRuntime } from "./runtime.js";
|
|
4
|
-
import { monitorWeComProvider } from "./monitor.js";
|
|
5
|
-
import { getWeComWebSocket } from "./state-manager.js";
|
|
6
|
-
import { wecomSetupWizard, wecomSetupAdapter } from "./onboarding.js";
|
|
7
|
-
import { listWeComAccountIds, resolveWeComAccountMulti, resolveDefaultWeComAccountId, hasMultiAccounts, } from "./accounts.js";
|
|
8
|
-
import { CHANNEL_ID, TEXT_CHUNK_LIMIT, WEBHOOK_PATHS } from "./const.js";
|
|
9
|
-
import { uploadAndSendMedia } from "./media-uploader.js";
|
|
10
|
-
import { registerAgentWebhookTarget, deregisterAgentWebhookTarget } from "./agent/webhook.js";
|
|
11
|
-
import { resolveWecomTarget } from "./target.js";
|
|
12
|
-
import { sendText as sendAgentText, sendMedia as sendAgentMedia, uploadMedia as uploadAgentMedia } from "./agent/api-client.js";
|
|
13
|
-
import { startWebhookGateway, stopWebhookGateway } from "./webhook/index.js";
|
|
14
|
-
/**
|
|
15
|
-
* 使用 SDK 的 sendMessage 主动发送企业微信消息
|
|
16
|
-
* 优先 Bot WebSocket,不可用时自动回退到 Agent HTTP API
|
|
17
|
-
*/
|
|
18
|
-
async function sendWeComMessage({ to, content, accountId, cfg, }) {
|
|
19
|
-
const resolvedAccountId = accountId ?? DEFAULT_ACCOUNT_ID;
|
|
20
|
-
// 从 to 中提取目标(格式是 "${CHANNEL_ID}:xxx" 或直接是目标字符串)
|
|
21
|
-
const channelPrefix = new RegExp(`^${CHANNEL_ID}:`, "i");
|
|
22
|
-
const chatId = to.replace(channelPrefix, "");
|
|
23
|
-
// ── 尝试 Bot WebSocket ──
|
|
24
|
-
const wsClient = getWeComWebSocket(resolvedAccountId);
|
|
25
|
-
if (wsClient?.isConnected) {
|
|
26
|
-
const result = await wsClient.sendMessage(chatId, {
|
|
27
|
-
msgtype: 'markdown',
|
|
28
|
-
markdown: { content },
|
|
29
|
-
});
|
|
30
|
-
const messageId = result?.headers?.req_id ?? `wecom-${Date.now()}`;
|
|
31
|
-
return { channel: CHANNEL_ID, messageId, chatId };
|
|
32
|
-
}
|
|
33
|
-
// ── 回退到 Agent HTTP API ──
|
|
34
|
-
if (!cfg) {
|
|
35
|
-
throw new Error(`WSClient not connected for account ${resolvedAccountId} and no config available for Agent fallback`);
|
|
36
|
-
}
|
|
37
|
-
const account = resolveWeComAccountMulti({ cfg, accountId: resolvedAccountId });
|
|
38
|
-
const agent = account.agent;
|
|
39
|
-
if (!agent?.configured) {
|
|
40
|
-
throw new Error(`WSClient not connected for account ${resolvedAccountId} and Agent mode is not configured. ` +
|
|
41
|
-
`Please configure either Bot (botId + secret) or Agent (corpId + corpSecret + agentId) for this account.`);
|
|
42
|
-
}
|
|
43
|
-
const target = resolveWecomTarget(chatId);
|
|
44
|
-
if (!target) {
|
|
45
|
-
throw new Error(`Cannot resolve outbound target from "${to}"`);
|
|
46
|
-
}
|
|
47
|
-
console.log(`[wecom-outbound] Bot WS unavailable, sending via Agent HTTP API to ${JSON.stringify(target)} (accountId=${resolvedAccountId})`);
|
|
48
|
-
await sendAgentText({
|
|
49
|
-
agent,
|
|
50
|
-
toUser: target.touser,
|
|
51
|
-
toParty: target.toparty,
|
|
52
|
-
toTag: target.totag,
|
|
53
|
-
chatId: target.chatid,
|
|
54
|
-
text: content,
|
|
55
|
-
});
|
|
56
|
-
return {
|
|
57
|
-
channel: CHANNEL_ID,
|
|
58
|
-
messageId: `agent-${Date.now()}`,
|
|
59
|
-
chatId,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
// 企业微信频道元数据
|
|
63
|
-
const meta = {
|
|
64
|
-
id: CHANNEL_ID,
|
|
65
|
-
label: "企业微信",
|
|
66
|
-
selectionLabel: "企业微信 (WeCom)",
|
|
67
|
-
detailLabel: "企业微信智能机器人",
|
|
68
|
-
docsPath: `/channels/${CHANNEL_ID}`,
|
|
69
|
-
docsLabel: CHANNEL_ID,
|
|
70
|
-
blurb: "企业微信智能机器人接入插件",
|
|
71
|
-
systemImage: "message.fill",
|
|
72
|
-
};
|
|
73
|
-
export const wecomPlugin = {
|
|
74
|
-
id: CHANNEL_ID,
|
|
75
|
-
meta: {
|
|
76
|
-
...meta,
|
|
77
|
-
quickstartAllowFrom: true,
|
|
78
|
-
},
|
|
79
|
-
pairing: {
|
|
80
|
-
idLabel: "wecomUserId",
|
|
81
|
-
normalizeAllowEntry: (entry) => entry.replace(new RegExp(`^(${CHANNEL_ID}|user):`, "i"), "").trim(),
|
|
82
|
-
notifyApproval: async ({ cfg, id }) => {
|
|
83
|
-
// sendWeComMessage({
|
|
84
|
-
// to: id,
|
|
85
|
-
// content: " pairing approved",
|
|
86
|
-
// accountId: cfg.accountId,
|
|
87
|
-
// });
|
|
88
|
-
// Pairing approved for user
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
setupWizard: wecomSetupWizard,
|
|
92
|
-
setup: wecomSetupAdapter,
|
|
93
|
-
capabilities: {
|
|
94
|
-
chatTypes: ["direct", "group"],
|
|
95
|
-
reactions: false,
|
|
96
|
-
threads: false,
|
|
97
|
-
media: true,
|
|
98
|
-
nativeCommands: false,
|
|
99
|
-
blockStreaming: true,
|
|
100
|
-
},
|
|
101
|
-
reload: { configPrefixes: [`channels.${CHANNEL_ID}`] },
|
|
102
|
-
config: {
|
|
103
|
-
// 多账号:列出所有账户 ID
|
|
104
|
-
listAccountIds: (cfg) => listWeComAccountIds(cfg),
|
|
105
|
-
// 多账号:按 accountId 解析账户配置
|
|
106
|
-
resolveAccount: (cfg, accountId) => resolveWeComAccountMulti({ cfg, accountId }),
|
|
107
|
-
// 多账号:获取默认账户 ID
|
|
108
|
-
defaultAccountId: (cfg) => resolveDefaultWeComAccountId(cfg),
|
|
109
|
-
// 多账号:设置账户启用状态
|
|
110
|
-
setAccountEnabled: ({ cfg, accountId, enabled }) => {
|
|
111
|
-
if (!hasMultiAccounts(cfg)) {
|
|
112
|
-
// 单账号模式:设置顶层 enabled
|
|
113
|
-
const wecomConfig = (cfg.channels?.[CHANNEL_ID] ?? {});
|
|
114
|
-
return {
|
|
115
|
-
...cfg,
|
|
116
|
-
channels: {
|
|
117
|
-
...cfg.channels,
|
|
118
|
-
[CHANNEL_ID]: {
|
|
119
|
-
...wecomConfig,
|
|
120
|
-
enabled,
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
// 多账号模式:设置 accounts[accountId].enabled
|
|
126
|
-
const wecomConfig = (cfg.channels?.[CHANNEL_ID] ?? {});
|
|
127
|
-
return {
|
|
128
|
-
...cfg,
|
|
129
|
-
channels: {
|
|
130
|
-
...cfg.channels,
|
|
131
|
-
[CHANNEL_ID]: {
|
|
132
|
-
...wecomConfig,
|
|
133
|
-
accounts: {
|
|
134
|
-
...wecomConfig.accounts,
|
|
135
|
-
[accountId]: {
|
|
136
|
-
...wecomConfig.accounts?.[accountId],
|
|
137
|
-
enabled,
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
},
|
|
142
|
-
};
|
|
143
|
-
},
|
|
144
|
-
// 多账号:删除账户
|
|
145
|
-
deleteAccount: ({ cfg, accountId }) => {
|
|
146
|
-
if (!hasMultiAccounts(cfg)) {
|
|
147
|
-
// 单账号模式:删除整个 wecom 配置
|
|
148
|
-
const next = { ...cfg };
|
|
149
|
-
const nextChannels = { ...cfg.channels };
|
|
150
|
-
delete nextChannels[CHANNEL_ID];
|
|
151
|
-
if (Object.keys(nextChannels).length > 0) {
|
|
152
|
-
next.channels = nextChannels;
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
delete next.channels;
|
|
156
|
-
}
|
|
157
|
-
return next;
|
|
158
|
-
}
|
|
159
|
-
// 删除指定账号
|
|
160
|
-
const wecomConfig = cfg.channels?.[CHANNEL_ID];
|
|
161
|
-
const accounts = { ...wecomConfig?.accounts };
|
|
162
|
-
delete accounts[accountId];
|
|
163
|
-
return {
|
|
164
|
-
...cfg,
|
|
165
|
-
channels: {
|
|
166
|
-
...cfg.channels,
|
|
167
|
-
[CHANNEL_ID]: {
|
|
168
|
-
...wecomConfig,
|
|
169
|
-
accounts: Object.keys(accounts).length > 0 ? accounts : undefined,
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
};
|
|
173
|
-
},
|
|
174
|
-
// 检查是否已配置(Bot / Agent / botWebhook 凭证之一即可)
|
|
175
|
-
isConfigured: (account) => Boolean(account.botId?.trim() && account.secret?.trim()) ||
|
|
176
|
-
Boolean(account.agent?.configured) ||
|
|
177
|
-
Boolean(account.token?.trim() && account.encodingAESKey?.trim()),
|
|
178
|
-
// 描述账户信息
|
|
179
|
-
describeAccount: (account) => ({
|
|
180
|
-
accountId: account.accountId,
|
|
181
|
-
name: account.name,
|
|
182
|
-
enabled: account.enabled,
|
|
183
|
-
configured: Boolean(account.botId?.trim() && account.secret?.trim()) || Boolean(account.agent?.configured),
|
|
184
|
-
botId: account.botId,
|
|
185
|
-
websocketUrl: account.websocketUrl,
|
|
186
|
-
agentConfigured: Boolean(account.agent?.configured),
|
|
187
|
-
}),
|
|
188
|
-
// 解析允许来源列表(多账号:按 accountId 解析)
|
|
189
|
-
resolveAllowFrom: ({ cfg, accountId }) => {
|
|
190
|
-
const account = resolveWeComAccountMulti({ cfg, accountId });
|
|
191
|
-
return (account.config.allowFrom ?? []).map((entry) => String(entry));
|
|
192
|
-
},
|
|
193
|
-
// 格式化允许来源列表
|
|
194
|
-
formatAllowFrom: ({ allowFrom }) => allowFrom
|
|
195
|
-
.map((entry) => String(entry).trim())
|
|
196
|
-
.filter(Boolean),
|
|
197
|
-
},
|
|
198
|
-
security: {
|
|
199
|
-
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
|
200
|
-
const result = buildAccountScopedDmSecurityPolicy({
|
|
201
|
-
cfg,
|
|
202
|
-
channelKey: CHANNEL_ID,
|
|
203
|
-
accountId,
|
|
204
|
-
fallbackAccountId: account.accountId,
|
|
205
|
-
policy: account.config.dmPolicy,
|
|
206
|
-
allowFrom: account.config.allowFrom ?? [],
|
|
207
|
-
defaultPolicy: "open",
|
|
208
|
-
policyPathSuffix: "dmPolicy",
|
|
209
|
-
approveHint: formatPairingApproveHint(CHANNEL_ID),
|
|
210
|
-
normalizeEntry: (raw) => raw.replace(new RegExp(`^${CHANNEL_ID}:`, "i"), "").trim(),
|
|
211
|
-
});
|
|
212
|
-
return result;
|
|
213
|
-
},
|
|
214
|
-
collectWarnings: ({ cfg, accountId }) => {
|
|
215
|
-
const account = resolveWeComAccountMulti({ cfg, accountId });
|
|
216
|
-
const warnings = [];
|
|
217
|
-
// 动态构造配置路径(区分单账号 / 多账号)
|
|
218
|
-
const isMulti = hasMultiAccounts(cfg);
|
|
219
|
-
const basePath = isMulti && accountId
|
|
220
|
-
? `channels.${CHANNEL_ID}.accounts.${accountId}.`
|
|
221
|
-
: `channels.${CHANNEL_ID}.`;
|
|
222
|
-
// DM 策略警告
|
|
223
|
-
const dmPolicy = account.config.dmPolicy ?? "open";
|
|
224
|
-
if (dmPolicy === "open") {
|
|
225
|
-
const hasWildcard = (account.config.allowFrom ?? []).some((entry) => String(entry).trim() === "*");
|
|
226
|
-
if (!hasWildcard) {
|
|
227
|
-
warnings.push(`- 企业微信[${account.accountId}]私信:dmPolicy="open" 但 allowFrom 未包含 "*"。任何人都可以发消息,但允许列表为空可能导致意外行为。建议设置 ${basePath}allowFrom=["*"] 或使用 dmPolicy="pairing"。`);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
// 群组策略警告
|
|
231
|
-
const defaultGroupPolicy = cfg.channels?.defaults?.groupPolicy;
|
|
232
|
-
const groupPolicy = account.config.groupPolicy ?? defaultGroupPolicy ?? "open";
|
|
233
|
-
if (groupPolicy === "open") {
|
|
234
|
-
warnings.push(`- 企业微信[${account.accountId}]群组:groupPolicy="open" 允许所有群组中的成员触发。设置 ${basePath}groupPolicy="allowlist" + ${basePath}groupAllowFrom 来限制群组。`);
|
|
235
|
-
}
|
|
236
|
-
return warnings;
|
|
237
|
-
},
|
|
238
|
-
},
|
|
239
|
-
messaging: {
|
|
240
|
-
normalizeTarget: (target) => {
|
|
241
|
-
const trimmed = target.trim();
|
|
242
|
-
if (!trimmed)
|
|
243
|
-
return undefined;
|
|
244
|
-
return trimmed;
|
|
245
|
-
},
|
|
246
|
-
targetResolver: {
|
|
247
|
-
looksLikeId: (id) => {
|
|
248
|
-
const trimmed = id?.trim();
|
|
249
|
-
return Boolean(trimmed);
|
|
250
|
-
},
|
|
251
|
-
hint: "<userId|groupId>",
|
|
252
|
-
},
|
|
253
|
-
},
|
|
254
|
-
directory: {
|
|
255
|
-
self: async () => null,
|
|
256
|
-
listPeers: async () => [],
|
|
257
|
-
listGroups: async () => [],
|
|
258
|
-
},
|
|
259
|
-
outbound: {
|
|
260
|
-
deliveryMode: "gateway",
|
|
261
|
-
chunker: (text, limit) => getWeComRuntime().channel.text.chunkMarkdownText(text, limit),
|
|
262
|
-
textChunkLimit: TEXT_CHUNK_LIMIT,
|
|
263
|
-
sendText: async ({ to, text, accountId, cfg }) => {
|
|
264
|
-
return sendWeComMessage({ to, content: text, accountId: accountId ?? undefined, cfg });
|
|
265
|
-
},
|
|
266
|
-
sendMedia: async ({ to, text, mediaUrl, mediaLocalRoots, accountId, cfg }) => {
|
|
267
|
-
const resolvedAccountId = accountId ?? DEFAULT_ACCOUNT_ID;
|
|
268
|
-
const channelPrefix = new RegExp(`^${CHANNEL_ID}:`, "i");
|
|
269
|
-
const chatId = to.replace(channelPrefix, "");
|
|
270
|
-
// 如果没有 mediaUrl,fallback 为纯文本
|
|
271
|
-
if (!mediaUrl) {
|
|
272
|
-
return sendWeComMessage({ to, content: text || "", accountId: resolvedAccountId, cfg });
|
|
273
|
-
}
|
|
274
|
-
// ── 尝试 Bot WebSocket ──
|
|
275
|
-
const wsClient = getWeComWebSocket(resolvedAccountId);
|
|
276
|
-
if (wsClient?.isConnected) {
|
|
277
|
-
const result = await uploadAndSendMedia({
|
|
278
|
-
wsClient,
|
|
279
|
-
mediaUrl,
|
|
280
|
-
chatId,
|
|
281
|
-
mediaLocalRoots,
|
|
282
|
-
});
|
|
283
|
-
if (result.rejected) {
|
|
284
|
-
return sendWeComMessage({ to, content: `⚠️ ${result.rejectReason}`, accountId: resolvedAccountId, cfg });
|
|
285
|
-
}
|
|
286
|
-
if (!result.ok) {
|
|
287
|
-
const fallbackContent = text
|
|
288
|
-
? `${text}\n📎 ${mediaUrl}`
|
|
289
|
-
: `📎 ${mediaUrl}`;
|
|
290
|
-
return sendWeComMessage({ to, content: fallbackContent, accountId: resolvedAccountId, cfg });
|
|
291
|
-
}
|
|
292
|
-
if (text) {
|
|
293
|
-
await sendWeComMessage({ to, content: text, accountId: resolvedAccountId, cfg });
|
|
294
|
-
}
|
|
295
|
-
if (result.downgradeNote) {
|
|
296
|
-
await sendWeComMessage({ to, content: `ℹ️ ${result.downgradeNote}`, accountId: resolvedAccountId, cfg });
|
|
297
|
-
}
|
|
298
|
-
return {
|
|
299
|
-
channel: CHANNEL_ID,
|
|
300
|
-
messageId: result.messageId,
|
|
301
|
-
chatId,
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
// ── 回退到 Agent HTTP API ──
|
|
305
|
-
if (!cfg) {
|
|
306
|
-
throw new Error(`WSClient not connected for account ${resolvedAccountId} and no config available for Agent fallback`);
|
|
307
|
-
}
|
|
308
|
-
const account = resolveWeComAccountMulti({ cfg, accountId: resolvedAccountId });
|
|
309
|
-
const agent = account.agent;
|
|
310
|
-
if (!agent?.configured) {
|
|
311
|
-
throw new Error(`WSClient not connected for account ${resolvedAccountId} and Agent mode is not configured. ` +
|
|
312
|
-
`Please configure either Bot (botId + secret) or Agent (corpId + corpSecret + agentId).`);
|
|
313
|
-
}
|
|
314
|
-
// Agent 模式:文本 fallback(Agent HTTP API 不支持直接发 mediaUrl,需先上传)
|
|
315
|
-
const target = resolveWecomTarget(chatId);
|
|
316
|
-
if (!target) {
|
|
317
|
-
throw new Error(`Cannot resolve outbound target from "${to}"`);
|
|
318
|
-
}
|
|
319
|
-
console.log(`[wecom-outbound] Bot WS unavailable, sending media via Agent HTTP API to ${JSON.stringify(target)}`);
|
|
320
|
-
// 尝试下载并上传媒体到企微
|
|
321
|
-
try {
|
|
322
|
-
const mediaResponse = await fetch(mediaUrl);
|
|
323
|
-
if (mediaResponse.ok) {
|
|
324
|
-
const buffer = Buffer.from(await mediaResponse.arrayBuffer());
|
|
325
|
-
const filename = mediaUrl.split('/').pop() || 'file.bin';
|
|
326
|
-
const mediaId = await uploadAgentMedia({
|
|
327
|
-
agent,
|
|
328
|
-
type: 'file',
|
|
329
|
-
buffer,
|
|
330
|
-
filename,
|
|
331
|
-
});
|
|
332
|
-
await sendAgentMedia({
|
|
333
|
-
agent,
|
|
334
|
-
toUser: target.touser,
|
|
335
|
-
toParty: target.toparty,
|
|
336
|
-
toTag: target.totag,
|
|
337
|
-
chatId: target.chatid,
|
|
338
|
-
mediaId,
|
|
339
|
-
mediaType: 'file',
|
|
340
|
-
});
|
|
341
|
-
if (text) {
|
|
342
|
-
await sendAgentText({ agent, toUser: target.touser, toParty: target.toparty, toTag: target.totag, chatId: target.chatid, text });
|
|
343
|
-
}
|
|
344
|
-
return { channel: CHANNEL_ID, messageId: `agent-media-${Date.now()}`, chatId };
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
catch (err) {
|
|
348
|
-
console.warn(`[wecom-outbound] Agent media upload failed, falling back to text:`, err);
|
|
349
|
-
}
|
|
350
|
-
// 媒体上传失败,降级为文本 + URL
|
|
351
|
-
const fallbackContent = text ? `${text}\n📎 ${mediaUrl}` : `📎 ${mediaUrl}`;
|
|
352
|
-
await sendAgentText({ agent, toUser: target.touser, toParty: target.toparty, toTag: target.totag, chatId: target.chatid, text: fallbackContent });
|
|
353
|
-
return { channel: CHANNEL_ID, messageId: `agent-${Date.now()}`, chatId };
|
|
354
|
-
},
|
|
355
|
-
},
|
|
356
|
-
status: {
|
|
357
|
-
defaultRuntime: {
|
|
358
|
-
accountId: DEFAULT_ACCOUNT_ID,
|
|
359
|
-
running: false,
|
|
360
|
-
lastStartAt: null,
|
|
361
|
-
lastStopAt: null,
|
|
362
|
-
lastError: null,
|
|
363
|
-
},
|
|
364
|
-
collectStatusIssues: (accounts) => accounts.flatMap((entry) => {
|
|
365
|
-
const accountId = String(entry.accountId ?? DEFAULT_ACCOUNT_ID);
|
|
366
|
-
const enabled = entry.enabled !== false;
|
|
367
|
-
const configured = entry.configured === true;
|
|
368
|
-
if (!enabled) {
|
|
369
|
-
return [];
|
|
370
|
-
}
|
|
371
|
-
const issues = [];
|
|
372
|
-
if (!configured) {
|
|
373
|
-
issues.push({
|
|
374
|
-
channel: CHANNEL_ID,
|
|
375
|
-
accountId,
|
|
376
|
-
kind: "config",
|
|
377
|
-
message: "企业微信机器人 ID 或 Secret 未配置",
|
|
378
|
-
fix: "Run: openclaw channels add wecom --bot-id <id> --secret <secret>",
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
return issues;
|
|
382
|
-
}),
|
|
383
|
-
buildChannelSummary: ({ snapshot }) => ({
|
|
384
|
-
configured: snapshot.configured ?? false,
|
|
385
|
-
running: snapshot.running ?? false,
|
|
386
|
-
lastStartAt: snapshot.lastStartAt ?? null,
|
|
387
|
-
lastStopAt: snapshot.lastStopAt ?? null,
|
|
388
|
-
lastError: snapshot.lastError ?? null,
|
|
389
|
-
}),
|
|
390
|
-
probeAccount: async () => {
|
|
391
|
-
return { ok: true, status: 200 };
|
|
392
|
-
},
|
|
393
|
-
buildAccountSnapshot: ({ account, runtime }) => {
|
|
394
|
-
const configured = Boolean(account.botId?.trim() &&
|
|
395
|
-
account.secret?.trim()) || Boolean(account.agent?.configured);
|
|
396
|
-
return {
|
|
397
|
-
accountId: account.accountId,
|
|
398
|
-
name: account.name,
|
|
399
|
-
enabled: account.enabled,
|
|
400
|
-
configured,
|
|
401
|
-
running: runtime?.running ?? false,
|
|
402
|
-
lastStartAt: runtime?.lastStartAt ?? null,
|
|
403
|
-
lastStopAt: runtime?.lastStopAt ?? null,
|
|
404
|
-
lastError: runtime?.lastError ?? null,
|
|
405
|
-
};
|
|
406
|
-
},
|
|
407
|
-
},
|
|
408
|
-
gateway: {
|
|
409
|
-
startAccount: async (ctx) => {
|
|
410
|
-
// 多账号:按 accountId 解析账号配置
|
|
411
|
-
const account = resolveWeComAccountMulti({ cfg: ctx.cfg, accountId: ctx.accountId });
|
|
412
|
-
// 读取连接模式(默认 websocket)
|
|
413
|
-
const connectionMode = account.config.connectionMode ?? "websocket";
|
|
414
|
-
ctx.log?.info(`starting wecom[${ctx.accountId}] (name: ${account.name}, mode: ${connectionMode})`);
|
|
415
|
-
// ── Agent target 注册 ──────────────────────────────────────────
|
|
416
|
-
const agent = account.agent;
|
|
417
|
-
if (agent?.configured) {
|
|
418
|
-
const isMulti = hasMultiAccounts(ctx.cfg);
|
|
419
|
-
const paths = isMulti
|
|
420
|
-
? [
|
|
421
|
-
`${WEBHOOK_PATHS.AGENT_PLUGIN}/${ctx.accountId}`,
|
|
422
|
-
`${WEBHOOK_PATHS.AGENT}/${ctx.accountId}`,
|
|
423
|
-
WEBHOOK_PATHS.AGENT_PLUGIN,
|
|
424
|
-
WEBHOOK_PATHS.AGENT,
|
|
425
|
-
]
|
|
426
|
-
: [WEBHOOK_PATHS.AGENT_PLUGIN, WEBHOOK_PATHS.AGENT];
|
|
427
|
-
for (const p of paths) {
|
|
428
|
-
registerAgentWebhookTarget({
|
|
429
|
-
agent,
|
|
430
|
-
config: ctx.cfg,
|
|
431
|
-
runtime: {
|
|
432
|
-
log: ctx.log?.info ? (msg) => ctx.log.info(msg) : undefined,
|
|
433
|
-
error: ctx.log?.error ? (msg) => ctx.log.error(msg) : undefined,
|
|
434
|
-
},
|
|
435
|
-
path: p,
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
ctx.log?.info(`[${ctx.accountId}] wecom agent webhook registered at ${paths.join(", ")}`);
|
|
439
|
-
// 账号生命周期结束时清理
|
|
440
|
-
ctx.abortSignal.addEventListener("abort", () => {
|
|
441
|
-
deregisterAgentWebhookTarget(agent.accountId);
|
|
442
|
-
}, { once: true });
|
|
443
|
-
}
|
|
444
|
-
// ── Bot WebSocket 监听(需要 botId + secret)──────────────────
|
|
445
|
-
const hasBotCredentials = Boolean(account.botId?.trim() && account.secret?.trim());
|
|
446
|
-
if (hasBotCredentials) {
|
|
447
|
-
return monitorWeComProvider({
|
|
448
|
-
account,
|
|
449
|
-
config: ctx.cfg,
|
|
450
|
-
runtime: ctx.runtime,
|
|
451
|
-
abortSignal: ctx.abortSignal,
|
|
452
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- SDK 类型签名在不同版本间存在差异
|
|
453
|
-
setStatus: ctx.setStatus,
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
else if (connectionMode === "webhook") {
|
|
457
|
-
// ── Webhook 模式 ──────────────────────────────────────────────
|
|
458
|
-
const webhookAccount = {
|
|
459
|
-
...account,
|
|
460
|
-
connectionMode: "webhook",
|
|
461
|
-
token: account.config.token ?? "",
|
|
462
|
-
encodingAESKey: account.config.encodingAESKey ?? "",
|
|
463
|
-
receiveId: account.config.receiveId ?? "",
|
|
464
|
-
welcomeText: account.config.welcomeText,
|
|
465
|
-
};
|
|
466
|
-
const gatewayCtx = {
|
|
467
|
-
account: webhookAccount,
|
|
468
|
-
config: ctx.cfg,
|
|
469
|
-
runtime: ctx.runtime,
|
|
470
|
-
abortSignal: ctx.abortSignal,
|
|
471
|
-
setStatus: ctx.setStatus,
|
|
472
|
-
log: ctx.log,
|
|
473
|
-
accountId: ctx.accountId,
|
|
474
|
-
};
|
|
475
|
-
startWebhookGateway(gatewayCtx);
|
|
476
|
-
// 等待 abortSignal 停止后清理
|
|
477
|
-
await new Promise((resolve) => {
|
|
478
|
-
if (ctx.abortSignal.aborted) {
|
|
479
|
-
stopWebhookGateway(gatewayCtx);
|
|
480
|
-
resolve();
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
ctx.abortSignal.addEventListener("abort", () => {
|
|
484
|
-
stopWebhookGateway(gatewayCtx);
|
|
485
|
-
resolve();
|
|
486
|
-
}, { once: true });
|
|
487
|
-
});
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
// Agent-only:无 Bot,等待 abort 信号
|
|
491
|
-
return new Promise((resolve) => {
|
|
492
|
-
ctx.abortSignal.addEventListener("abort", () => resolve(), { once: true });
|
|
493
|
-
});
|
|
494
|
-
},
|
|
495
|
-
logoutAccount: async ({ cfg, accountId }) => {
|
|
496
|
-
const resolvedAccountId = accountId ?? DEFAULT_ACCOUNT_ID;
|
|
497
|
-
const isMulti = hasMultiAccounts(cfg);
|
|
498
|
-
let nextCfg = { ...cfg };
|
|
499
|
-
let cleared = false;
|
|
500
|
-
let changed = false;
|
|
501
|
-
if (!isMulti) {
|
|
502
|
-
// 单账号模式:删除顶层 botId/secret
|
|
503
|
-
const wecomConfig = (cfg.channels?.[CHANNEL_ID] ?? {});
|
|
504
|
-
const nextWecom = { ...wecomConfig };
|
|
505
|
-
if (nextWecom.botId || nextWecom.secret) {
|
|
506
|
-
delete nextWecom.botId;
|
|
507
|
-
delete nextWecom.secret;
|
|
508
|
-
cleared = true;
|
|
509
|
-
changed = true;
|
|
510
|
-
}
|
|
511
|
-
if (changed) {
|
|
512
|
-
if (Object.keys(nextWecom).length > 0) {
|
|
513
|
-
nextCfg.channels = { ...nextCfg.channels, [CHANNEL_ID]: nextWecom };
|
|
514
|
-
}
|
|
515
|
-
else {
|
|
516
|
-
const nextChannels = { ...nextCfg.channels };
|
|
517
|
-
delete nextChannels[CHANNEL_ID];
|
|
518
|
-
if (Object.keys(nextChannels).length > 0) {
|
|
519
|
-
nextCfg.channels = nextChannels;
|
|
520
|
-
}
|
|
521
|
-
else {
|
|
522
|
-
delete nextCfg.channels;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
else {
|
|
528
|
-
// 多账号模式:删除指定账号的 botId/secret
|
|
529
|
-
const wecomConfig = (cfg.channels?.[CHANNEL_ID] ?? {});
|
|
530
|
-
const accountCfg = wecomConfig.accounts?.[resolvedAccountId];
|
|
531
|
-
if (accountCfg?.botId || accountCfg?.secret) {
|
|
532
|
-
const nextAccount = { ...accountCfg };
|
|
533
|
-
delete nextAccount.botId;
|
|
534
|
-
delete nextAccount.secret;
|
|
535
|
-
cleared = true;
|
|
536
|
-
changed = true;
|
|
537
|
-
const nextAccounts = { ...wecomConfig.accounts };
|
|
538
|
-
if (Object.keys(nextAccount).length > 0) {
|
|
539
|
-
nextAccounts[resolvedAccountId] = nextAccount;
|
|
540
|
-
}
|
|
541
|
-
else {
|
|
542
|
-
delete nextAccounts[resolvedAccountId];
|
|
543
|
-
}
|
|
544
|
-
nextCfg = {
|
|
545
|
-
...cfg,
|
|
546
|
-
channels: {
|
|
547
|
-
...cfg.channels,
|
|
548
|
-
[CHANNEL_ID]: {
|
|
549
|
-
...wecomConfig,
|
|
550
|
-
accounts: Object.keys(nextAccounts).length > 0 ? nextAccounts : undefined,
|
|
551
|
-
},
|
|
552
|
-
},
|
|
553
|
-
};
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
if (changed) {
|
|
557
|
-
await getWeComRuntime().config.writeConfigFile(nextCfg);
|
|
558
|
-
}
|
|
559
|
-
const resolved = resolveWeComAccountMulti({ cfg: changed ? nextCfg : cfg, accountId: resolvedAccountId });
|
|
560
|
-
const loggedOut = !resolved.botId && !resolved.secret;
|
|
561
|
-
return { cleared, envToken: false, loggedOut };
|
|
562
|
-
},
|
|
563
|
-
},
|
|
564
|
-
};
|
package/dist/src/chat-queue.d.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
type QueueStatus = "queued" | "immediate";
|
|
2
|
-
/**
|
|
3
|
-
* 构建队列键
|
|
4
|
-
*
|
|
5
|
-
* 使用 accountId + chatId 作为维度,确保同一会话中的消息串行处理。
|
|
6
|
-
* 不同会话之间互不影响,可以并行处理。
|
|
7
|
-
*/
|
|
8
|
-
export declare function buildQueueKey(accountId: string, chatId: string): string;
|
|
9
|
-
/**
|
|
10
|
-
* 检查指定会话是否有正在处理的任务
|
|
11
|
-
*/
|
|
12
|
-
export declare function hasActiveTask(key: string): boolean;
|
|
13
|
-
/**
|
|
14
|
-
* 将任务加入串行队列
|
|
15
|
-
*
|
|
16
|
-
* 如果队列中已有任务(status="queued"),新任务会排队等待;
|
|
17
|
-
* 如果队列为空(status="immediate"),任务立即执行。
|
|
18
|
-
*
|
|
19
|
-
* 即使前一个任务失败,后续任务仍会继续执行(.then(task, task))。
|
|
20
|
-
*/
|
|
21
|
-
export declare function enqueueWeComChatTask(params: {
|
|
22
|
-
accountId: string;
|
|
23
|
-
chatId: string;
|
|
24
|
-
task: () => Promise<void>;
|
|
25
|
-
}): {
|
|
26
|
-
status: QueueStatus;
|
|
27
|
-
promise: Promise<void>;
|
|
28
|
-
};
|
|
29
|
-
/** @internal 测试专用:重置所有队列状态 */
|
|
30
|
-
export declare function _resetChatQueueState(): void;
|
|
31
|
-
export {};
|
package/dist/src/chat-queue.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
const chatQueues = new Map();
|
|
2
|
-
/**
|
|
3
|
-
* 构建队列键
|
|
4
|
-
*
|
|
5
|
-
* 使用 accountId + chatId 作为维度,确保同一会话中的消息串行处理。
|
|
6
|
-
* 不同会话之间互不影响,可以并行处理。
|
|
7
|
-
*/
|
|
8
|
-
export function buildQueueKey(accountId, chatId) {
|
|
9
|
-
return `${accountId}:${chatId}`;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* 检查指定会话是否有正在处理的任务
|
|
13
|
-
*/
|
|
14
|
-
export function hasActiveTask(key) {
|
|
15
|
-
return chatQueues.has(key);
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* 将任务加入串行队列
|
|
19
|
-
*
|
|
20
|
-
* 如果队列中已有任务(status="queued"),新任务会排队等待;
|
|
21
|
-
* 如果队列为空(status="immediate"),任务立即执行。
|
|
22
|
-
*
|
|
23
|
-
* 即使前一个任务失败,后续任务仍会继续执行(.then(task, task))。
|
|
24
|
-
*/
|
|
25
|
-
export function enqueueWeComChatTask(params) {
|
|
26
|
-
const { accountId, chatId, task } = params;
|
|
27
|
-
const key = buildQueueKey(accountId, chatId);
|
|
28
|
-
const prev = chatQueues.get(key) ?? Promise.resolve();
|
|
29
|
-
const status = chatQueues.has(key) ? "queued" : "immediate";
|
|
30
|
-
// continue queue even if previous task failed
|
|
31
|
-
const next = prev.then(task, task);
|
|
32
|
-
chatQueues.set(key, next);
|
|
33
|
-
const cleanup = () => {
|
|
34
|
-
// 只有当前任务仍是队列末尾时才清理,避免误删后续任务
|
|
35
|
-
if (chatQueues.get(key) === next) {
|
|
36
|
-
chatQueues.delete(key);
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
next.then(cleanup, cleanup);
|
|
40
|
-
return { status, promise: next };
|
|
41
|
-
}
|
|
42
|
-
/** @internal 测试专用:重置所有队列状态 */
|
|
43
|
-
export function _resetChatQueueState() {
|
|
44
|
-
chatQueues.clear();
|
|
45
|
-
}
|
package/dist/src/http.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* **WecomHttpOptions (HTTP 选项)**
|
|
3
|
-
*
|
|
4
|
-
* @property proxyUrl 代理服务器地址
|
|
5
|
-
* @property timeoutMs 请求超时时间 (毫秒)
|
|
6
|
-
* @property signal AbortSignal 信号
|
|
7
|
-
*/
|
|
8
|
-
export type WecomHttpOptions = {
|
|
9
|
-
proxyUrl?: string;
|
|
10
|
-
timeoutMs?: number;
|
|
11
|
-
signal?: AbortSignal;
|
|
12
|
-
};
|
|
13
|
-
/**
|
|
14
|
-
* **wecomFetch (统一 HTTP 请求)**
|
|
15
|
-
*
|
|
16
|
-
* 基于 `undici` 的 fetch 封装,自动处理 ProxyAgent 和 Timeout。
|
|
17
|
-
* 所有对企业微信 API 的调用都应经过此函数。
|
|
18
|
-
*/
|
|
19
|
-
export declare function wecomFetch(input: string | URL, init?: RequestInit, opts?: WecomHttpOptions): Promise<Response>;
|
|
20
|
-
/**
|
|
21
|
-
* **readResponseBodyAsBuffer (读取响应 Body)**
|
|
22
|
-
*
|
|
23
|
-
* 将 Response Body 读取为 Buffer,支持最大字节限制以防止内存溢出。
|
|
24
|
-
* 适用于下载媒体文件等场景。
|
|
25
|
-
*/
|
|
26
|
-
export declare function readResponseBodyAsBuffer(res: Response, maxBytes?: number): Promise<Buffer>;
|