@wwlocal/aibot-plugin-node 20260409.20.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 +489 -0
- package/config.example.json +169 -0
- package/dist/cjs/index.js +76 -0
- package/dist/cjs/src/adapters/anthropic-adapter.js +534 -0
- package/dist/cjs/src/adapters/base-adapter.js +176 -0
- package/dist/cjs/src/adapters/deepseek-adapter.js +328 -0
- package/dist/cjs/src/adapters/dify-adapter.js +636 -0
- package/dist/cjs/src/adapters/index.js +131 -0
- package/dist/cjs/src/adapters/openai-adapter.js +361 -0
- package/dist/cjs/src/adapters/webhook-adapter.js +260 -0
- package/dist/cjs/src/agent-forwarder.js +87 -0
- package/dist/cjs/src/ca-cert.js +162 -0
- package/dist/cjs/src/config.js +169 -0
- package/dist/cjs/src/const.js +124 -0
- package/dist/cjs/src/conversation-manager.js +147 -0
- package/dist/cjs/src/dm-policy.js +46 -0
- package/dist/cjs/src/group-policy.js +95 -0
- package/dist/cjs/src/media-handler.js +136 -0
- package/dist/cjs/src/media-loader.js +271 -0
- package/dist/cjs/src/media-storage.js +165 -0
- package/dist/cjs/src/media-uploader.js +203 -0
- package/dist/cjs/src/message-parser.js +133 -0
- package/dist/cjs/src/message-sender.js +87 -0
- package/dist/cjs/src/monitor.js +849 -0
- package/dist/cjs/src/reqid-store.js +87 -0
- package/dist/cjs/src/server.js +72 -0
- package/dist/cjs/src/service-manager.js +135 -0
- package/dist/cjs/src/state-manager.js +143 -0
- package/dist/cjs/src/template-card-parser.js +498 -0
- package/dist/cjs/src/timeout.js +41 -0
- package/dist/cjs/src/version.js +25 -0
- package/dist/esm/index.js +74 -0
- package/dist/esm/src/adapters/anthropic-adapter.js +512 -0
- package/dist/esm/src/adapters/base-adapter.js +174 -0
- package/dist/esm/src/adapters/deepseek-adapter.js +326 -0
- package/dist/esm/src/adapters/dify-adapter.js +634 -0
- package/dist/esm/src/adapters/index.js +123 -0
- package/dist/esm/src/adapters/openai-adapter.js +339 -0
- package/dist/esm/src/adapters/webhook-adapter.js +258 -0
- package/dist/esm/src/agent-forwarder.js +84 -0
- package/dist/esm/src/ca-cert.js +136 -0
- package/dist/esm/src/config.js +145 -0
- package/dist/esm/src/const.js +100 -0
- package/dist/esm/src/conversation-manager.js +144 -0
- package/dist/esm/src/dm-policy.js +44 -0
- package/dist/esm/src/group-policy.js +92 -0
- package/dist/esm/src/media-handler.js +133 -0
- package/dist/esm/src/media-loader.js +246 -0
- package/dist/esm/src/media-storage.js +143 -0
- package/dist/esm/src/media-uploader.js +198 -0
- package/dist/esm/src/message-parser.js +131 -0
- package/dist/esm/src/message-sender.js +83 -0
- package/dist/esm/src/monitor.js +841 -0
- package/dist/esm/src/reqid-store.js +85 -0
- package/dist/esm/src/server.js +69 -0
- package/dist/esm/src/service-manager.js +133 -0
- package/dist/esm/src/state-manager.js +134 -0
- package/dist/esm/src/template-card-parser.js +495 -0
- package/dist/esm/src/timeout.js +38 -0
- package/dist/esm/src/version.js +22 -0
- package/dist/esm/types/index.d.ts +14 -0
- package/dist/esm/types/src/adapters/anthropic-adapter.d.ts +93 -0
- package/dist/esm/types/src/adapters/base-adapter.d.ts +76 -0
- package/dist/esm/types/src/adapters/deepseek-adapter.d.ts +87 -0
- package/dist/esm/types/src/adapters/dify-adapter.d.ts +100 -0
- package/dist/esm/types/src/adapters/index.d.ts +60 -0
- package/dist/esm/types/src/adapters/openai-adapter.d.ts +82 -0
- package/dist/esm/types/src/adapters/types.d.ts +373 -0
- package/dist/esm/types/src/adapters/webhook-adapter.d.ts +54 -0
- package/dist/esm/types/src/agent-forwarder.d.ts +32 -0
- package/dist/esm/types/src/ca-cert.d.ts +53 -0
- package/dist/esm/types/src/config.d.ts +29 -0
- package/dist/esm/types/src/const.d.ts +74 -0
- package/dist/esm/types/src/conversation-manager.d.ts +81 -0
- package/dist/esm/types/src/dm-policy.d.ts +27 -0
- package/dist/esm/types/src/group-policy.d.ts +28 -0
- package/dist/esm/types/src/interface.d.ts +332 -0
- package/dist/esm/types/src/media-handler.d.ts +36 -0
- package/dist/esm/types/src/media-loader.d.ts +47 -0
- package/dist/esm/types/src/media-storage.d.ts +35 -0
- package/dist/esm/types/src/media-uploader.d.ts +65 -0
- package/dist/esm/types/src/message-parser.d.ts +89 -0
- package/dist/esm/types/src/message-sender.d.ts +34 -0
- package/dist/esm/types/src/monitor.d.ts +30 -0
- package/dist/esm/types/src/reqid-store.d.ts +23 -0
- package/dist/esm/types/src/server.d.ts +23 -0
- package/dist/esm/types/src/service-manager.d.ts +52 -0
- package/dist/esm/types/src/state-manager.d.ts +76 -0
- package/dist/esm/types/src/template-card-parser.d.ts +18 -0
- package/dist/esm/types/src/timeout.d.ts +20 -0
- package/dist/esm/types/src/version.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/package.json +51 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DM(私聊)访问控制模块
|
|
3
|
+
*
|
|
4
|
+
* 仅保留 open/allowlist/disabled 三种策略,不包含 OpenClaw 的 pairing 机制
|
|
5
|
+
*/
|
|
6
|
+
import type { RuntimeLogger, ResolvedAccount } from "./interface.js";
|
|
7
|
+
import type { WSClient, WsFrame } from "@wecom/aibot-node-sdk";
|
|
8
|
+
export interface DmPolicyCheckResult {
|
|
9
|
+
/** 是否允许继续处理消息 */
|
|
10
|
+
allowed: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 检查 DM Policy 访问控制
|
|
14
|
+
*
|
|
15
|
+
* 策略说明:
|
|
16
|
+
* - open: 允许所有人私聊
|
|
17
|
+
* - allowlist: 仅允许白名单中的用户私聊
|
|
18
|
+
* - disabled: 禁用所有私聊
|
|
19
|
+
*/
|
|
20
|
+
export declare function checkDmPolicy(params: {
|
|
21
|
+
senderId: string;
|
|
22
|
+
isGroup: boolean;
|
|
23
|
+
account: ResolvedAccount;
|
|
24
|
+
wsClient: WSClient;
|
|
25
|
+
frame: WsFrame;
|
|
26
|
+
runtime: RuntimeLogger;
|
|
27
|
+
}): Promise<DmPolicyCheckResult>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 群组访问控制模块
|
|
3
|
+
*
|
|
4
|
+
* 负责群组策略检查(groupPolicy、群组白名单、群内发送者白名单)
|
|
5
|
+
* 使用独立配置类型,不依赖 OpenClaw
|
|
6
|
+
*/
|
|
7
|
+
import type { RuntimeLogger, ResolvedAccount } from "./interface.js";
|
|
8
|
+
export interface GroupPolicyCheckResult {
|
|
9
|
+
/** 是否允许继续处理消息 */
|
|
10
|
+
allowed: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 检查群组策略访问控制
|
|
14
|
+
*
|
|
15
|
+
* 两层检查:
|
|
16
|
+
* 1. 群组白名单(groupAllowFrom)
|
|
17
|
+
* 2. 每群发送者白名单(groups[chatId].allowFrom)
|
|
18
|
+
*/
|
|
19
|
+
export declare function checkGroupPolicy(params: {
|
|
20
|
+
chatId: string;
|
|
21
|
+
senderId: string;
|
|
22
|
+
account: ResolvedAccount;
|
|
23
|
+
runtime: RuntimeLogger;
|
|
24
|
+
}): GroupPolicyCheckResult;
|
|
25
|
+
/**
|
|
26
|
+
* 检查发送者是否在允许列表中(通用)
|
|
27
|
+
*/
|
|
28
|
+
export declare function isSenderAllowed(senderId: string, allowFrom: string[]): boolean;
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信私有部署 AI 机器人插件类型定义
|
|
3
|
+
*/
|
|
4
|
+
import { WeComCommand } from "./const.js";
|
|
5
|
+
/**
|
|
6
|
+
* 运行时日志接口
|
|
7
|
+
* 用于统一日志输出,替代 OpenClaw 的 RuntimeEnv
|
|
8
|
+
*/
|
|
9
|
+
export interface RuntimeLogger {
|
|
10
|
+
log?: (...args: any[]) => void;
|
|
11
|
+
error?: (...args: any[]) => void;
|
|
12
|
+
}
|
|
13
|
+
/** 智能体端点配置 */
|
|
14
|
+
export interface AgentEndpoint {
|
|
15
|
+
/** 端点名称(用于路由选择) */
|
|
16
|
+
name: string;
|
|
17
|
+
/** 适配器类型:openai / deepseek / anthropic / dify / webhook(默认 openai) */
|
|
18
|
+
provider?: string;
|
|
19
|
+
/** API URL (如 https://api.openai.com/v1/chat/completions) */
|
|
20
|
+
url: string;
|
|
21
|
+
/** API Key */
|
|
22
|
+
apiKey?: string;
|
|
23
|
+
/** 模型名称 */
|
|
24
|
+
model?: string;
|
|
25
|
+
/** 自定义请求头 */
|
|
26
|
+
headers?: Record<string, string>;
|
|
27
|
+
/** 是否启用流式 (默认 true) */
|
|
28
|
+
stream?: boolean;
|
|
29
|
+
/** 系统提示词 */
|
|
30
|
+
systemPrompt?: string;
|
|
31
|
+
/** 请求超时 (毫秒,默认 300000) */
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
/** 多轮对话最大保留轮数(0 = 禁用对话记忆,默认 20) */
|
|
34
|
+
maxHistoryRounds?: number;
|
|
35
|
+
/** 适配器专属扩展配置 */
|
|
36
|
+
providerOptions?: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
/** 企微账号配置 */
|
|
39
|
+
export interface AccountConfig {
|
|
40
|
+
/** 账号 ID(用于内部标识,默认为 botId) */
|
|
41
|
+
accountId?: string;
|
|
42
|
+
/** 账号名称(可选,用于日志显示) */
|
|
43
|
+
name?: string;
|
|
44
|
+
/** 是否启用(默认 true) */
|
|
45
|
+
enabled?: boolean;
|
|
46
|
+
/** WebSocket 连接地址 */
|
|
47
|
+
websocketUrl: string;
|
|
48
|
+
/** 机器人 ID */
|
|
49
|
+
botId: string;
|
|
50
|
+
/** 机器人密钥 */
|
|
51
|
+
secret: string;
|
|
52
|
+
/** CA 证书文件路径(PEM 格式,用于私有化部署自签名证书) */
|
|
53
|
+
caCert?: string;
|
|
54
|
+
/** 是否在智能体响应前发送"思考中"消息(默认 true) */
|
|
55
|
+
sendThinkingMessage?: boolean;
|
|
56
|
+
/** 私聊策略:open=全部允许, allowlist=白名单, disabled=禁用 */
|
|
57
|
+
dmPolicy?: "open" | "allowlist" | "disabled";
|
|
58
|
+
/** 私聊白名单(userid 列表,dmPolicy=allowlist 时生效) */
|
|
59
|
+
allowFrom?: string[];
|
|
60
|
+
/** 群组策略:open=全部允许, allowlist=白名单, disabled=禁用 */
|
|
61
|
+
groupPolicy?: "open" | "allowlist" | "disabled";
|
|
62
|
+
/** 群组白名单(chatid 列表,groupPolicy=allowlist 时生效) */
|
|
63
|
+
groupAllowFrom?: string[];
|
|
64
|
+
/** 每群发送者白名单 */
|
|
65
|
+
groups?: Record<string, {
|
|
66
|
+
allowFrom?: string[];
|
|
67
|
+
}>;
|
|
68
|
+
/** 允许读取本地媒体文件的根目录列表 */
|
|
69
|
+
mediaLocalRoots?: string[];
|
|
70
|
+
/** 指定使用的智能体端点名称(默认使用第一个) */
|
|
71
|
+
agentName?: string;
|
|
72
|
+
}
|
|
73
|
+
/** 插件配置 */
|
|
74
|
+
export interface PluginConfig {
|
|
75
|
+
/** HTTP 服务端口 (默认 3000) */
|
|
76
|
+
port?: number;
|
|
77
|
+
/** 数据存储目录(媒体文件等,默认 ./data) */
|
|
78
|
+
dataDir?: string;
|
|
79
|
+
/** 智能体端点列表 */
|
|
80
|
+
agents: AgentEndpoint[];
|
|
81
|
+
/** 企微账号列表 */
|
|
82
|
+
accounts: AccountConfig[];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Monitor 配置选项
|
|
86
|
+
*/
|
|
87
|
+
export interface MonitorOptions {
|
|
88
|
+
/** 企微账号配置(已解析) */
|
|
89
|
+
account: ResolvedAccount;
|
|
90
|
+
/** 插件配置 */
|
|
91
|
+
config: PluginConfig;
|
|
92
|
+
/** 运行时日志 */
|
|
93
|
+
runtime: RuntimeLogger;
|
|
94
|
+
/** 中止信号 */
|
|
95
|
+
abortSignal?: AbortSignal;
|
|
96
|
+
/** 状态更新回调 */
|
|
97
|
+
setStatus?: (next: Record<string, unknown>) => void;
|
|
98
|
+
}
|
|
99
|
+
/** 已解析的账号配置(带默认值和智能体端点引用) */
|
|
100
|
+
export interface ResolvedAccount extends Required<Pick<AccountConfig, "accountId" | "websocketUrl" | "botId" | "secret">> {
|
|
101
|
+
name: string;
|
|
102
|
+
enabled: boolean;
|
|
103
|
+
caCert?: string;
|
|
104
|
+
sendThinkingMessage: boolean;
|
|
105
|
+
dmPolicy: "open" | "allowlist" | "disabled";
|
|
106
|
+
allowFrom: string[];
|
|
107
|
+
groupPolicy: "open" | "allowlist" | "disabled";
|
|
108
|
+
groupAllowFrom: string[];
|
|
109
|
+
groups: Record<string, {
|
|
110
|
+
allowFrom?: string[];
|
|
111
|
+
}>;
|
|
112
|
+
mediaLocalRoots: string[];
|
|
113
|
+
/** 解析后的智能体端点 */
|
|
114
|
+
agent: AgentEndpoint;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* 消息状态
|
|
118
|
+
*/
|
|
119
|
+
export interface MessageState {
|
|
120
|
+
accumulatedText: string;
|
|
121
|
+
/** 流式回复的 streamId */
|
|
122
|
+
streamId?: string;
|
|
123
|
+
/** 是否已成功发送过媒体文件 */
|
|
124
|
+
hasMedia?: boolean;
|
|
125
|
+
/** 是否有媒体发送失败 */
|
|
126
|
+
hasMediaFailed?: boolean;
|
|
127
|
+
/** 媒体发送失败时的纯文本错误摘要 */
|
|
128
|
+
mediaErrorSummary?: string;
|
|
129
|
+
/** deliver 回调是否被调用过 */
|
|
130
|
+
deliverCalled?: boolean;
|
|
131
|
+
/** 流式回复是否已过期(errcode 846608) */
|
|
132
|
+
streamExpired?: boolean;
|
|
133
|
+
/** 是否已成功发送过模板卡片 */
|
|
134
|
+
hasTemplateCard?: boolean;
|
|
135
|
+
}
|
|
136
|
+
/** 从文本中提取的模板卡片 */
|
|
137
|
+
export interface ExtractedTemplateCard {
|
|
138
|
+
/** 原始 JSON 对象(已验证 card_type 合法) */
|
|
139
|
+
cardJson: Record<string, unknown>;
|
|
140
|
+
/** card_type 值 */
|
|
141
|
+
cardType: string;
|
|
142
|
+
}
|
|
143
|
+
/** extractTemplateCards 返回值 */
|
|
144
|
+
export interface TemplateCardExtractionResult {
|
|
145
|
+
/** 提取到的合法模板卡片列表 */
|
|
146
|
+
cards: ExtractedTemplateCard[];
|
|
147
|
+
/** 移除卡片代码块后的剩余文本 */
|
|
148
|
+
remainingText: string;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* WebSocket 请求消息基础格式
|
|
152
|
+
*/
|
|
153
|
+
export interface WeComRequest {
|
|
154
|
+
cmd: string;
|
|
155
|
+
headers: {
|
|
156
|
+
req_id: string;
|
|
157
|
+
};
|
|
158
|
+
body: any;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* WebSocket 响应消息格式
|
|
162
|
+
*/
|
|
163
|
+
export interface WeComResponse {
|
|
164
|
+
headers: {
|
|
165
|
+
req_id: string;
|
|
166
|
+
};
|
|
167
|
+
errcode: number;
|
|
168
|
+
errmsg: string;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* 企业微信私有部署认证请求
|
|
172
|
+
*/
|
|
173
|
+
export interface WeComSubscribeRequest extends WeComRequest {
|
|
174
|
+
cmd: WeComCommand.SUBSCRIBE;
|
|
175
|
+
body: {
|
|
176
|
+
secret: string;
|
|
177
|
+
bot_id: string;
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* 企业微信私有部署推送消息格式
|
|
182
|
+
*/
|
|
183
|
+
export interface WeComCallbackMessage {
|
|
184
|
+
cmd: WeComCommand.AIBOT_CALLBACK | "aibot_event_callback";
|
|
185
|
+
headers: {
|
|
186
|
+
req_id: string;
|
|
187
|
+
};
|
|
188
|
+
body: {
|
|
189
|
+
msgid: string;
|
|
190
|
+
aibotid: string;
|
|
191
|
+
chatid?: string;
|
|
192
|
+
chattype: "single" | "group";
|
|
193
|
+
from: {
|
|
194
|
+
userid: string;
|
|
195
|
+
};
|
|
196
|
+
response_url: string;
|
|
197
|
+
msgtype: "text" | "image" | "voice" | "video" | "file" | "stream" | "mixed" | "event";
|
|
198
|
+
text?: {
|
|
199
|
+
content: string;
|
|
200
|
+
};
|
|
201
|
+
image?: {
|
|
202
|
+
url?: string;
|
|
203
|
+
base64?: string;
|
|
204
|
+
md5?: string;
|
|
205
|
+
};
|
|
206
|
+
mixed?: {
|
|
207
|
+
msg_item: Array<{
|
|
208
|
+
msgtype: "text" | "image";
|
|
209
|
+
text?: {
|
|
210
|
+
content: string;
|
|
211
|
+
};
|
|
212
|
+
image?: {
|
|
213
|
+
url?: string;
|
|
214
|
+
base64?: string;
|
|
215
|
+
md5?: string;
|
|
216
|
+
};
|
|
217
|
+
}>;
|
|
218
|
+
};
|
|
219
|
+
quote?: {
|
|
220
|
+
msgtype: string;
|
|
221
|
+
text?: {
|
|
222
|
+
content: string;
|
|
223
|
+
};
|
|
224
|
+
image?: {
|
|
225
|
+
url?: string;
|
|
226
|
+
aeskey?: string;
|
|
227
|
+
};
|
|
228
|
+
file?: {
|
|
229
|
+
url?: string;
|
|
230
|
+
aeskey?: string;
|
|
231
|
+
};
|
|
232
|
+
};
|
|
233
|
+
stream?: {
|
|
234
|
+
id: string;
|
|
235
|
+
};
|
|
236
|
+
event?: {
|
|
237
|
+
eventtype: string;
|
|
238
|
+
template_card_event?: {
|
|
239
|
+
card_type?: string;
|
|
240
|
+
event_key?: string;
|
|
241
|
+
task_id?: string;
|
|
242
|
+
selected_items?: {
|
|
243
|
+
selected_item?: Array<{
|
|
244
|
+
question_key?: string;
|
|
245
|
+
option_ids?: {
|
|
246
|
+
option_id?: string[];
|
|
247
|
+
};
|
|
248
|
+
}>;
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
};
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* 企业微信私有部署响应消息格式
|
|
256
|
+
*/
|
|
257
|
+
export interface WeComResponseMessage extends WeComRequest {
|
|
258
|
+
cmd: WeComCommand.AIBOT_RESPONSE;
|
|
259
|
+
body: {
|
|
260
|
+
msgtype: "stream" | "text" | "markdown";
|
|
261
|
+
stream?: {
|
|
262
|
+
id: string;
|
|
263
|
+
finish: boolean;
|
|
264
|
+
content: string;
|
|
265
|
+
msg_item?: Array<{
|
|
266
|
+
msgtype: "image" | "file";
|
|
267
|
+
image?: {
|
|
268
|
+
base64: string;
|
|
269
|
+
md5: string;
|
|
270
|
+
};
|
|
271
|
+
}>;
|
|
272
|
+
feedback?: {
|
|
273
|
+
id: string;
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
text?: {
|
|
277
|
+
content: string;
|
|
278
|
+
};
|
|
279
|
+
markdown?: {
|
|
280
|
+
content: string;
|
|
281
|
+
};
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
/** deliver 回调参数 */
|
|
285
|
+
export interface DeliverPayload {
|
|
286
|
+
text?: string;
|
|
287
|
+
mediaUrl?: string;
|
|
288
|
+
mediaUrls?: string[];
|
|
289
|
+
}
|
|
290
|
+
/** deliver 回调信息 */
|
|
291
|
+
export interface DeliverInfo {
|
|
292
|
+
kind: "block" | "final";
|
|
293
|
+
}
|
|
294
|
+
/** 智能体转发选项 */
|
|
295
|
+
export interface ForwardOptions {
|
|
296
|
+
/** 消息文本 */
|
|
297
|
+
text: string;
|
|
298
|
+
/** 媒体文件路径列表 */
|
|
299
|
+
mediaPaths?: Array<{
|
|
300
|
+
path: string;
|
|
301
|
+
contentType?: string;
|
|
302
|
+
}>;
|
|
303
|
+
/** 引用内容 */
|
|
304
|
+
quoteContent?: string;
|
|
305
|
+
/** 智能体端点配置 */
|
|
306
|
+
agent: AgentEndpoint;
|
|
307
|
+
/** deliver 回调 */
|
|
308
|
+
deliver: (payload: DeliverPayload, info: DeliverInfo) => Promise<void>;
|
|
309
|
+
/** 回复开始回调 */
|
|
310
|
+
onReplyStart?: () => Promise<void>;
|
|
311
|
+
/** 错误回调 */
|
|
312
|
+
onError?: (err: Error, info: {
|
|
313
|
+
kind: string;
|
|
314
|
+
}) => void;
|
|
315
|
+
/** 中止信号 */
|
|
316
|
+
abortSignal?: AbortSignal;
|
|
317
|
+
/** 运行时日志 */
|
|
318
|
+
runtime?: RuntimeLogger;
|
|
319
|
+
/** 扩展上下文(适配器可按需使用,如 Dify 的会话管理) */
|
|
320
|
+
context?: {
|
|
321
|
+
/** 账号 ID(用于会话隔离,防止多账号历史串联) */
|
|
322
|
+
accountId?: string;
|
|
323
|
+
/** 企微用户 ID */
|
|
324
|
+
userId?: string;
|
|
325
|
+
/** 企微聊天 ID */
|
|
326
|
+
chatId?: string;
|
|
327
|
+
/** 聊天类型 */
|
|
328
|
+
chatType?: "single" | "group";
|
|
329
|
+
/** 其他自定义字段 */
|
|
330
|
+
[key: string]: unknown;
|
|
331
|
+
};
|
|
332
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 入站媒体(图片/文件)下载和保存模块
|
|
3
|
+
*
|
|
4
|
+
* 负责下载、检测格式、保存媒体到本地文件系统
|
|
5
|
+
* 替代 OpenClaw 的 core.channel.media.saveMediaBuffer 和 fetchRemoteMedia
|
|
6
|
+
*/
|
|
7
|
+
import type { WSClient } from "@wecom/aibot-node-sdk";
|
|
8
|
+
import type { RuntimeLogger, ResolvedAccount, PluginConfig } from "./interface.js";
|
|
9
|
+
/**
|
|
10
|
+
* 下载并保存所有图片到本地
|
|
11
|
+
*/
|
|
12
|
+
export declare function downloadAndSaveImages(params: {
|
|
13
|
+
imageUrls: string[];
|
|
14
|
+
imageAesKeys?: Map<string, string>;
|
|
15
|
+
account: ResolvedAccount;
|
|
16
|
+
config: PluginConfig;
|
|
17
|
+
runtime: RuntimeLogger;
|
|
18
|
+
wsClient: WSClient;
|
|
19
|
+
}): Promise<Array<{
|
|
20
|
+
path: string;
|
|
21
|
+
contentType?: string;
|
|
22
|
+
}>>;
|
|
23
|
+
/**
|
|
24
|
+
* 下载并保存所有文件到本地
|
|
25
|
+
*/
|
|
26
|
+
export declare function downloadAndSaveFiles(params: {
|
|
27
|
+
fileUrls: string[];
|
|
28
|
+
fileAesKeys?: Map<string, string>;
|
|
29
|
+
account: ResolvedAccount;
|
|
30
|
+
config: PluginConfig;
|
|
31
|
+
runtime: RuntimeLogger;
|
|
32
|
+
wsClient: WSClient;
|
|
33
|
+
}): Promise<Array<{
|
|
34
|
+
path: string;
|
|
35
|
+
contentType?: string;
|
|
36
|
+
}>>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 出站媒体文件加载器
|
|
3
|
+
*
|
|
4
|
+
* 内联 openclaw-compat.ts 的 fallback 实现:
|
|
5
|
+
* - loadOutboundMediaFromUrl(支持远程 URL 和本地文件)
|
|
6
|
+
* - detectMime(buffer 嗅探 + 扩展名映射)
|
|
7
|
+
* - assertLocalMediaAllowed(安全白名单检查)
|
|
8
|
+
*
|
|
9
|
+
* 不再有 OpenClaw SDK 探测逻辑,直接使用独立实现。
|
|
10
|
+
*/
|
|
11
|
+
/** 媒体加载结果 */
|
|
12
|
+
export type WebMediaResult = {
|
|
13
|
+
buffer: Buffer;
|
|
14
|
+
contentType?: string;
|
|
15
|
+
kind?: string;
|
|
16
|
+
fileName?: string;
|
|
17
|
+
};
|
|
18
|
+
export type OutboundMediaLoadOptions = {
|
|
19
|
+
maxBytes?: number;
|
|
20
|
+
mediaLocalRoots?: readonly string[];
|
|
21
|
+
};
|
|
22
|
+
export type DetectMimeOptions = {
|
|
23
|
+
buffer?: Buffer;
|
|
24
|
+
headerMime?: string | null;
|
|
25
|
+
filePath?: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* 检测 MIME 类型
|
|
29
|
+
*
|
|
30
|
+
* 支持两种调用签名:
|
|
31
|
+
* - detectMime(buffer) → 仅 buffer 嗅探
|
|
32
|
+
* - detectMime({ buffer, headerMime, filePath }) → 完整参数
|
|
33
|
+
*/
|
|
34
|
+
export declare function detectMime(bufferOrOpts: Buffer | DetectMimeOptions): Promise<string | undefined>;
|
|
35
|
+
/**
|
|
36
|
+
* 校验本地媒体文件路径是否在允许的根目录列表内
|
|
37
|
+
*/
|
|
38
|
+
export declare function assertLocalMediaAllowed(mediaPath: string, localRoots: readonly string[] | undefined): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* 从 URL 或本地路径加载媒体文件
|
|
41
|
+
*
|
|
42
|
+
* 支持:
|
|
43
|
+
* - 远程 URL (http:// / https://)
|
|
44
|
+
* - 本地文件路径 (file:// 或绝对路径)
|
|
45
|
+
* - ~ 路径展开
|
|
46
|
+
*/
|
|
47
|
+
export declare function loadOutboundMediaFromUrl(mediaUrl: string, options?: OutboundMediaLoadOptions): Promise<WebMediaResult>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 本地文件媒体存储模块
|
|
3
|
+
*
|
|
4
|
+
* 替代 OpenClaw 的 core.channel.media.saveMediaBuffer
|
|
5
|
+
* 将媒体文件存储到本地文件系统,按日期/方向/类型组织目录
|
|
6
|
+
*/
|
|
7
|
+
export interface SaveMediaResult {
|
|
8
|
+
/** 存储的文件路径 */
|
|
9
|
+
path: string;
|
|
10
|
+
/** 检测到的内容类型 */
|
|
11
|
+
contentType: string;
|
|
12
|
+
/** 文件名 */
|
|
13
|
+
fileName: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 将媒体文件保存到本地文件系统
|
|
17
|
+
*
|
|
18
|
+
* 存储路径: {dataDir}/media/{direction}/{YYYY-MM-DD}/{filename}
|
|
19
|
+
*
|
|
20
|
+
* @param buffer - 文件数据
|
|
21
|
+
* @param contentType - 内容类型(如 image/jpeg)
|
|
22
|
+
* @param direction - 方向:inbound(入站)或 outbound(出站)
|
|
23
|
+
* @param maxBytes - 最大字节数限制
|
|
24
|
+
* @param originalFilename - 原始文件名(可选)
|
|
25
|
+
* @param dataDir - 数据目录路径
|
|
26
|
+
* @returns 保存结果
|
|
27
|
+
*/
|
|
28
|
+
export declare function saveMediaBuffer(buffer: Buffer, contentType: string, direction: "inbound" | "outbound", maxBytes: number, originalFilename: string | undefined, dataDir: string): Promise<SaveMediaResult>;
|
|
29
|
+
/**
|
|
30
|
+
* 从本地路径加载媒体文件
|
|
31
|
+
*
|
|
32
|
+
* @param filePath - 文件路径
|
|
33
|
+
* @returns 文件 buffer
|
|
34
|
+
*/
|
|
35
|
+
export declare function loadLocalMedia(filePath: string): Buffer;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 出站媒体上传工具模块
|
|
3
|
+
*
|
|
4
|
+
* 负责:
|
|
5
|
+
* - 从 mediaUrl 加载文件 buffer(远程 URL 或本地路径均支持)
|
|
6
|
+
* - 检测 MIME 类型并映射为企微媒体类型
|
|
7
|
+
* - 文件大小检查与降级策略
|
|
8
|
+
* - 分片上传 + 发送
|
|
9
|
+
*/
|
|
10
|
+
import type { WeComMediaType, WSClient, WsFrameHeaders } from "@wecom/aibot-node-sdk";
|
|
11
|
+
/** 媒体文件解析结果 */
|
|
12
|
+
export interface ResolvedMedia {
|
|
13
|
+
buffer: Buffer;
|
|
14
|
+
contentType: string;
|
|
15
|
+
fileName: string;
|
|
16
|
+
}
|
|
17
|
+
/** 文件大小检查结果 */
|
|
18
|
+
export interface FileSizeCheckResult {
|
|
19
|
+
finalType: WeComMediaType;
|
|
20
|
+
shouldReject: boolean;
|
|
21
|
+
rejectReason?: string;
|
|
22
|
+
downgraded: boolean;
|
|
23
|
+
downgradeNote?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function detectWeComMediaType(mimeType: string): WeComMediaType;
|
|
26
|
+
export declare function resolveMediaFile(mediaUrl: string, mediaLocalRoots?: readonly string[]): Promise<ResolvedMedia>;
|
|
27
|
+
export declare function applyFileSizeLimits(fileSize: number, detectedType: WeComMediaType, contentType?: string): FileSizeCheckResult;
|
|
28
|
+
export interface UploadAndSendMediaOptions {
|
|
29
|
+
wsClient: WSClient;
|
|
30
|
+
mediaUrl: string;
|
|
31
|
+
chatId: string;
|
|
32
|
+
mediaLocalRoots?: readonly string[];
|
|
33
|
+
log?: (...args: any[]) => void;
|
|
34
|
+
errorLog?: (...args: any[]) => void;
|
|
35
|
+
}
|
|
36
|
+
export interface UploadAndSendMediaResult {
|
|
37
|
+
ok: boolean;
|
|
38
|
+
messageId?: string;
|
|
39
|
+
finalType?: WeComMediaType;
|
|
40
|
+
rejected?: boolean;
|
|
41
|
+
rejectReason?: string;
|
|
42
|
+
downgraded?: boolean;
|
|
43
|
+
downgradeNote?: string;
|
|
44
|
+
error?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 公共媒体上传+发送流程
|
|
48
|
+
*
|
|
49
|
+
* resolveMediaFile → detectType → sizeCheck → uploadMedia → sendMediaMessage
|
|
50
|
+
*/
|
|
51
|
+
export declare function uploadAndSendMedia(options: UploadAndSendMediaOptions): Promise<UploadAndSendMediaResult>;
|
|
52
|
+
export interface UploadAndReplyMediaOptions {
|
|
53
|
+
wsClient: WSClient;
|
|
54
|
+
mediaUrl: string;
|
|
55
|
+
frame: WsFrameHeaders;
|
|
56
|
+
mediaLocalRoots?: readonly string[];
|
|
57
|
+
log?: (...args: any[]) => void;
|
|
58
|
+
errorLog?: (...args: any[]) => void;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* 被动回复媒体上传+发送流程
|
|
62
|
+
*
|
|
63
|
+
* resolveMediaFile → detectType → sizeCheck → uploadMedia → replyMedia
|
|
64
|
+
*/
|
|
65
|
+
export declare function uploadAndReplyMedia(options: UploadAndReplyMediaOptions): Promise<UploadAndSendMediaResult>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信私有部署消息内容解析模块
|
|
3
|
+
*
|
|
4
|
+
* 负责从 WsFrame 中提取文本、图片、引用等内容
|
|
5
|
+
*/
|
|
6
|
+
export interface MessageBody {
|
|
7
|
+
msgid: string;
|
|
8
|
+
aibotid?: string;
|
|
9
|
+
chatid?: string;
|
|
10
|
+
chattype: "single" | "group";
|
|
11
|
+
from: {
|
|
12
|
+
corpid?: string;
|
|
13
|
+
userid: string;
|
|
14
|
+
};
|
|
15
|
+
response_url?: string;
|
|
16
|
+
msgtype: string;
|
|
17
|
+
text?: {
|
|
18
|
+
content: string;
|
|
19
|
+
};
|
|
20
|
+
image?: {
|
|
21
|
+
url?: string;
|
|
22
|
+
aeskey?: string;
|
|
23
|
+
};
|
|
24
|
+
voice?: {
|
|
25
|
+
content?: string;
|
|
26
|
+
};
|
|
27
|
+
mixed?: {
|
|
28
|
+
msg_item: Array<{
|
|
29
|
+
msgtype: "text" | "image";
|
|
30
|
+
text?: {
|
|
31
|
+
content: string;
|
|
32
|
+
};
|
|
33
|
+
image?: {
|
|
34
|
+
url?: string;
|
|
35
|
+
aeskey?: string;
|
|
36
|
+
};
|
|
37
|
+
}>;
|
|
38
|
+
};
|
|
39
|
+
file?: {
|
|
40
|
+
url?: string;
|
|
41
|
+
aeskey?: string;
|
|
42
|
+
};
|
|
43
|
+
quote?: {
|
|
44
|
+
msgtype: string;
|
|
45
|
+
text?: {
|
|
46
|
+
content: string;
|
|
47
|
+
};
|
|
48
|
+
voice?: {
|
|
49
|
+
content: string;
|
|
50
|
+
};
|
|
51
|
+
image?: {
|
|
52
|
+
url?: string;
|
|
53
|
+
aeskey?: string;
|
|
54
|
+
};
|
|
55
|
+
file?: {
|
|
56
|
+
url?: string;
|
|
57
|
+
aeskey?: string;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
event?: {
|
|
61
|
+
eventtype?: string;
|
|
62
|
+
template_card_event?: {
|
|
63
|
+
card_type?: string;
|
|
64
|
+
event_key?: string;
|
|
65
|
+
task_id?: string;
|
|
66
|
+
selected_items?: {
|
|
67
|
+
selected_item?: Array<{
|
|
68
|
+
question_key?: string;
|
|
69
|
+
option_ids?: {
|
|
70
|
+
option_id?: string[];
|
|
71
|
+
};
|
|
72
|
+
}>;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export interface ParsedMessageContent {
|
|
78
|
+
textParts: string[];
|
|
79
|
+
imageUrls: string[];
|
|
80
|
+
imageAesKeys: Map<string, string>;
|
|
81
|
+
fileUrls: string[];
|
|
82
|
+
fileAesKeys: Map<string, string>;
|
|
83
|
+
quoteContent: string | undefined;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 解析消息内容(支持单条消息、图文混排、事件回调和引用消息)
|
|
87
|
+
* @returns 提取的文本数组、图片URL数组和引用消息内容
|
|
88
|
+
*/
|
|
89
|
+
export declare function parseMessageContent(body: MessageBody): ParsedMessageContent;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信私有部署消息发送模块
|
|
3
|
+
*
|
|
4
|
+
* 负责通过 WSClient 发送回复消息,包含超时保护
|
|
5
|
+
*/
|
|
6
|
+
import type { RuntimeLogger } from "./interface.js";
|
|
7
|
+
import { type WSClient, type WsFrame } from "@wecom/aibot-node-sdk";
|
|
8
|
+
/** 流式回复超时错误码(>6分钟未更新,服务端拒绝继续流式更新) */
|
|
9
|
+
export declare const STREAM_EXPIRED_ERRCODE = 846608;
|
|
10
|
+
/**
|
|
11
|
+
* 流式回复过期错误
|
|
12
|
+
* 当服务端返回 errcode=846608 时抛出,表示流式消息已超过6分钟无法更新,
|
|
13
|
+
* 调用方需降级为主动发送(sendMessage)方式回复。
|
|
14
|
+
*/
|
|
15
|
+
export declare class StreamExpiredError extends Error {
|
|
16
|
+
readonly errcode = 846608;
|
|
17
|
+
constructor(message?: string);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 发送企业微信回复消息
|
|
21
|
+
* 供 monitor 内部和出站消息使用
|
|
22
|
+
*
|
|
23
|
+
* @returns messageId (streamId)
|
|
24
|
+
*/
|
|
25
|
+
export declare function sendWeComReply(params: {
|
|
26
|
+
wsClient: WSClient;
|
|
27
|
+
frame: WsFrame;
|
|
28
|
+
text?: string;
|
|
29
|
+
runtime: RuntimeLogger;
|
|
30
|
+
/** 是否为流式回复的最终消息,默认为 true */
|
|
31
|
+
finish?: boolean;
|
|
32
|
+
/** 指定 streamId,用于流式回复时保持相同的 streamId */
|
|
33
|
+
streamId?: string;
|
|
34
|
+
}): Promise<string>;
|