@wecom/wecom-openclaw-plugin 2026.4.2-beta.0 → 2026.4.3-beta.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 +395 -10
- package/dist/index.d.ts +2 -4
- package/dist/index.js +58 -0
- package/dist/src/accounts.d.ts +57 -0
- package/dist/src/accounts.js +222 -0
- package/dist/src/agent/api-client.d.ts +94 -0
- package/dist/src/agent/api-client.js +292 -0
- package/dist/src/agent/handler.d.ts +63 -0
- package/dist/src/agent/handler.js +551 -0
- package/dist/src/agent/index.d.ts +5 -0
- package/dist/src/agent/index.js +5 -0
- package/dist/src/agent/webhook.d.ts +24 -0
- package/dist/src/agent/webhook.js +228 -0
- package/dist/src/agent/xml.d.ts +21 -0
- package/dist/src/agent/xml.js +41 -0
- package/dist/src/channel.js +579 -0
- package/dist/src/chat-queue.d.ts +31 -0
- package/dist/src/chat-queue.js +45 -0
- package/dist/{esm/types/src → src}/const.d.ts +46 -0
- package/dist/{esm/src → src}/const.js +77 -30
- package/dist/{esm/src → src}/dm-policy.js +6 -9
- package/dist/src/dynamic-agent.d.ts +37 -0
- package/dist/src/dynamic-agent.js +60 -0
- package/dist/src/dynamic-routing.d.ts +65 -0
- package/dist/src/dynamic-routing.js +58 -0
- package/dist/{esm/src → src}/group-policy.js +8 -22
- package/dist/src/http.d.ts +26 -0
- package/dist/src/http.js +93 -0
- package/dist/{esm/types/src → src}/interface.d.ts +0 -2
- package/dist/src/interface.js +4 -0
- package/dist/src/mcp/index.js +6 -0
- package/dist/{esm/src → src}/mcp/interceptors/biz-error.js +2 -6
- package/dist/{esm/src → src}/mcp/interceptors/index.js +6 -10
- package/dist/{esm/src → src}/mcp/interceptors/msg-media.js +6 -10
- package/dist/{esm/src → src}/mcp/interceptors/smartpage-create.js +2 -6
- package/dist/{esm/src → src}/mcp/interceptors/smartpage-export.js +2 -6
- package/dist/src/mcp/interceptors/types.js +7 -0
- package/dist/{esm/src → src}/mcp/schema.js +1 -4
- package/dist/{esm/src → src}/mcp/tool.js +4 -8
- package/dist/{esm/src → src}/mcp/transport.js +15 -16
- package/dist/{esm/src → src}/media-handler.js +6 -10
- package/dist/{esm/src → src}/media-uploader.js +64 -10
- package/dist/{esm/src → src}/message-parser.js +1 -4
- package/dist/{esm/types/src → src}/message-sender.d.ts +18 -0
- package/dist/{esm/src → src}/message-sender.js +45 -11
- package/dist/{esm/src → src}/monitor.js +145 -292
- package/dist/{esm/src → src}/onboarding.js +22 -26
- package/dist/{esm/types/src → src}/openclaw-compat.d.ts +49 -0
- package/dist/{esm/src → src}/openclaw-compat.js +133 -27
- package/dist/{esm/src → src}/reqid-store.js +4 -7
- package/dist/{esm/src → src}/runtime.js +2 -5
- package/dist/src/shared/command-auth.d.ts +23 -0
- package/dist/src/shared/command-auth.js +73 -0
- package/dist/src/shared/xml-parser.d.ts +46 -0
- package/dist/src/shared/xml-parser.js +210 -0
- package/dist/{esm/src → src}/state-dir-resolve.js +3 -7
- package/dist/{esm/src → src}/state-manager.js +84 -16
- package/dist/src/target.d.ts +35 -0
- package/dist/src/target.js +67 -0
- package/dist/src/template-card-manager.d.ts +55 -0
- package/dist/src/template-card-manager.js +205 -0
- package/dist/{esm/src → src}/template-card-parser.js +17 -8
- package/dist/{esm/src → src}/timeout.js +2 -5
- package/dist/src/types/account.d.ts +29 -0
- package/dist/src/types/account.js +4 -0
- package/dist/src/types/config.d.ts +91 -0
- package/dist/src/types/config.js +7 -0
- package/dist/src/types/constants.d.ts +42 -0
- package/dist/src/types/constants.js +42 -0
- package/dist/src/types/index.d.ts +7 -0
- package/dist/src/types/index.js +5 -0
- package/dist/src/types/message.d.ts +238 -0
- package/dist/src/types/message.js +5 -0
- package/dist/{esm/types/src → src}/utils.d.ts +41 -0
- package/dist/{esm/src → src}/utils.js +24 -10
- package/dist/src/version.d.ts +2 -0
- package/dist/src/version.js +24 -0
- package/dist/src/webhook/command-auth.d.ts +47 -0
- package/dist/src/webhook/command-auth.js +96 -0
- package/dist/src/webhook/gateway.d.ts +36 -0
- package/dist/src/webhook/gateway.js +235 -0
- package/dist/src/webhook/handler.d.ts +18 -0
- package/dist/src/webhook/handler.js +420 -0
- package/dist/src/webhook/helpers.d.ts +156 -0
- package/dist/src/webhook/helpers.js +833 -0
- package/dist/src/webhook/http.d.ts +26 -0
- package/dist/src/webhook/http.js +94 -0
- package/dist/src/webhook/index.d.ts +11 -0
- package/dist/src/webhook/index.js +14 -0
- package/dist/src/webhook/media.d.ts +29 -0
- package/dist/src/webhook/media.js +88 -0
- package/dist/src/webhook/monitor.d.ts +59 -0
- package/dist/src/webhook/monitor.js +1161 -0
- package/dist/src/webhook/state.d.ts +220 -0
- package/dist/src/webhook/state.js +491 -0
- package/dist/src/webhook/target.d.ts +41 -0
- package/dist/src/webhook/target.js +132 -0
- package/dist/src/webhook/types.d.ts +347 -0
- package/dist/src/webhook/types.js +33 -0
- package/dist/src/webhook/video-frame.d.ts +13 -0
- package/dist/src/webhook/video-frame.js +30 -0
- package/package.json +15 -7
- package/dist/cjs/index.js +0 -45
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/src/channel.js +0 -339
- package/dist/cjs/src/channel.js.map +0 -1
- package/dist/cjs/src/const.js +0 -137
- package/dist/cjs/src/const.js.map +0 -1
- package/dist/cjs/src/dm-policy.js +0 -89
- package/dist/cjs/src/dm-policy.js.map +0 -1
- package/dist/cjs/src/group-policy.js +0 -133
- package/dist/cjs/src/group-policy.js.map +0 -1
- package/dist/cjs/src/mcp/interceptors/biz-error.js +0 -70
- package/dist/cjs/src/mcp/interceptors/biz-error.js.map +0 -1
- package/dist/cjs/src/mcp/interceptors/index.js +0 -73
- package/dist/cjs/src/mcp/interceptors/index.js.map +0 -1
- package/dist/cjs/src/mcp/interceptors/msg-media.js +0 -159
- package/dist/cjs/src/mcp/interceptors/msg-media.js.map +0 -1
- package/dist/cjs/src/mcp/interceptors/smartpage-create.js +0 -157
- package/dist/cjs/src/mcp/interceptors/smartpage-create.js.map +0 -1
- package/dist/cjs/src/mcp/interceptors/smartpage-export.js +0 -93
- package/dist/cjs/src/mcp/interceptors/smartpage-export.js.map +0 -1
- package/dist/cjs/src/mcp/schema.js +0 -104
- package/dist/cjs/src/mcp/schema.js.map +0 -1
- package/dist/cjs/src/mcp/tool.js +0 -208
- package/dist/cjs/src/mcp/tool.js.map +0 -1
- package/dist/cjs/src/mcp/transport.js +0 -417
- package/dist/cjs/src/mcp/transport.js.map +0 -1
- package/dist/cjs/src/media-handler.js +0 -129
- package/dist/cjs/src/media-handler.js.map +0 -1
- package/dist/cjs/src/media-uploader.js +0 -284
- package/dist/cjs/src/media-uploader.js.map +0 -1
- package/dist/cjs/src/message-parser.js +0 -134
- package/dist/cjs/src/message-parser.js.map +0 -1
- package/dist/cjs/src/message-sender.js +0 -89
- package/dist/cjs/src/message-sender.js.map +0 -1
- package/dist/cjs/src/monitor.js +0 -991
- package/dist/cjs/src/monitor.js.map +0 -1
- package/dist/cjs/src/onboarding.js +0 -182
- package/dist/cjs/src/onboarding.js.map +0 -1
- package/dist/cjs/src/openclaw-compat.js +0 -400
- package/dist/cjs/src/openclaw-compat.js.map +0 -1
- package/dist/cjs/src/reqid-store.js +0 -88
- package/dist/cjs/src/reqid-store.js.map +0 -1
- package/dist/cjs/src/runtime.js +0 -16
- package/dist/cjs/src/runtime.js.map +0 -1
- package/dist/cjs/src/state-dir-resolve.js +0 -35
- package/dist/cjs/src/state-dir-resolve.js.map +0 -1
- package/dist/cjs/src/state-manager.js +0 -158
- package/dist/cjs/src/state-manager.js.map +0 -1
- package/dist/cjs/src/template-card-parser.js +0 -641
- package/dist/cjs/src/template-card-parser.js.map +0 -1
- package/dist/cjs/src/timeout.js +0 -42
- package/dist/cjs/src/timeout.js.map +0 -1
- package/dist/cjs/src/utils.js +0 -60
- package/dist/cjs/src/utils.js.map +0 -1
- package/dist/cjs/src/version.js +0 -26
- package/dist/cjs/src/version.js.map +0 -1
- package/dist/esm/index.js +0 -41
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/src/channel.js +0 -337
- package/dist/esm/src/channel.js.map +0 -1
- package/dist/esm/src/const.js.map +0 -1
- package/dist/esm/src/dm-policy.js.map +0 -1
- package/dist/esm/src/group-policy.js.map +0 -1
- package/dist/esm/src/mcp/interceptors/biz-error.js.map +0 -1
- package/dist/esm/src/mcp/interceptors/index.js.map +0 -1
- package/dist/esm/src/mcp/interceptors/msg-media.js.map +0 -1
- package/dist/esm/src/mcp/interceptors/smartpage-create.js.map +0 -1
- package/dist/esm/src/mcp/interceptors/smartpage-export.js.map +0 -1
- package/dist/esm/src/mcp/schema.js.map +0 -1
- package/dist/esm/src/mcp/tool.js.map +0 -1
- package/dist/esm/src/mcp/transport.js.map +0 -1
- package/dist/esm/src/media-handler.js.map +0 -1
- package/dist/esm/src/media-uploader.js.map +0 -1
- package/dist/esm/src/message-parser.js.map +0 -1
- package/dist/esm/src/message-sender.js.map +0 -1
- package/dist/esm/src/monitor.js.map +0 -1
- package/dist/esm/src/onboarding.js.map +0 -1
- package/dist/esm/src/openclaw-compat.js.map +0 -1
- package/dist/esm/src/reqid-store.js.map +0 -1
- package/dist/esm/src/runtime.js.map +0 -1
- package/dist/esm/src/state-dir-resolve.js.map +0 -1
- package/dist/esm/src/state-manager.js.map +0 -1
- package/dist/esm/src/template-card-parser.js.map +0 -1
- package/dist/esm/src/timeout.js.map +0 -1
- package/dist/esm/src/utils.js.map +0 -1
- package/dist/esm/src/version.js +0 -23
- package/dist/esm/src/version.js.map +0 -1
- package/dist/esm/types/index.d.ts +0 -9
- package/dist/esm/types/src/version.d.ts +0 -2
- /package/dist/{esm/types/src → src}/channel.d.ts +0 -0
- /package/dist/{esm/types/src → src}/dm-policy.d.ts +0 -0
- /package/dist/{esm/types/src → src}/group-policy.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/index.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/interceptors/biz-error.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/interceptors/index.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/interceptors/msg-media.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/interceptors/smartpage-create.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/interceptors/smartpage-export.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/interceptors/types.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/schema.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/tool.d.ts +0 -0
- /package/dist/{esm/types/src → src}/mcp/transport.d.ts +0 -0
- /package/dist/{esm/types/src → src}/media-handler.d.ts +0 -0
- /package/dist/{esm/types/src → src}/media-uploader.d.ts +0 -0
- /package/dist/{esm/types/src → src}/message-parser.d.ts +0 -0
- /package/dist/{esm/types/src → src}/monitor.d.ts +0 -0
- /package/dist/{esm/types/src → src}/onboarding.d.ts +0 -0
- /package/dist/{esm/types/src → src}/reqid-store.d.ts +0 -0
- /package/dist/{esm/types/src → src}/runtime.d.ts +0 -0
- /package/dist/{esm/types/src → src}/state-dir-resolve.d.ts +0 -0
- /package/dist/{esm/types/src → src}/state-manager.d.ts +0 -0
- /package/dist/{esm/types/src → src}/template-card-parser.d.ts +0 -0
- /package/dist/{esm/types/src → src}/timeout.d.ts +0 -0
|
@@ -70,5 +70,51 @@ export declare const EVENT_ENTER_CHECK_UPDATE = "event.enter_check_update";
|
|
|
70
70
|
export declare const CMD_ENTER_EVENT_REPLY = "ww_ai_robot_enter_event";
|
|
71
71
|
/** WSClient scene 参数:企微 OpenClaw 场景 */
|
|
72
72
|
export declare const SCENE_WECOM_OPENCLAW = 1;
|
|
73
|
+
/**
|
|
74
|
+
* WeCom 双模式常量定义
|
|
75
|
+
*/
|
|
76
|
+
/** 固定 Webhook 路径 */
|
|
77
|
+
export declare const WEBHOOK_PATHS: {
|
|
78
|
+
/** Bot 模式历史兼容路径(不再维护) */
|
|
79
|
+
readonly BOT: "/wecom";
|
|
80
|
+
/** Bot 模式历史备用兼容路径(不再维护) */
|
|
81
|
+
readonly BOT_ALT: "/wecom/bot";
|
|
82
|
+
/** Agent 模式历史兼容路径(不再维护) */
|
|
83
|
+
readonly AGENT: "/wecom/agent";
|
|
84
|
+
/** Bot 模式推荐路径前缀 */
|
|
85
|
+
readonly BOT_PLUGIN: "/plugins/wecom/bot";
|
|
86
|
+
/** Agent 模式推荐路径前缀 */
|
|
87
|
+
readonly AGENT_PLUGIN: "/plugins/wecom/agent";
|
|
88
|
+
};
|
|
89
|
+
/** 企业微信 API 端点 */
|
|
90
|
+
export declare const API_ENDPOINTS: {
|
|
91
|
+
readonly GET_TOKEN: "https://qyapi.weixin.qq.com/cgi-bin/gettoken";
|
|
92
|
+
readonly SEND_MESSAGE: "https://qyapi.weixin.qq.com/cgi-bin/message/send";
|
|
93
|
+
readonly SEND_APPCHAT: "https://qyapi.weixin.qq.com/cgi-bin/appchat/send";
|
|
94
|
+
readonly UPLOAD_MEDIA: "https://qyapi.weixin.qq.com/cgi-bin/media/upload";
|
|
95
|
+
readonly DOWNLOAD_MEDIA: "https://qyapi.weixin.qq.com/cgi-bin/media/get";
|
|
96
|
+
};
|
|
97
|
+
/** 各类限制常量 */
|
|
98
|
+
export declare const LIMITS: {
|
|
99
|
+
/** 文本消息最大字节数 */
|
|
100
|
+
readonly TEXT_MAX_BYTES: 2048;
|
|
101
|
+
/** Token 刷新缓冲时间 (提前刷新) */
|
|
102
|
+
readonly TOKEN_REFRESH_BUFFER_MS: 60000;
|
|
103
|
+
/** HTTP 请求超时 */
|
|
104
|
+
readonly REQUEST_TIMEOUT_MS: 15000;
|
|
105
|
+
/** 最大请求体大小 */
|
|
106
|
+
readonly MAX_REQUEST_BODY_SIZE: number;
|
|
107
|
+
};
|
|
108
|
+
/** AES 加密常量 */
|
|
109
|
+
export declare const CRYPTO: {
|
|
110
|
+
/** PKCS#7 块大小 */
|
|
111
|
+
readonly PKCS7_BLOCK_SIZE: 32;
|
|
112
|
+
/** AES Key 长度 */
|
|
113
|
+
readonly AES_KEY_LENGTH: 32;
|
|
114
|
+
};
|
|
73
115
|
/** 合法的模板卡片 card_type 列表 */
|
|
74
116
|
export declare const VALID_CARD_TYPES: string[];
|
|
117
|
+
/** 模板卡片缓存条目 TTL(毫秒):24小时 */
|
|
118
|
+
export declare const TEMPLATE_CARD_CACHE_TTL_MS: number;
|
|
119
|
+
/** 模板卡片缓存最大条目数 */
|
|
120
|
+
export declare const TEMPLATE_CARD_CACHE_MAX_SIZE = 300;
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* 企业微信渠道 ID
|
|
6
6
|
*/
|
|
7
|
-
const CHANNEL_ID = "wecom";
|
|
7
|
+
export const CHANNEL_ID = "wecom";
|
|
8
8
|
/**
|
|
9
9
|
* 企业微信 WebSocket 命令枚举
|
|
10
10
|
*/
|
|
11
|
-
var WeComCommand;
|
|
11
|
+
export var WeComCommand;
|
|
12
12
|
(function (WeComCommand) {
|
|
13
13
|
/** 认证订阅 */
|
|
14
14
|
WeComCommand["SUBSCRIBE"] = "aibot_subscribe";
|
|
@@ -23,35 +23,37 @@ var WeComCommand;
|
|
|
23
23
|
// 超时和重试配置
|
|
24
24
|
// ============================================================================
|
|
25
25
|
/** 图片下载超时时间(毫秒) */
|
|
26
|
-
const IMAGE_DOWNLOAD_TIMEOUT_MS =
|
|
26
|
+
export const IMAGE_DOWNLOAD_TIMEOUT_MS = 30_000;
|
|
27
27
|
/** 文件下载超时时间(毫秒) */
|
|
28
|
-
const FILE_DOWNLOAD_TIMEOUT_MS =
|
|
28
|
+
export const FILE_DOWNLOAD_TIMEOUT_MS = 60_000;
|
|
29
29
|
/** 消息发送超时时间(毫秒) */
|
|
30
|
-
const REPLY_SEND_TIMEOUT_MS =
|
|
30
|
+
export const REPLY_SEND_TIMEOUT_MS = 15_000;
|
|
31
|
+
/** 消息处理总超时时间(毫秒) */
|
|
32
|
+
export const MESSAGE_PROCESS_TIMEOUT_MS = 6 * 60 * 1000;
|
|
31
33
|
/** WebSocket 心跳间隔(毫秒) */
|
|
32
|
-
const WS_HEARTBEAT_INTERVAL_MS =
|
|
34
|
+
export const WS_HEARTBEAT_INTERVAL_MS = 30_000;
|
|
33
35
|
/** WebSocket 连接断开时的最大重连次数 */
|
|
34
|
-
const WS_MAX_RECONNECT_ATTEMPTS = 10;
|
|
36
|
+
export const WS_MAX_RECONNECT_ATTEMPTS = 10;
|
|
35
37
|
/** WebSocket 认证失败时的最大重试次数 */
|
|
36
|
-
const WS_MAX_AUTH_FAILURE_ATTEMPTS = 5;
|
|
38
|
+
export const WS_MAX_AUTH_FAILURE_ATTEMPTS = 5;
|
|
37
39
|
// ============================================================================
|
|
38
40
|
// 消息状态管理配置
|
|
39
41
|
// ============================================================================
|
|
40
42
|
/** messageStates Map 条目的最大 TTL(毫秒),防止内存泄漏 */
|
|
41
|
-
const MESSAGE_STATE_TTL_MS = 10 * 60 * 1000;
|
|
43
|
+
export const MESSAGE_STATE_TTL_MS = 10 * 60 * 1000;
|
|
42
44
|
/** messageStates Map 清理间隔(毫秒) */
|
|
43
|
-
const MESSAGE_STATE_CLEANUP_INTERVAL_MS =
|
|
45
|
+
export const MESSAGE_STATE_CLEANUP_INTERVAL_MS = 60_000;
|
|
44
46
|
/** messageStates Map 最大条目数 */
|
|
45
|
-
const MESSAGE_STATE_MAX_SIZE = 500;
|
|
47
|
+
export const MESSAGE_STATE_MAX_SIZE = 500;
|
|
46
48
|
// ============================================================================
|
|
47
49
|
// 消息模板
|
|
48
50
|
// ============================================================================
|
|
49
51
|
/** "思考中"流式消息占位内容 */
|
|
50
|
-
const THINKING_MESSAGE = "<think></think>";
|
|
52
|
+
export const THINKING_MESSAGE = "<think></think>";
|
|
51
53
|
/** 仅包含图片时的消息占位符 */
|
|
52
|
-
const MEDIA_IMAGE_PLACEHOLDER = "<media:image>";
|
|
54
|
+
export const MEDIA_IMAGE_PLACEHOLDER = "<media:image>";
|
|
53
55
|
/** 仅包含文件时的消息占位符 */
|
|
54
|
-
const MEDIA_DOCUMENT_PLACEHOLDER = "<media:document>";
|
|
56
|
+
export const MEDIA_DOCUMENT_PLACEHOLDER = "<media:document>";
|
|
55
57
|
// ============================================================================
|
|
56
58
|
// 默认值
|
|
57
59
|
// ============================================================================
|
|
@@ -59,52 +61,97 @@ const MEDIA_DOCUMENT_PLACEHOLDER = "<media:document>";
|
|
|
59
61
|
// MCP 配置
|
|
60
62
|
// ============================================================================
|
|
61
63
|
/** 获取 MCP 配置的 WebSocket 命令 */
|
|
62
|
-
const MCP_GET_CONFIG_CMD = "aibot_get_mcp_config";
|
|
64
|
+
export const MCP_GET_CONFIG_CMD = "aibot_get_mcp_config";
|
|
63
65
|
/** MCP 配置拉取超时时间(毫秒) */
|
|
64
|
-
const MCP_CONFIG_FETCH_TIMEOUT_MS =
|
|
66
|
+
export const MCP_CONFIG_FETCH_TIMEOUT_MS = 15_000;
|
|
65
67
|
// ============================================================================
|
|
66
68
|
// 默认值
|
|
67
69
|
// ============================================================================
|
|
68
70
|
/** 默认媒体大小上限(MB) */
|
|
69
|
-
const DEFAULT_MEDIA_MAX_MB = 5;
|
|
71
|
+
export const DEFAULT_MEDIA_MAX_MB = 5;
|
|
70
72
|
/** 文本分块大小上限 */
|
|
71
|
-
const TEXT_CHUNK_LIMIT = 4000;
|
|
73
|
+
export const TEXT_CHUNK_LIMIT = 4000;
|
|
72
74
|
// ============================================================================
|
|
73
75
|
// 媒体上传相关常量
|
|
74
76
|
// ============================================================================
|
|
75
77
|
/** 图片大小上限(字节):10MB */
|
|
76
|
-
const IMAGE_MAX_BYTES = 10 * 1024 * 1024;
|
|
78
|
+
export const IMAGE_MAX_BYTES = 10 * 1024 * 1024;
|
|
77
79
|
/** 视频大小上限(字节):10MB */
|
|
78
|
-
const VIDEO_MAX_BYTES = 10 * 1024 * 1024;
|
|
80
|
+
export const VIDEO_MAX_BYTES = 10 * 1024 * 1024;
|
|
79
81
|
/** 语音大小上限(字节):2MB */
|
|
80
|
-
const VOICE_MAX_BYTES = 2 * 1024 * 1024;
|
|
82
|
+
export const VOICE_MAX_BYTES = 2 * 1024 * 1024;
|
|
81
83
|
/** 文件大小上限(字节):20MB */
|
|
82
|
-
const FILE_MAX_BYTES = 20 * 1024 * 1024;
|
|
84
|
+
export const FILE_MAX_BYTES = 20 * 1024 * 1024;
|
|
83
85
|
/** 文件绝对上限(字节):超过此值无法发送,等于 FILE_MAX_BYTES */
|
|
84
|
-
const ABSOLUTE_MAX_BYTES = FILE_MAX_BYTES;
|
|
86
|
+
export const ABSOLUTE_MAX_BYTES = FILE_MAX_BYTES;
|
|
87
|
+
/** 上传分片大小(字节,Base64 编码前):512KB */
|
|
88
|
+
export const UPLOAD_CHUNK_SIZE = 512 * 1024;
|
|
85
89
|
// ============================================================================
|
|
86
90
|
// 事件/命令名称常量
|
|
87
91
|
// ============================================================================
|
|
88
92
|
/** 版本检查事件名称(SDK 事件监听用) */
|
|
89
|
-
const EVENT_ENTER_CHECK_UPDATE = "event.enter_check_update";
|
|
93
|
+
export const EVENT_ENTER_CHECK_UPDATE = "event.enter_check_update";
|
|
90
94
|
/** 版本检查事件回复命令名称 */
|
|
91
|
-
const CMD_ENTER_EVENT_REPLY = "ww_ai_robot_enter_event";
|
|
95
|
+
export const CMD_ENTER_EVENT_REPLY = "ww_ai_robot_enter_event";
|
|
92
96
|
// ============================================================================
|
|
93
97
|
// SDK 连接配置
|
|
94
98
|
// ============================================================================
|
|
95
99
|
/** WSClient scene 参数:企微 OpenClaw 场景 */
|
|
96
|
-
const SCENE_WECOM_OPENCLAW = 1;
|
|
100
|
+
export const SCENE_WECOM_OPENCLAW = 1;
|
|
101
|
+
/**
|
|
102
|
+
* WeCom 双模式常量定义
|
|
103
|
+
*/
|
|
104
|
+
/** 固定 Webhook 路径 */
|
|
105
|
+
export const WEBHOOK_PATHS = {
|
|
106
|
+
/** Bot 模式历史兼容路径(不再维护) */
|
|
107
|
+
BOT: "/wecom",
|
|
108
|
+
/** Bot 模式历史备用兼容路径(不再维护) */
|
|
109
|
+
BOT_ALT: "/wecom/bot",
|
|
110
|
+
/** Agent 模式历史兼容路径(不再维护) */
|
|
111
|
+
AGENT: "/wecom/agent",
|
|
112
|
+
/** Bot 模式推荐路径前缀 */
|
|
113
|
+
BOT_PLUGIN: "/plugins/wecom/bot",
|
|
114
|
+
/** Agent 模式推荐路径前缀 */
|
|
115
|
+
AGENT_PLUGIN: "/plugins/wecom/agent",
|
|
116
|
+
};
|
|
117
|
+
/** 企业微信 API 端点 */
|
|
118
|
+
export const API_ENDPOINTS = {
|
|
119
|
+
GET_TOKEN: "https://qyapi.weixin.qq.com/cgi-bin/gettoken",
|
|
120
|
+
SEND_MESSAGE: "https://qyapi.weixin.qq.com/cgi-bin/message/send",
|
|
121
|
+
SEND_APPCHAT: "https://qyapi.weixin.qq.com/cgi-bin/appchat/send",
|
|
122
|
+
UPLOAD_MEDIA: "https://qyapi.weixin.qq.com/cgi-bin/media/upload",
|
|
123
|
+
DOWNLOAD_MEDIA: "https://qyapi.weixin.qq.com/cgi-bin/media/get",
|
|
124
|
+
};
|
|
125
|
+
/** 各类限制常量 */
|
|
126
|
+
export const LIMITS = {
|
|
127
|
+
/** 文本消息最大字节数 */
|
|
128
|
+
TEXT_MAX_BYTES: 2048,
|
|
129
|
+
/** Token 刷新缓冲时间 (提前刷新) */
|
|
130
|
+
TOKEN_REFRESH_BUFFER_MS: 60_000,
|
|
131
|
+
/** HTTP 请求超时 */
|
|
132
|
+
REQUEST_TIMEOUT_MS: 15_000,
|
|
133
|
+
/** 最大请求体大小 */
|
|
134
|
+
MAX_REQUEST_BODY_SIZE: 1024 * 1024,
|
|
135
|
+
};
|
|
136
|
+
/** AES 加密常量 */
|
|
137
|
+
export const CRYPTO = {
|
|
138
|
+
/** PKCS#7 块大小 */
|
|
139
|
+
PKCS7_BLOCK_SIZE: 32,
|
|
140
|
+
/** AES Key 长度 */
|
|
141
|
+
AES_KEY_LENGTH: 32,
|
|
142
|
+
};
|
|
97
143
|
// ============================================================================
|
|
98
144
|
// 模板卡片配置
|
|
99
145
|
// ============================================================================
|
|
100
146
|
/** 合法的模板卡片 card_type 列表 */
|
|
101
|
-
const VALID_CARD_TYPES = [
|
|
147
|
+
export const VALID_CARD_TYPES = [
|
|
102
148
|
"text_notice",
|
|
103
149
|
"news_notice",
|
|
104
150
|
"button_interaction",
|
|
105
151
|
"vote_interaction",
|
|
106
152
|
"multiple_interaction",
|
|
107
153
|
];
|
|
108
|
-
|
|
109
|
-
export
|
|
110
|
-
|
|
154
|
+
/** 模板卡片缓存条目 TTL(毫秒):24小时 */
|
|
155
|
+
export const TEMPLATE_CARD_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
156
|
+
/** 模板卡片缓存最大条目数 */
|
|
157
|
+
export const TEMPLATE_CARD_CACHE_MAX_SIZE = 300;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { getWeComRuntime } from './runtime.js';
|
|
2
|
-
import { CHANNEL_ID } from './const.js';
|
|
3
|
-
import { sendWeComReply } from './message-sender.js';
|
|
4
|
-
import { isSenderAllowed } from './group-policy.js';
|
|
5
|
-
|
|
6
1
|
/**
|
|
7
2
|
* 企业微信 DM(私聊)访问控制模块
|
|
8
3
|
*
|
|
9
4
|
* 负责私聊策略检查、配对流程
|
|
10
5
|
*/
|
|
6
|
+
import { getWeComRuntime } from "./runtime.js";
|
|
7
|
+
import { CHANNEL_ID } from "./const.js";
|
|
8
|
+
import { sendWeComReply } from "./message-sender.js";
|
|
9
|
+
import { isSenderAllowed } from "./group-policy.js";
|
|
11
10
|
// ============================================================================
|
|
12
11
|
// 公开 API
|
|
13
12
|
// ============================================================================
|
|
@@ -15,7 +14,7 @@ import { isSenderAllowed } from './group-policy.js';
|
|
|
15
14
|
* 检查 DM Policy 访问控制
|
|
16
15
|
* @returns 检查结果,包含是否允许继续处理
|
|
17
16
|
*/
|
|
18
|
-
async function checkDmPolicy(params) {
|
|
17
|
+
export async function checkDmPolicy(params) {
|
|
19
18
|
const { senderId, isGroup, account, wsClient, frame, runtime } = params;
|
|
20
19
|
const core = getWeComRuntime();
|
|
21
20
|
// 群聊消息不检查 DM Policy
|
|
@@ -34,6 +33,7 @@ async function checkDmPolicy(params) {
|
|
|
34
33
|
return { allowed: true };
|
|
35
34
|
}
|
|
36
35
|
// OpenClaw <= 2026.2.19 signature: readAllowFromStore(channel, env?, accountId?)
|
|
36
|
+
// @ts-expect-error — 兼容旧版 SDK 的三参数签名,新版已改为单参数对象
|
|
37
37
|
const oldStoreAllowFrom = await core.channel.pairing.readAllowFromStore('wecom', undefined, account.accountId).catch(() => []);
|
|
38
38
|
// Compatibility fallback for newer OpenClaw implementations.
|
|
39
39
|
const newStoreAllowFrom = await core.channel.pairing
|
|
@@ -82,6 +82,3 @@ async function checkDmPolicy(params) {
|
|
|
82
82
|
runtime.log?.(`[WeCom] Blocked unauthorized sender ${senderId} (dmPolicy=${dmPolicy})`);
|
|
83
83
|
return { allowed: false };
|
|
84
84
|
}
|
|
85
|
-
|
|
86
|
-
export { checkDmPolicy };
|
|
87
|
-
//# sourceMappingURL=dm-policy.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **动态 Agent 路由模块**
|
|
3
|
+
*
|
|
4
|
+
* 为每个用户/群组自动生成独立的 Agent ID,实现会话隔离。
|
|
5
|
+
* 参考: openclaw-plugin-wecom/dynamic-agent.js
|
|
6
|
+
*/
|
|
7
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk";
|
|
8
|
+
export interface DynamicAgentConfig {
|
|
9
|
+
enabled: boolean;
|
|
10
|
+
dmCreateAgent: boolean;
|
|
11
|
+
groupEnabled: boolean;
|
|
12
|
+
adminUsers: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* **getDynamicAgentConfig (读取动态 Agent 配置)**
|
|
16
|
+
*
|
|
17
|
+
* 从全局配置中读取动态 Agent 配置,提供默认值。
|
|
18
|
+
*/
|
|
19
|
+
export declare function getDynamicAgentConfig(config: OpenClawConfig): DynamicAgentConfig;
|
|
20
|
+
/**
|
|
21
|
+
* **generateAgentId (生成动态 Agent ID)**
|
|
22
|
+
*
|
|
23
|
+
* 根据账号 + 聊天类型 + 对端 ID 生成确定性的 Agent ID,避免多账号串会话。
|
|
24
|
+
* 格式: wecom-{accountId}-{type}-{sanitizedPeerId}
|
|
25
|
+
*/
|
|
26
|
+
export declare function generateAgentId(chatType: "dm" | "group", peerId: string, accountId?: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* **shouldUseDynamicAgent (检查是否使用动态 Agent)**
|
|
29
|
+
*
|
|
30
|
+
* 根据配置和发送者信息判断是否应使用动态 Agent。
|
|
31
|
+
* 管理员(adminUsers)始终绕过动态路由,使用主 Agent。
|
|
32
|
+
*/
|
|
33
|
+
export declare function shouldUseDynamicAgent(params: {
|
|
34
|
+
chatType: "dm" | "group";
|
|
35
|
+
senderId: string;
|
|
36
|
+
config: OpenClawConfig;
|
|
37
|
+
}): boolean;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **动态 Agent 路由模块**
|
|
3
|
+
*
|
|
4
|
+
* 为每个用户/群组自动生成独立的 Agent ID,实现会话隔离。
|
|
5
|
+
* 参考: openclaw-plugin-wecom/dynamic-agent.js
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* **getDynamicAgentConfig (读取动态 Agent 配置)**
|
|
9
|
+
*
|
|
10
|
+
* 从全局配置中读取动态 Agent 配置,提供默认值。
|
|
11
|
+
*/
|
|
12
|
+
export function getDynamicAgentConfig(config) {
|
|
13
|
+
const dynamicAgents = config?.channels?.wecom?.dynamicAgents;
|
|
14
|
+
return {
|
|
15
|
+
enabled: dynamicAgents?.enabled ?? false,
|
|
16
|
+
dmCreateAgent: dynamicAgents?.dmCreateAgent ?? true,
|
|
17
|
+
groupEnabled: dynamicAgents?.groupEnabled ?? true,
|
|
18
|
+
adminUsers: dynamicAgents?.adminUsers ?? [],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function sanitizeDynamicIdPart(value) {
|
|
22
|
+
return String(value)
|
|
23
|
+
.trim()
|
|
24
|
+
.toLowerCase()
|
|
25
|
+
.replace(/[^a-z0-9_-]/g, "_");
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* **generateAgentId (生成动态 Agent ID)**
|
|
29
|
+
*
|
|
30
|
+
* 根据账号 + 聊天类型 + 对端 ID 生成确定性的 Agent ID,避免多账号串会话。
|
|
31
|
+
* 格式: wecom-{accountId}-{type}-{sanitizedPeerId}
|
|
32
|
+
*/
|
|
33
|
+
export function generateAgentId(chatType, peerId, accountId) {
|
|
34
|
+
const sanitizedPeer = sanitizeDynamicIdPart(peerId) || "unknown";
|
|
35
|
+
const sanitizedAccountId = sanitizeDynamicIdPart(accountId ?? "default") || "default";
|
|
36
|
+
return `wecom-${sanitizedAccountId}-${chatType}-${sanitizedPeer}`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* **shouldUseDynamicAgent (检查是否使用动态 Agent)**
|
|
40
|
+
*
|
|
41
|
+
* 根据配置和发送者信息判断是否应使用动态 Agent。
|
|
42
|
+
* 管理员(adminUsers)始终绕过动态路由,使用主 Agent。
|
|
43
|
+
*/
|
|
44
|
+
export function shouldUseDynamicAgent(params) {
|
|
45
|
+
const { chatType, senderId, config } = params;
|
|
46
|
+
const dynamicConfig = getDynamicAgentConfig(config);
|
|
47
|
+
if (!dynamicConfig.enabled) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
// 管理员绕过动态路由
|
|
51
|
+
const sender = String(senderId).trim().toLowerCase();
|
|
52
|
+
const isAdmin = dynamicConfig.adminUsers.some((admin) => admin.trim().toLowerCase() === sender);
|
|
53
|
+
if (isAdmin) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
if (chatType === "group") {
|
|
57
|
+
return dynamicConfig.groupEnabled;
|
|
58
|
+
}
|
|
59
|
+
return dynamicConfig.dmCreateAgent;
|
|
60
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 动态路由统一处理模块
|
|
3
|
+
* 提供统一的路由注入接口。
|
|
4
|
+
*/
|
|
5
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
|
|
6
|
+
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
7
|
+
/**
|
|
8
|
+
* 路由对象(从 core.channel.routing.resolveAgentRoute 返回)
|
|
9
|
+
*/
|
|
10
|
+
export interface AgentRoute {
|
|
11
|
+
agentId: string;
|
|
12
|
+
sessionKey: string;
|
|
13
|
+
matchedBy: string;
|
|
14
|
+
accountId: string;
|
|
15
|
+
mainSessionKey?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 动态路由处理参数
|
|
19
|
+
*/
|
|
20
|
+
export interface DynamicRoutingParams {
|
|
21
|
+
/** 原始路由对象 */
|
|
22
|
+
route: AgentRoute;
|
|
23
|
+
/** 全局配置 */
|
|
24
|
+
config: OpenClawConfig;
|
|
25
|
+
/** 插件运行时 */
|
|
26
|
+
core: PluginRuntime;
|
|
27
|
+
/** 账号 ID */
|
|
28
|
+
accountId: string;
|
|
29
|
+
/** 会话类型 */
|
|
30
|
+
chatType: "group" | "dm";
|
|
31
|
+
/** 对话 ID(群聊为 chatId,私聊为 userId) */
|
|
32
|
+
chatId: string;
|
|
33
|
+
/** 发送者用户 ID */
|
|
34
|
+
senderId: string;
|
|
35
|
+
/** 日志输出函数(可选) */
|
|
36
|
+
log?: (msg: string) => void;
|
|
37
|
+
/** 错误日志输出函数(可选) */
|
|
38
|
+
error?: (msg: string) => void;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 动态路由处理结果
|
|
42
|
+
*/
|
|
43
|
+
export interface DynamicRoutingResult {
|
|
44
|
+
/** 是否使用动态 Agent */
|
|
45
|
+
useDynamicAgent: boolean;
|
|
46
|
+
/** 最终的 agentId(可能被动态注入修改) */
|
|
47
|
+
finalAgentId: string;
|
|
48
|
+
/** 最终的 sessionKey(可能被动态注入修改) */
|
|
49
|
+
finalSessionKey: string;
|
|
50
|
+
/** 是否修改了路由(注入了动态 Agent) */
|
|
51
|
+
routeModified: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 统一处理动态路由注入逻辑
|
|
55
|
+
*
|
|
56
|
+
* 功能:
|
|
57
|
+
* 1. 判断是否需要使用动态 Agent
|
|
58
|
+
* 2. 根据 matchedBy 判断配置类型
|
|
59
|
+
* 3. 返回最终的路由信息(不修改传入的 route 对象)
|
|
60
|
+
* 4. 输出详细的调试日志
|
|
61
|
+
*
|
|
62
|
+
* @param params 动态路由处理参数
|
|
63
|
+
* @returns 处理结果
|
|
64
|
+
*/
|
|
65
|
+
export declare function processDynamicRouting(params: DynamicRoutingParams): DynamicRoutingResult;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 动态路由统一处理模块
|
|
3
|
+
* 提供统一的路由注入接口。
|
|
4
|
+
*/
|
|
5
|
+
import { shouldUseDynamicAgent, generateAgentId } from "./dynamic-agent.js";
|
|
6
|
+
/**
|
|
7
|
+
* 统一处理动态路由注入逻辑
|
|
8
|
+
*
|
|
9
|
+
* 功能:
|
|
10
|
+
* 1. 判断是否需要使用动态 Agent
|
|
11
|
+
* 2. 根据 matchedBy 判断配置类型
|
|
12
|
+
* 3. 返回最终的路由信息(不修改传入的 route 对象)
|
|
13
|
+
* 4. 输出详细的调试日志
|
|
14
|
+
*
|
|
15
|
+
* @param params 动态路由处理参数
|
|
16
|
+
* @returns 处理结果
|
|
17
|
+
*/
|
|
18
|
+
export function processDynamicRouting(params) {
|
|
19
|
+
const { route, config, accountId, chatType, chatId, senderId, log } = params;
|
|
20
|
+
log?.(`[dynamic-routing] 🔍 调试 - matchedBy=${route.matchedBy}, agentId=${route.agentId}`);
|
|
21
|
+
if (route.matchedBy !== "default") {
|
|
22
|
+
log?.(`[dynamic-routing] ℹ️ 检测到匹配的 bindings (matchedBy=${route.matchedBy}),跳过动态路由`);
|
|
23
|
+
return {
|
|
24
|
+
useDynamicAgent: false,
|
|
25
|
+
finalAgentId: route.agentId,
|
|
26
|
+
finalSessionKey: route.sessionKey,
|
|
27
|
+
routeModified: false,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// 判断是否使用动态 Agent
|
|
31
|
+
const useDynamicAgent = shouldUseDynamicAgent({
|
|
32
|
+
chatType,
|
|
33
|
+
senderId,
|
|
34
|
+
config,
|
|
35
|
+
});
|
|
36
|
+
log?.(`[dynamic-routing] 是否使用动态路由: useDynamicAgent=${useDynamicAgent}`);
|
|
37
|
+
// 使用动态 Agent
|
|
38
|
+
if (useDynamicAgent) {
|
|
39
|
+
log?.(`[dynamic-routing] 原始路由信息: agentId=${route.agentId}, matchedBy=${route.matchedBy}, sessionKey=${route.sessionKey}`);
|
|
40
|
+
const targetAgentId = generateAgentId(chatType, chatId, accountId);
|
|
41
|
+
const targetSessionKey = `agent:${targetAgentId}:wecom:${accountId}:${chatType}:${chatId}`;
|
|
42
|
+
log?.(`[dynamic-routing] 🔄 路由注入: agentId=${targetAgentId}, sessionKey=${targetSessionKey}`);
|
|
43
|
+
return {
|
|
44
|
+
useDynamicAgent: true,
|
|
45
|
+
finalAgentId: targetAgentId,
|
|
46
|
+
finalSessionKey: targetSessionKey,
|
|
47
|
+
routeModified: true,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
log?.('[dynamic-routing] 🔄不使用动态路由');
|
|
51
|
+
// 不使用动态 Agent,返回原始路由
|
|
52
|
+
return {
|
|
53
|
+
useDynamicAgent: false,
|
|
54
|
+
finalAgentId: route.agentId,
|
|
55
|
+
finalSessionKey: route.sessionKey,
|
|
56
|
+
routeModified: false,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { CHANNEL_ID } from './const.js';
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* 企业微信群组访问控制模块
|
|
5
3
|
*
|
|
6
4
|
* 负责群组策略检查(groupPolicy、群组白名单、群内发送者白名单)
|
|
7
5
|
*/
|
|
6
|
+
import { CHANNEL_ID } from "./const.js";
|
|
8
7
|
// ============================================================================
|
|
9
8
|
// 内部辅助函数
|
|
10
9
|
// ============================================================================
|
|
@@ -76,22 +75,12 @@ function isGroupSenderAllowed(params) {
|
|
|
76
75
|
* 检查群组策略访问控制
|
|
77
76
|
* @returns 检查结果,包含是否允许继续处理
|
|
78
77
|
*/
|
|
79
|
-
function checkGroupPolicy(params) {
|
|
80
|
-
const { chatId, senderId, account,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
// providerConfigPresent: config.channels?.[CHANNEL_ID] !== undefined,
|
|
86
|
-
// groupPolicy: wecomConfig.groupPolicy,
|
|
87
|
-
// defaultGroupPolicy,
|
|
88
|
-
// });
|
|
89
|
-
// warnMissingProviderGroupPolicyFallbackOnce({
|
|
90
|
-
// providerMissingFallbackApplied,
|
|
91
|
-
// providerKey: CHANNEL_ID,
|
|
92
|
-
// accountId: account.accountId,
|
|
93
|
-
// log: (msg) => runtime.log?.(msg),
|
|
94
|
-
// });
|
|
78
|
+
export function checkGroupPolicy(params) {
|
|
79
|
+
const { chatId, senderId, account, runtime } = params;
|
|
80
|
+
// 使用 account.config(已经过多账号合并),而非顶层 config.channels.wecom
|
|
81
|
+
// 避免多账户模式下 groupAllowFrom / groups 等字段取不到账号级配置
|
|
82
|
+
const wecomConfig = account.config;
|
|
83
|
+
const groupPolicy = wecomConfig.groupPolicy ?? "open";
|
|
95
84
|
const groupAllowFrom = wecomConfig.groupAllowFrom ?? [];
|
|
96
85
|
const groupAllowed = isWeComGroupAllowed({
|
|
97
86
|
groupPolicy,
|
|
@@ -116,7 +105,7 @@ function checkGroupPolicy(params) {
|
|
|
116
105
|
/**
|
|
117
106
|
* 检查发送者是否在允许列表中(通用)
|
|
118
107
|
*/
|
|
119
|
-
function isSenderAllowed(senderId, allowFrom) {
|
|
108
|
+
export function isSenderAllowed(senderId, allowFrom) {
|
|
120
109
|
if (allowFrom.includes("*")) {
|
|
121
110
|
return true;
|
|
122
111
|
}
|
|
@@ -125,6 +114,3 @@ function isSenderAllowed(senderId, allowFrom) {
|
|
|
125
114
|
return normalized === senderId || normalized === `user:${senderId}`;
|
|
126
115
|
});
|
|
127
116
|
}
|
|
128
|
-
|
|
129
|
-
export { checkGroupPolicy, isSenderAllowed };
|
|
130
|
-
//# sourceMappingURL=group-policy.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
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>;
|
package/dist/src/http.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { ProxyAgent, fetch as undiciFetch } from "undici";
|
|
2
|
+
const proxyDispatchers = new Map();
|
|
3
|
+
/**
|
|
4
|
+
* **getProxyDispatcher (获取代理 Dispatcher)**
|
|
5
|
+
*
|
|
6
|
+
* 缓存并复用 ProxyAgent,避免重复创建连接池。
|
|
7
|
+
*/
|
|
8
|
+
function getProxyDispatcher(proxyUrl) {
|
|
9
|
+
const existing = proxyDispatchers.get(proxyUrl);
|
|
10
|
+
if (existing)
|
|
11
|
+
return existing;
|
|
12
|
+
const created = new ProxyAgent(proxyUrl);
|
|
13
|
+
proxyDispatchers.set(proxyUrl, created);
|
|
14
|
+
return created;
|
|
15
|
+
}
|
|
16
|
+
function mergeAbortSignal(params) {
|
|
17
|
+
const signals = [];
|
|
18
|
+
if (params.signal)
|
|
19
|
+
signals.push(params.signal);
|
|
20
|
+
if (params.timeoutMs && Number.isFinite(params.timeoutMs) && params.timeoutMs > 0) {
|
|
21
|
+
signals.push(AbortSignal.timeout(params.timeoutMs));
|
|
22
|
+
}
|
|
23
|
+
if (!signals.length)
|
|
24
|
+
return undefined;
|
|
25
|
+
if (signals.length === 1)
|
|
26
|
+
return signals[0];
|
|
27
|
+
return AbortSignal.any(signals);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* **wecomFetch (统一 HTTP 请求)**
|
|
31
|
+
*
|
|
32
|
+
* 基于 `undici` 的 fetch 封装,自动处理 ProxyAgent 和 Timeout。
|
|
33
|
+
* 所有对企业微信 API 的调用都应经过此函数。
|
|
34
|
+
*/
|
|
35
|
+
export async function wecomFetch(input, init, opts) {
|
|
36
|
+
const proxyUrl = opts?.proxyUrl?.trim() ?? "";
|
|
37
|
+
const dispatcher = proxyUrl ? getProxyDispatcher(proxyUrl) : undefined;
|
|
38
|
+
const initSignal = init?.signal ?? undefined;
|
|
39
|
+
const signal = mergeAbortSignal({ signal: opts?.signal ?? initSignal, timeoutMs: opts?.timeoutMs });
|
|
40
|
+
const headers = new Headers(init?.headers ?? {});
|
|
41
|
+
if (!headers.has("User-Agent")) {
|
|
42
|
+
headers.set("User-Agent", "OpenClaw/2.0 (WeCom-Agent)");
|
|
43
|
+
}
|
|
44
|
+
const nextInit = {
|
|
45
|
+
...(init ?? {}),
|
|
46
|
+
...(signal ? { signal } : {}),
|
|
47
|
+
...(dispatcher ? { dispatcher } : {}),
|
|
48
|
+
headers,
|
|
49
|
+
};
|
|
50
|
+
try {
|
|
51
|
+
return await undiciFetch(input, nextInit);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
if (err instanceof Error && err.name === "TypeError" && err.message === "fetch failed") {
|
|
55
|
+
const cause = err.cause;
|
|
56
|
+
console.error(`[wecom-http] fetch failed: ${input} (proxy: ${proxyUrl || "none"})${cause ? ` - cause: ${String(cause)}` : ""}`);
|
|
57
|
+
}
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* **readResponseBodyAsBuffer (读取响应 Body)**
|
|
63
|
+
*
|
|
64
|
+
* 将 Response Body 读取为 Buffer,支持最大字节限制以防止内存溢出。
|
|
65
|
+
* 适用于下载媒体文件等场景。
|
|
66
|
+
*/
|
|
67
|
+
export async function readResponseBodyAsBuffer(res, maxBytes) {
|
|
68
|
+
if (!res.body)
|
|
69
|
+
return Buffer.alloc(0);
|
|
70
|
+
const limit = maxBytes && Number.isFinite(maxBytes) && maxBytes > 0 ? maxBytes : undefined;
|
|
71
|
+
const chunks = [];
|
|
72
|
+
let total = 0;
|
|
73
|
+
const reader = res.body.getReader();
|
|
74
|
+
while (true) {
|
|
75
|
+
const { done, value } = await reader.read();
|
|
76
|
+
if (done)
|
|
77
|
+
break;
|
|
78
|
+
if (!value)
|
|
79
|
+
continue;
|
|
80
|
+
total += value.byteLength;
|
|
81
|
+
if (limit && total > limit) {
|
|
82
|
+
try {
|
|
83
|
+
await reader.cancel("body too large");
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// ignore
|
|
87
|
+
}
|
|
88
|
+
throw new Error(`response body too large (>${limit} bytes)`);
|
|
89
|
+
}
|
|
90
|
+
chunks.push(value);
|
|
91
|
+
}
|
|
92
|
+
return Buffer.concat(chunks.map((c) => Buffer.from(c)));
|
|
93
|
+
}
|