@wecom/wecom-openclaw-plugin 1.0.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 +18 -0
- package/dist/index.cjs.js +1791 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.esm.js +1787 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/src/channel.d.ts +3 -0
- package/dist/src/const.d.ts +48 -0
- package/dist/src/dm-policy.d.ts +29 -0
- package/dist/src/group-policy.d.ts +29 -0
- package/dist/src/interface.d.ts +144 -0
- package/dist/src/media-handler.d.ts +36 -0
- package/dist/src/message-parser.d.ts +72 -0
- package/dist/src/message-sender.d.ts +23 -0
- package/dist/src/monitor.d.ts +27 -0
- package/dist/src/onboarding.d.ts +5 -0
- package/dist/src/reqid-store.d.ts +31 -0
- package/dist/src/runtime.d.ts +3 -0
- package/dist/src/state-manager.d.ts +76 -0
- package/dist/src/timeout.d.ts +20 -0
- package/dist/src/utils.d.ts +73 -0
- package/openclaw.plugin.json +11 -0
- package/package.json +56 -0
|
@@ -0,0 +1,72 @@
|
|
|
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
|
+
userid: string;
|
|
13
|
+
};
|
|
14
|
+
response_url?: string;
|
|
15
|
+
msgtype: string;
|
|
16
|
+
text?: {
|
|
17
|
+
content: string;
|
|
18
|
+
};
|
|
19
|
+
image?: {
|
|
20
|
+
url?: string;
|
|
21
|
+
aeskey?: string;
|
|
22
|
+
};
|
|
23
|
+
voice?: {
|
|
24
|
+
content?: string;
|
|
25
|
+
};
|
|
26
|
+
mixed?: {
|
|
27
|
+
msg_item: Array<{
|
|
28
|
+
msgtype: "text" | "image";
|
|
29
|
+
text?: {
|
|
30
|
+
content: string;
|
|
31
|
+
};
|
|
32
|
+
image?: {
|
|
33
|
+
url?: string;
|
|
34
|
+
aeskey?: string;
|
|
35
|
+
};
|
|
36
|
+
}>;
|
|
37
|
+
};
|
|
38
|
+
file?: {
|
|
39
|
+
url?: string;
|
|
40
|
+
aeskey?: string;
|
|
41
|
+
};
|
|
42
|
+
quote?: {
|
|
43
|
+
msgtype: string;
|
|
44
|
+
text?: {
|
|
45
|
+
content: string;
|
|
46
|
+
};
|
|
47
|
+
voice?: {
|
|
48
|
+
content: string;
|
|
49
|
+
};
|
|
50
|
+
image?: {
|
|
51
|
+
url?: string;
|
|
52
|
+
aeskey?: string;
|
|
53
|
+
};
|
|
54
|
+
file?: {
|
|
55
|
+
url?: string;
|
|
56
|
+
aeskey?: string;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export interface ParsedMessageContent {
|
|
61
|
+
textParts: string[];
|
|
62
|
+
imageUrls: string[];
|
|
63
|
+
imageAesKeys: Map<string, string>;
|
|
64
|
+
fileUrls: string[];
|
|
65
|
+
fileAesKeys: Map<string, string>;
|
|
66
|
+
quoteContent: string | undefined;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 解析消息内容(支持单条消息、图文混排和引用消息)
|
|
70
|
+
* @returns 提取的文本数组、图片URL数组和引用消息内容
|
|
71
|
+
*/
|
|
72
|
+
export declare function parseMessageContent(body: MessageBody): ParsedMessageContent;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信消息发送模块
|
|
3
|
+
*
|
|
4
|
+
* 负责通过 WSClient 发送回复消息,包含超时保护
|
|
5
|
+
*/
|
|
6
|
+
import type { RuntimeEnv } from "openclaw/plugin-sdk";
|
|
7
|
+
import type { WSClient, WsFrame } from "@wecom/aibot-node-sdk";
|
|
8
|
+
/**
|
|
9
|
+
* 发送企业微信回复消息
|
|
10
|
+
* 供 monitor 内部和 channel outbound 使用
|
|
11
|
+
*
|
|
12
|
+
* @returns messageId (streamId)
|
|
13
|
+
*/
|
|
14
|
+
export declare function sendWeComReply(params: {
|
|
15
|
+
wsClient: WSClient;
|
|
16
|
+
frame: WsFrame;
|
|
17
|
+
text?: string;
|
|
18
|
+
runtime: RuntimeEnv;
|
|
19
|
+
/** 是否为流式回复的最终消息,默认为 true */
|
|
20
|
+
finish?: boolean;
|
|
21
|
+
/** 指定 streamId,用于流式回复时保持相同的 streamId */
|
|
22
|
+
streamId?: string;
|
|
23
|
+
}): Promise<string>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信 WebSocket 监控器主模块
|
|
3
|
+
*
|
|
4
|
+
* 负责:
|
|
5
|
+
* - 建立和管理 WebSocket 连接
|
|
6
|
+
* - 协调消息处理流程(解析→策略检查→下载图片→路由回复)
|
|
7
|
+
* - 资源生命周期管理
|
|
8
|
+
*
|
|
9
|
+
* 子模块:
|
|
10
|
+
* - message-parser.ts : 消息内容解析
|
|
11
|
+
* - message-sender.ts : 消息发送(带超时保护)
|
|
12
|
+
* - media-handler.ts : 图片下载和保存(带超时保护)
|
|
13
|
+
* - group-policy.ts : 群组访问控制
|
|
14
|
+
* - dm-policy.ts : 私聊访问控制
|
|
15
|
+
* - state-manager.ts : 全局状态管理(带 TTL 清理)
|
|
16
|
+
* - timeout.ts : 超时工具
|
|
17
|
+
*/
|
|
18
|
+
import type { WeComMonitorOptions } from "./interface.js";
|
|
19
|
+
export type { WeComMonitorOptions } from "./interface.js";
|
|
20
|
+
export { WeComCommand } from "./const.js";
|
|
21
|
+
export { getWeComWebSocket, setReqIdForChat, getReqIdForChatAsync, getReqIdForChat, deleteReqIdForChat, warmupReqIdStore, flushReqIdStore, } from "./state-manager.js";
|
|
22
|
+
export { sendWeComReply } from "./message-sender.js";
|
|
23
|
+
/**
|
|
24
|
+
* 监听企业微信 WebSocket 连接
|
|
25
|
+
* 使用 aibot-node-sdk 简化连接管理
|
|
26
|
+
*/
|
|
27
|
+
export declare function monitorWeComProvider(options: WeComMonitorOptions): Promise<void>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Store 配置 */
|
|
2
|
+
interface ReqIdStoreOptions {
|
|
3
|
+
/** TTL 毫秒数,超时的 reqId 视为过期(默认 24 小时) */
|
|
4
|
+
ttlMs?: number;
|
|
5
|
+
/** 内存最大条目数(默认 200) */
|
|
6
|
+
memoryMaxSize?: number;
|
|
7
|
+
/** 磁盘最大条目数(默认 500) */
|
|
8
|
+
fileMaxEntries?: number;
|
|
9
|
+
/** 磁盘写入防抖时间(毫秒),默认 1000ms */
|
|
10
|
+
flushDebounceMs?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface PersistentReqIdStore {
|
|
13
|
+
/** 设置 chatId 对应的 reqId(写入内存 + 防抖写磁盘) */
|
|
14
|
+
set(chatId: string, reqId: string): void;
|
|
15
|
+
/** 获取 chatId 对应的 reqId(异步:优先内存,miss 时查磁盘并回填内存) */
|
|
16
|
+
get(chatId: string): Promise<string | undefined>;
|
|
17
|
+
/** 同步获取 chatId 对应的 reqId(仅内存) */
|
|
18
|
+
getSync(chatId: string): string | undefined;
|
|
19
|
+
/** 删除 chatId 对应的 reqId */
|
|
20
|
+
delete(chatId: string): void;
|
|
21
|
+
/** 启动时从磁盘预热内存,返回加载条目数 */
|
|
22
|
+
warmup(onError?: (error: unknown) => void): Promise<number>;
|
|
23
|
+
/** 立即将内存数据刷写到磁盘(用于优雅退出) */
|
|
24
|
+
flush(): Promise<void>;
|
|
25
|
+
/** 清空内存缓存 */
|
|
26
|
+
clearMemory(): void;
|
|
27
|
+
/** 返回内存中的条目数 */
|
|
28
|
+
memorySize(): number;
|
|
29
|
+
}
|
|
30
|
+
export declare function createPersistentReqIdStore(accountId: string, options?: ReqIdStoreOptions): PersistentReqIdStore;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信全局状态管理模块
|
|
3
|
+
*
|
|
4
|
+
* 负责管理 WSClient 实例、消息状态(带 TTL 清理)、ReqId 存储
|
|
5
|
+
* 解决全局 Map 的内存泄漏问题
|
|
6
|
+
*/
|
|
7
|
+
import type { WSClient } from "@wecom/aibot-node-sdk";
|
|
8
|
+
import type { MessageState } from "./interface.js";
|
|
9
|
+
/**
|
|
10
|
+
* 获取指定账户的 WSClient 实例
|
|
11
|
+
*/
|
|
12
|
+
export declare function getWeComWebSocket(accountId: string): WSClient | null;
|
|
13
|
+
/**
|
|
14
|
+
* 设置指定账户的 WSClient 实例
|
|
15
|
+
*/
|
|
16
|
+
export declare function setWeComWebSocket(accountId: string, client: WSClient): void;
|
|
17
|
+
/**
|
|
18
|
+
* 删除指定账户的 WSClient 实例
|
|
19
|
+
*/
|
|
20
|
+
export declare function deleteWeComWebSocket(accountId: string): void;
|
|
21
|
+
/**
|
|
22
|
+
* 启动消息状态定期清理(自动 TTL 清理 + 容量限制)
|
|
23
|
+
*/
|
|
24
|
+
export declare function startMessageStateCleanup(): void;
|
|
25
|
+
/**
|
|
26
|
+
* 停止消息状态定期清理
|
|
27
|
+
*/
|
|
28
|
+
export declare function stopMessageStateCleanup(): void;
|
|
29
|
+
/**
|
|
30
|
+
* 设置消息状态
|
|
31
|
+
*/
|
|
32
|
+
export declare function setMessageState(messageId: string, state: MessageState): void;
|
|
33
|
+
/**
|
|
34
|
+
* 获取消息状态
|
|
35
|
+
*/
|
|
36
|
+
export declare function getMessageState(messageId: string): MessageState | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* 删除消息状态
|
|
39
|
+
*/
|
|
40
|
+
export declare function deleteMessageState(messageId: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* 清空所有消息状态
|
|
43
|
+
*/
|
|
44
|
+
export declare function clearAllMessageStates(): void;
|
|
45
|
+
/**
|
|
46
|
+
* 设置 chatId 对应的 reqId(写入内存 + 防抖写磁盘)
|
|
47
|
+
*/
|
|
48
|
+
export declare function setReqIdForChat(chatId: string, reqId: string, accountId?: string): void;
|
|
49
|
+
/**
|
|
50
|
+
* 获取 chatId 对应的 reqId(异步:优先内存,miss 时查磁盘并回填内存)
|
|
51
|
+
*/
|
|
52
|
+
export declare function getReqIdForChatAsync(chatId: string, accountId?: string): Promise<string | undefined>;
|
|
53
|
+
/**
|
|
54
|
+
* 获取 chatId 对应的 reqId(同步:仅内存,保留向后兼容)
|
|
55
|
+
*/
|
|
56
|
+
export declare function getReqIdForChat(chatId: string, accountId?: string): string | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* 删除 chatId 对应的 reqId
|
|
59
|
+
*/
|
|
60
|
+
export declare function deleteReqIdForChat(chatId: string, accountId?: string): void;
|
|
61
|
+
/**
|
|
62
|
+
* 启动时预热 reqId 缓存(从磁盘加载到内存)
|
|
63
|
+
*/
|
|
64
|
+
export declare function warmupReqIdStore(accountId?: string, log?: (...args: unknown[]) => void): Promise<number>;
|
|
65
|
+
/**
|
|
66
|
+
* 立即将 reqId 数据刷写到磁盘(用于优雅退出)
|
|
67
|
+
*/
|
|
68
|
+
export declare function flushReqIdStore(accountId?: string): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* 清理指定账户的所有资源
|
|
71
|
+
*/
|
|
72
|
+
export declare function cleanupAccount(accountId: string): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* 清理所有资源(用于进程退出)
|
|
75
|
+
*/
|
|
76
|
+
export declare function cleanupAll(): Promise<void>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 超时控制工具模块
|
|
3
|
+
*
|
|
4
|
+
* 为异步操作提供统一的超时保护机制
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* 为 Promise 添加超时保护
|
|
8
|
+
*
|
|
9
|
+
* @param promise - 原始 Promise
|
|
10
|
+
* @param timeoutMs - 超时时间(毫秒)
|
|
11
|
+
* @param message - 超时错误消息
|
|
12
|
+
* @returns 带超时保护的 Promise
|
|
13
|
+
*/
|
|
14
|
+
export declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number, message?: string): Promise<T>;
|
|
15
|
+
/**
|
|
16
|
+
* 超时错误类型
|
|
17
|
+
*/
|
|
18
|
+
export declare class TimeoutError extends Error {
|
|
19
|
+
constructor(message: string);
|
|
20
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 企业微信公共工具函数
|
|
3
|
+
*/
|
|
4
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk";
|
|
5
|
+
/**
|
|
6
|
+
* 企业微信配置类型
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* 企业微信群组配置
|
|
10
|
+
*/
|
|
11
|
+
export interface WeComGroupConfig {
|
|
12
|
+
/** 群组内发送者白名单(仅列表中的成员消息会被处理) */
|
|
13
|
+
allowFrom?: Array<string | number>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 企业微信配置类型
|
|
17
|
+
*/
|
|
18
|
+
export interface WeComConfig {
|
|
19
|
+
enabled?: boolean;
|
|
20
|
+
websocketUrl?: string;
|
|
21
|
+
botId?: string;
|
|
22
|
+
secret?: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
allowFrom?: Array<string | number>;
|
|
25
|
+
dmPolicy?: "open" | "allowlist" | "pairing" | "disabled";
|
|
26
|
+
/** 群组访问策略:"open" = 允许所有群组(默认),"allowlist" = 仅允许 groupAllowFrom 中的群组,"disabled" = 禁用群组消息 */
|
|
27
|
+
groupPolicy?: "open" | "allowlist" | "disabled";
|
|
28
|
+
/** 群组白名单(仅 groupPolicy="allowlist" 时生效) */
|
|
29
|
+
groupAllowFrom?: Array<string | number>;
|
|
30
|
+
/** 每个群组的详细配置(如群组内发送者白名单) */
|
|
31
|
+
groups?: Record<string, WeComGroupConfig>;
|
|
32
|
+
/** 是否发送"思考中"消息,默认为 true */
|
|
33
|
+
sendThinkingMessage?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare const DefaultWsUrl = "wss://openws.work.weixin.qq.com";
|
|
36
|
+
export interface ResolvedWeComAccount {
|
|
37
|
+
accountId: string;
|
|
38
|
+
name: string;
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
websocketUrl: string;
|
|
41
|
+
botId: string;
|
|
42
|
+
secret: string;
|
|
43
|
+
/** 是否发送"思考中"消息,默认为 true */
|
|
44
|
+
sendThinkingMessage: boolean;
|
|
45
|
+
config: WeComConfig;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 解析企业微信账户配置
|
|
49
|
+
*/
|
|
50
|
+
export declare function resolveWeComAccount(cfg: OpenClawConfig): ResolvedWeComAccount;
|
|
51
|
+
/**
|
|
52
|
+
* 设置企业微信账户配置
|
|
53
|
+
*/
|
|
54
|
+
export declare function setWeComAccount(cfg: OpenClawConfig, account: Partial<WeComConfig>): OpenClawConfig;
|
|
55
|
+
/**
|
|
56
|
+
* 生成企业微信请求 ID
|
|
57
|
+
* @param prefix - 请求类型前缀(subscribe, ping, response 等)
|
|
58
|
+
* @returns 格式化的请求 ID
|
|
59
|
+
*/
|
|
60
|
+
export declare function generateReqId(prefix: string): string;
|
|
61
|
+
/**
|
|
62
|
+
* 判断 req_id 的类型
|
|
63
|
+
* @param reqId - 请求 ID
|
|
64
|
+
* @returns 请求类型(subscribe, ping, response 等)
|
|
65
|
+
*/
|
|
66
|
+
export declare function getReqIdType(reqId: string): string | null;
|
|
67
|
+
/**
|
|
68
|
+
* 检查 req_id 是否为指定类型
|
|
69
|
+
* @param reqId - 请求 ID
|
|
70
|
+
* @param type - 请求类型
|
|
71
|
+
* @returns 是否匹配
|
|
72
|
+
*/
|
|
73
|
+
export declare function isReqIdType(reqId: string, type: string): boolean;
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wecom/wecom-openclaw-plugin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.cjs.js",
|
|
6
|
+
"module": "dist/index.esm.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"openclaw.plugin.json",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "rollup -c",
|
|
15
|
+
"dev": "rollup -c -w",
|
|
16
|
+
"clean": "rm -rf dist",
|
|
17
|
+
"prebuild": "npm run clean",
|
|
18
|
+
"publish:release": "npm run prebuild && npm run build && npm publish"
|
|
19
|
+
},
|
|
20
|
+
"description": "OpenClaw WeCom (企业微信) channel plugin",
|
|
21
|
+
"openclaw": {
|
|
22
|
+
"extensions": [
|
|
23
|
+
"./dist/index.esm.js"
|
|
24
|
+
],
|
|
25
|
+
"channel": {
|
|
26
|
+
"id": "wecom",
|
|
27
|
+
"label": "企业微信",
|
|
28
|
+
"selectionLabel": "企业微信 (WeCom)",
|
|
29
|
+
"docsPath": "/channels/wecom",
|
|
30
|
+
"docsLabel": "wecom-openclaw-plugin",
|
|
31
|
+
"blurb": "企业微信机器人接入插件",
|
|
32
|
+
"order": 80,
|
|
33
|
+
"quickstartAllowFrom": true
|
|
34
|
+
},
|
|
35
|
+
"install": {
|
|
36
|
+
"npmSpec": "@wecom/wecom-openclaw-plugin",
|
|
37
|
+
"localPath": "extensions/wecom-openclaw-plugin",
|
|
38
|
+
"defaultChoice": "npm"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@wecom/aibot-node-sdk": "^1.0.0",
|
|
43
|
+
"file-type": "^21.3.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@rollup/plugin-commonjs": "^25.0.7",
|
|
47
|
+
"@rollup/plugin-json": "^6.1.0",
|
|
48
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
49
|
+
"@rollup/plugin-typescript": "^11.1.6",
|
|
50
|
+
"openclaw": ">=2026.1.29",
|
|
51
|
+
"rollup": "^4.9.6",
|
|
52
|
+
"rollup-plugin-dts": "^6.1.0",
|
|
53
|
+
"tslib": "^2.6.2",
|
|
54
|
+
"typescript": "^5.3.3"
|
|
55
|
+
}
|
|
56
|
+
}
|