@lanmers/wecom-openclaw-plugin 1.0.12

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.
Files changed (59) hide show
  1. package/README.md +220 -0
  2. package/dist/index.cjs.js +3591 -0
  3. package/dist/index.cjs.js.map +1 -0
  4. package/dist/index.d.ts +11 -0
  5. package/dist/index.esm.js +3565 -0
  6. package/dist/index.esm.js.map +1 -0
  7. package/dist/src/channel.d.ts +3 -0
  8. package/dist/src/const.d.ts +64 -0
  9. package/dist/src/dm-policy.d.ts +29 -0
  10. package/dist/src/group-policy.d.ts +29 -0
  11. package/dist/src/interface.d.ts +154 -0
  12. package/dist/src/mcp/index.d.ts +6 -0
  13. package/dist/src/mcp/schema.d.ts +11 -0
  14. package/dist/src/mcp/tool.d.ts +55 -0
  15. package/dist/src/mcp/transport.d.ts +61 -0
  16. package/dist/src/mcp-config.d.ts +29 -0
  17. package/dist/src/media-handler.d.ts +36 -0
  18. package/dist/src/media-uploader.d.ts +131 -0
  19. package/dist/src/message-parser.d.ts +72 -0
  20. package/dist/src/message-sender.d.ts +23 -0
  21. package/dist/src/monitor.d.ts +27 -0
  22. package/dist/src/onboarding.d.ts +5 -0
  23. package/dist/src/openclaw-compat.d.ts +48 -0
  24. package/dist/src/reqid-store.d.ts +31 -0
  25. package/dist/src/runtime.d.ts +3 -0
  26. package/dist/src/state-manager.d.ts +76 -0
  27. package/dist/src/timeout.d.ts +20 -0
  28. package/dist/src/utils.d.ts +96 -0
  29. package/dist/src/version.d.ts +2 -0
  30. package/openclaw.plugin.json +14 -0
  31. package/package.json +73 -0
  32. package/skills/wecom-contact-lookup/SKILL.md +162 -0
  33. package/skills/wecom-doc/SKILL.md +363 -0
  34. package/skills/wecom-doc/references/doc-api.md +224 -0
  35. package/skills/wecom-doc-manager/SKILL.md +64 -0
  36. package/skills/wecom-doc-manager/references/api-create-doc.md +56 -0
  37. package/skills/wecom-doc-manager/references/api-edit-doc-content.md +68 -0
  38. package/skills/wecom-doc-manager/references/api-export-document.md +88 -0
  39. package/skills/wecom-edit-todo/SKILL.md +249 -0
  40. package/skills/wecom-get-todo-detail/SKILL.md +143 -0
  41. package/skills/wecom-get-todo-list/SKILL.md +127 -0
  42. package/skills/wecom-meeting-create/SKILL.md +158 -0
  43. package/skills/wecom-meeting-create/references/example-full.md +30 -0
  44. package/skills/wecom-meeting-create/references/example-reminder.md +46 -0
  45. package/skills/wecom-meeting-create/references/example-security.md +22 -0
  46. package/skills/wecom-meeting-manage/SKILL.md +136 -0
  47. package/skills/wecom-meeting-query/SKILL.md +330 -0
  48. package/skills/wecom-preflight/SKILL.md +141 -0
  49. package/skills/wecom-schedule/SKILL.md +159 -0
  50. package/skills/wecom-schedule/references/api-check-availability.md +56 -0
  51. package/skills/wecom-schedule/references/api-create-schedule.md +38 -0
  52. package/skills/wecom-schedule/references/api-get-schedule-detail.md +81 -0
  53. package/skills/wecom-schedule/references/api-update-schedule.md +30 -0
  54. package/skills/wecom-schedule/references/ref-reminders.md +24 -0
  55. package/skills/wecom-smartsheet-data/SKILL.md +71 -0
  56. package/skills/wecom-smartsheet-data/references/api-get-records.md +61 -0
  57. package/skills/wecom-smartsheet-data/references/cell-value-formats.md +120 -0
  58. package/skills/wecom-smartsheet-schema/SKILL.md +92 -0
  59. package/skills/wecom-smartsheet-schema/references/field-types.md +43 -0
@@ -0,0 +1,3 @@
1
+ import { type ChannelPlugin } from "openclaw/plugin-sdk";
2
+ import type { ResolvedWeComAccount } from "./utils.js";
3
+ export declare const wecomPlugin: ChannelPlugin<ResolvedWeComAccount>;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * 企业微信渠道常量定义
3
+ */
4
+ /**
5
+ * 企业微信渠道 ID
6
+ */
7
+ export declare const CHANNEL_ID: "wecom";
8
+ /**
9
+ * 企业微信 WebSocket 命令枚举
10
+ */
11
+ export declare enum WeComCommand {
12
+ /** 认证订阅 */
13
+ SUBSCRIBE = "aibot_subscribe",
14
+ /** 心跳 */
15
+ PING = "ping",
16
+ /** 企业微信推送消息 */
17
+ AIBOT_CALLBACK = "aibot_callback",
18
+ /** clawdbot 响应消息 */
19
+ AIBOT_RESPONSE = "aibot_response"
20
+ }
21
+ /** 图片下载超时时间(毫秒) */
22
+ export declare const IMAGE_DOWNLOAD_TIMEOUT_MS = 30000;
23
+ /** 文件下载超时时间(毫秒) */
24
+ export declare const FILE_DOWNLOAD_TIMEOUT_MS = 60000;
25
+ /** 消息发送超时时间(毫秒) */
26
+ export declare const REPLY_SEND_TIMEOUT_MS = 15000;
27
+ /** 消息处理总超时时间(毫秒) */
28
+ export declare const MESSAGE_PROCESS_TIMEOUT_MS: number;
29
+ /** WebSocket 心跳间隔(毫秒) */
30
+ export declare const WS_HEARTBEAT_INTERVAL_MS = 30000;
31
+ /** WebSocket 最大重连次数 */
32
+ export declare const WS_MAX_RECONNECT_ATTEMPTS = 100;
33
+ /** messageStates Map 条目的最大 TTL(毫秒),防止内存泄漏 */
34
+ export declare const MESSAGE_STATE_TTL_MS: number;
35
+ /** messageStates Map 清理间隔(毫秒) */
36
+ export declare const MESSAGE_STATE_CLEANUP_INTERVAL_MS = 60000;
37
+ /** messageStates Map 最大条目数 */
38
+ export declare const MESSAGE_STATE_MAX_SIZE = 500;
39
+ /** "思考中"流式消息占位内容 */
40
+ export declare const THINKING_MESSAGE = "<think></think>";
41
+ /** 仅包含图片时的消息占位符 */
42
+ export declare const MEDIA_IMAGE_PLACEHOLDER = "<media:image>";
43
+ /** 仅包含文件时的消息占位符 */
44
+ export declare const MEDIA_DOCUMENT_PLACEHOLDER = "<media:document>";
45
+ /** 获取 MCP 配置的 WebSocket 命令 */
46
+ export declare const MCP_GET_CONFIG_CMD = "aibot_get_mcp_config";
47
+ /** MCP 配置拉取超时时间(毫秒) */
48
+ export declare const MCP_CONFIG_FETCH_TIMEOUT_MS = 15000;
49
+ /** 默认媒体大小上限(MB) */
50
+ export declare const DEFAULT_MEDIA_MAX_MB = 5;
51
+ /** 文本分块大小上限 */
52
+ export declare const TEXT_CHUNK_LIMIT = 4000;
53
+ /** 图片大小上限(字节):10MB */
54
+ export declare const IMAGE_MAX_BYTES: number;
55
+ /** 视频大小上限(字节):10MB */
56
+ export declare const VIDEO_MAX_BYTES: number;
57
+ /** 语音大小上限(字节):2MB */
58
+ export declare const VOICE_MAX_BYTES: number;
59
+ /** 文件大小上限(字节):20MB */
60
+ export declare const FILE_MAX_BYTES: number;
61
+ /** 文件绝对上限(字节):超过此值无法发送,等于 FILE_MAX_BYTES */
62
+ export declare const ABSOLUTE_MAX_BYTES: number;
63
+ /** 上传分片大小(字节,Base64 编码前):512KB */
64
+ export declare const UPLOAD_CHUNK_SIZE: number;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * 企业微信 DM(私聊)访问控制模块
3
+ *
4
+ * 负责私聊策略检查、配对流程
5
+ */
6
+ import type { RuntimeEnv } from "openclaw/plugin-sdk";
7
+ import type { WSClient, WsFrame } from "@wecom/aibot-node-sdk";
8
+ import type { ResolvedWeComAccount } from "./utils.js";
9
+ /**
10
+ * DM Policy 检查结果
11
+ */
12
+ export interface DmPolicyCheckResult {
13
+ /** 是否允许继续处理消息 */
14
+ allowed: boolean;
15
+ /** 是否已发送配对消息(仅在 pairing 模式下) */
16
+ pairingSent?: boolean;
17
+ }
18
+ /**
19
+ * 检查 DM Policy 访问控制
20
+ * @returns 检查结果,包含是否允许继续处理
21
+ */
22
+ export declare function checkDmPolicy(params: {
23
+ senderId: string;
24
+ isGroup: boolean;
25
+ account: ResolvedWeComAccount;
26
+ wsClient: WSClient;
27
+ frame: WsFrame;
28
+ runtime: RuntimeEnv;
29
+ }): Promise<DmPolicyCheckResult>;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * 企业微信群组访问控制模块
3
+ *
4
+ * 负责群组策略检查(groupPolicy、群组白名单、群内发送者白名单)
5
+ */
6
+ import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk";
7
+ import type { ResolvedWeComAccount } from "./utils.js";
8
+ /**
9
+ * 群组策略检查结果
10
+ */
11
+ export interface GroupPolicyCheckResult {
12
+ /** 是否允许继续处理消息 */
13
+ allowed: boolean;
14
+ }
15
+ /**
16
+ * 检查群组策略访问控制
17
+ * @returns 检查结果,包含是否允许继续处理
18
+ */
19
+ export declare function checkGroupPolicy(params: {
20
+ chatId: string;
21
+ senderId: string;
22
+ account: ResolvedWeComAccount;
23
+ config: OpenClawConfig;
24
+ runtime: RuntimeEnv;
25
+ }): GroupPolicyCheckResult;
26
+ /**
27
+ * 检查发送者是否在允许列表中(通用)
28
+ */
29
+ export declare function isSenderAllowed(senderId: string, allowFrom: string[]): boolean;
@@ -0,0 +1,154 @@
1
+ /**
2
+ * 企业微信渠道类型定义
3
+ */
4
+ import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk";
5
+ import type { ResolvedWeComAccount } from "./utils.js";
6
+ import { WeComCommand } from "./const.js";
7
+ /**
8
+ * Monitor 配置选项
9
+ */
10
+ export type WeComMonitorOptions = {
11
+ account: ResolvedWeComAccount;
12
+ config: OpenClawConfig;
13
+ runtime: RuntimeEnv;
14
+ abortSignal?: AbortSignal;
15
+ };
16
+ /**
17
+ * 消息状态
18
+ */
19
+ export interface MessageState {
20
+ accumulatedText: string;
21
+ /** 流式回复的 streamId,用于保持同一个流式回复使用相同的 streamId */
22
+ streamId?: string;
23
+ /** 是否有用户可见的文本内容(不包括 <think>...</think> 标签) */
24
+ hasText?: boolean;
25
+ /** 是否已成功发送过媒体文件 */
26
+ hasMedia?: boolean;
27
+ /** 是否有媒体发送失败(权限不足、文件过大等) */
28
+ hasMediaFailed?: boolean;
29
+ /** 媒体发送失败时的纯文本错误摘要(用于替换 thinking 流展示给用户) */
30
+ mediaErrorSummary?: string;
31
+ /** deliver 回调是否被调用过(用于区分"核心无回复"和"核心回复了空内容") */
32
+ deliverCalled?: boolean;
33
+ }
34
+ /**
35
+ * WebSocket 请求消息基础格式
36
+ */
37
+ export interface WeComRequest {
38
+ cmd: string;
39
+ headers: {
40
+ req_id: string;
41
+ };
42
+ body: any;
43
+ }
44
+ /**
45
+ * WebSocket 响应消息格式
46
+ */
47
+ export interface WeComResponse {
48
+ headers: {
49
+ req_id: string;
50
+ };
51
+ errcode: number;
52
+ errmsg: string;
53
+ }
54
+ /**
55
+ * 企业微信认证请求
56
+ */
57
+ export interface WeComSubscribeRequest extends WeComRequest {
58
+ cmd: WeComCommand.SUBSCRIBE;
59
+ body: {
60
+ secret: string;
61
+ bot_id: string;
62
+ };
63
+ }
64
+ /**
65
+ * 企业微信推送消息格式
66
+ */
67
+ export interface WeComCallbackMessage {
68
+ cmd: WeComCommand.AIBOT_CALLBACK;
69
+ headers: {
70
+ req_id: string;
71
+ };
72
+ body: {
73
+ msgid: string;
74
+ aibotid: string;
75
+ chatid: string;
76
+ chattype: "single" | "group";
77
+ from: {
78
+ userid: string;
79
+ };
80
+ response_url: string;
81
+ msgtype: "text" | "image" | "voice" | "video" | "file" | "stream" | "mixed";
82
+ text?: {
83
+ content: string;
84
+ };
85
+ image?: {
86
+ /** 图片 URL(通过 URL 方式接收图片时) */
87
+ url?: string;
88
+ /** 图片 base64 数据(直接传输时) */
89
+ base64?: string;
90
+ md5?: string;
91
+ };
92
+ /** 图文混排消息 */
93
+ mixed?: {
94
+ msg_item: Array<{
95
+ msgtype: "text" | "image";
96
+ text?: {
97
+ content: string;
98
+ };
99
+ image?: {
100
+ url?: string;
101
+ base64?: string;
102
+ md5?: string;
103
+ };
104
+ }>;
105
+ };
106
+ quote?: {
107
+ msgtype: string;
108
+ text?: {
109
+ content: string;
110
+ };
111
+ image?: {
112
+ url?: string;
113
+ aeskey?: string;
114
+ };
115
+ file?: {
116
+ url?: string;
117
+ aeskey?: string;
118
+ };
119
+ };
120
+ stream?: {
121
+ id: string;
122
+ };
123
+ };
124
+ }
125
+ /**
126
+ * 企业微信响应消息格式
127
+ */
128
+ export interface WeComResponseMessage extends WeComRequest {
129
+ cmd: WeComCommand.AIBOT_RESPONSE;
130
+ body: {
131
+ msgtype: "stream" | "text" | "markdown";
132
+ stream?: {
133
+ id: string;
134
+ finish: boolean;
135
+ content: string;
136
+ msg_item?: Array<{
137
+ msgtype: "image" | "file";
138
+ image?: {
139
+ base64: string;
140
+ md5: string;
141
+ };
142
+ }>;
143
+ feedback?: {
144
+ id: string;
145
+ };
146
+ };
147
+ text?: {
148
+ content: string;
149
+ };
150
+ markdown?: {
151
+ content: string;
152
+ };
153
+ };
154
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * MCP 模块统一导出
3
+ */
4
+ export { createWeComMcpTool } from "./tool.js";
5
+ export { sendJsonRpc, clearCategoryCache, McpRpcError, McpHttpError, type McpToolInfo } from "./transport.js";
6
+ export { cleanSchemaForGemini } from "./schema.js";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * MCP Schema 清洗模块
3
+ *
4
+ * 负责内联 $ref/$defs 引用并移除 Gemini 不支持的 JSON Schema 关键词,
5
+ * 防止 Gemini 模型解析 function response 时报 400 错误。
6
+ */
7
+ /**
8
+ * 清洗 JSON Schema,内联 $ref 引用并移除 Gemini 不支持的关键词,
9
+ * 防止 Gemini 模型解析 function response 时报 400 错误。
10
+ */
11
+ export declare function cleanSchemaForGemini(schema: unknown): unknown;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * wecom_mcp — 模拟 MCP 调用的 Agent Tool
3
+ *
4
+ * 通过 MCP Streamable HTTP 传输协议调用企业微信 MCP Server,
5
+ * 提供 list(列出所有工具)和 call(调用工具)两个操作。
6
+ *
7
+ * 在 skills 中的使用方式:
8
+ * wecom_mcp list <category>
9
+ * wecom_mcp call <category> <method> '<jsonArgs>'
10
+ *
11
+ * 示例:
12
+ * wecom_mcp list contact
13
+ * wecom_mcp call contact getContact '{}'
14
+ */
15
+ /**
16
+ * 创建 wecom_mcp Agent Tool 定义
17
+ */
18
+ export declare function createWeComMcpTool(): {
19
+ name: string;
20
+ label: string;
21
+ description: string;
22
+ parameters: {
23
+ type: "object";
24
+ properties: {
25
+ action: {
26
+ type: string;
27
+ enum: string[];
28
+ description: string;
29
+ };
30
+ category: {
31
+ type: string;
32
+ description: string;
33
+ };
34
+ accountId: {
35
+ type: string;
36
+ description: string;
37
+ };
38
+ method: {
39
+ type: string;
40
+ description: string;
41
+ };
42
+ args: {
43
+ type: string[];
44
+ description: string;
45
+ };
46
+ };
47
+ required: string[];
48
+ };
49
+ execute(_toolCallId: string, params: unknown): Promise<{
50
+ content: {
51
+ type: "text";
52
+ text: string;
53
+ }[];
54
+ }>;
55
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * MCP Streamable HTTP 传输层模块
3
+ *
4
+ * 负责:
5
+ * - MCP JSON-RPC over HTTP 通信(发送请求、解析响应)
6
+ * - Streamable HTTP session 生命周期管理(initialize 握手 → Mcp-Session-Id 维护 → 失效重建)
7
+ * - 自动检测无状态 Server:如果 initialize 响应未返回 Mcp-Session-Id,
8
+ * 则标记为无状态模式,后续请求跳过握手和 session 管理
9
+ * - SSE 流式响应解析
10
+ * - MCP 配置运行时缓存(通过 WSClient 拉取 URL 并缓存在内存中)
11
+ */
12
+ /**
13
+ * MCP JSON-RPC 错误
14
+ *
15
+ * 携带服务端返回的 JSON-RPC error.code,
16
+ * 用于上层按错误码进行差异化处理(如特定错误码触发缓存清理)。
17
+ */
18
+ export declare class McpRpcError extends Error {
19
+ readonly code: number;
20
+ readonly data?: unknown | undefined;
21
+ constructor(code: number, message: string, data?: unknown | undefined);
22
+ }
23
+ /**
24
+ * MCP HTTP 错误
25
+ *
26
+ * 携带 HTTP 状态码,用于精确判断 session 失效(404)等场景,
27
+ * 避免通过字符串匹配 "404" 导致的误判。
28
+ */
29
+ export declare class McpHttpError extends Error {
30
+ readonly statusCode: number;
31
+ constructor(statusCode: number, message: string);
32
+ }
33
+ /**
34
+ * 清理指定品类的所有 MCP 缓存(配置、会话、无状态标记)
35
+ *
36
+ * 当 MCP Server 返回特定错误码时调用,确保下次请求重新拉取配置并重建会话。
37
+ *
38
+ * @param category - MCP 品类名称
39
+ * @param accountId - 账户 ID(可选,用于清理特定账户的缓存)
40
+ */
41
+ export declare function clearCategoryCache(category: string, accountId?: string): void;
42
+ /** tools/list 返回的工具描述 */
43
+ export interface McpToolInfo {
44
+ name: string;
45
+ description?: string;
46
+ inputSchema?: Record<string, unknown>;
47
+ }
48
+ /**
49
+ * 发送 JSON-RPC 请求到 MCP Server(Streamable HTTP 协议)
50
+ *
51
+ * 自动管理 session 生命周期:
52
+ * - 无状态 Server:跳过 session 管理,直接发送请求
53
+ * - 有状态 Server:首次调用先执行 initialize 握手,session 失效(404)时自动重建并重试
54
+ *
55
+ * @param category - MCP 品类名称
56
+ * @param method - JSON-RPC 方法名
57
+ * @param params - JSON-RPC 参数
58
+ * @param accountId - 账户 ID(可选,默认使用 DEFAULT_ACCOUNT_ID)
59
+ * @returns JSON-RPC result
60
+ */
61
+ export declare function sendJsonRpc(category: string, method: string, params?: Record<string, unknown>, accountId?: string): Promise<unknown>;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * MCP 配置拉取与持久化模块
3
+ *
4
+ * 负责:
5
+ * - 通过 WSClient 发送 aibot_get_mcp_config 请求
6
+ * - 解析服务端响应,提取 MCP 配置(url、type、is_authed)
7
+ * - 将配置写入 openclaw.plugin.json 的 wecomMcp 字段
8
+ */
9
+ import { type WSClient } from "@wecom/aibot-node-sdk";
10
+ import type { RuntimeEnv } from "openclaw/plugin-sdk";
11
+ import type { McpConfigBody } from "./interface.js";
12
+ /**
13
+ * 通过 WSClient 发送 aibot_get_mcp_config 命令,获取 MCP 配置
14
+ *
15
+ * @param wsClient - 已认证的 WSClient 实例
16
+ * @returns MCP 配置(url、type、is_authed)
17
+ * @throws 响应错误码非 0 或缺少 url 字段时抛出错误
18
+ */
19
+ export declare function fetchMcpConfig(wsClient: WSClient): Promise<McpConfigBody>;
20
+ /**
21
+ * 拉取 MCP 配置并持久化到 openclaw.plugin.json
22
+ *
23
+ * 认证成功后调用。失败仅记录日志,不影响 WebSocket 消息正常收发。
24
+ *
25
+ * @param wsClient - 已认证的 WSClient 实例
26
+ * @param accountId - 账户 ID(用于日志)
27
+ * @param runtime - 运行时环境(用于日志)
28
+ */
29
+ export declare function fetchAndSaveMcpConfig(wsClient: WSClient, accountId: string, runtime: RuntimeEnv): Promise<void>;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * 企业微信媒体(图片)下载和保存模块
3
+ *
4
+ * 负责下载、检测格式、保存图片到本地,包含超时保护
5
+ */
6
+ import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk";
7
+ import type { WSClient } from "@wecom/aibot-node-sdk";
8
+ import type { ResolvedWeComAccount } from "./utils.js";
9
+ /**
10
+ * 下载并保存所有图片到本地,每张图片的下载带超时保护
11
+ */
12
+ export declare function downloadAndSaveImages(params: {
13
+ imageUrls: string[];
14
+ imageAesKeys?: Map<string, string>;
15
+ account: ResolvedWeComAccount;
16
+ config: OpenClawConfig;
17
+ runtime: RuntimeEnv;
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: ResolvedWeComAccount;
30
+ config: OpenClawConfig;
31
+ runtime: RuntimeEnv;
32
+ wsClient: WSClient;
33
+ }): Promise<Array<{
34
+ path: string;
35
+ contentType?: string;
36
+ }>>;
@@ -0,0 +1,131 @@
1
+ /**
2
+ * 企业微信出站媒体上传工具模块
3
+ *
4
+ * 负责:
5
+ * - 从 mediaUrl 加载文件 buffer(远程 URL 或本地路径均支持)
6
+ * - 检测 MIME 类型并映射为企微媒体类型
7
+ * - 文件大小检查与降级策略
8
+ */
9
+ import type { WeComMediaType, WSClient, WsFrameHeaders } from "@wecom/aibot-node-sdk";
10
+ /** 媒体文件解析结果 */
11
+ export interface ResolvedMedia {
12
+ /** 文件数据 */
13
+ buffer: Buffer;
14
+ /** 检测到的 MIME 类型 */
15
+ contentType: string;
16
+ /** 文件名(从 URL 提取或默认生成) */
17
+ fileName: string;
18
+ }
19
+ /** 文件大小检查结果 */
20
+ export interface FileSizeCheckResult {
21
+ /** 最终确定的企微媒体类型(可能被降级) */
22
+ finalType: WeComMediaType;
23
+ /** 是否需要拒绝(超过绝对限制) */
24
+ shouldReject: boolean;
25
+ /** 拒绝原因(仅 shouldReject=true 时有值) */
26
+ rejectReason?: string;
27
+ /** 是否发生了降级 */
28
+ downgraded: boolean;
29
+ /** 降级说明(仅 downgraded=true 时有值) */
30
+ downgradeNote?: string;
31
+ }
32
+ /**
33
+ * 根据 MIME 类型检测企微媒体类型
34
+ *
35
+ * @param mimeType - MIME 类型字符串
36
+ * @returns 企微媒体类型
37
+ */
38
+ export declare function detectWeComMediaType(mimeType: string): WeComMediaType;
39
+ /**
40
+ * 从 mediaUrl 加载媒体文件
41
+ *
42
+ * 支持远程 URL(http/https)和本地路径(file:// 或绝对路径),
43
+ * 利用 openclaw plugin-sdk 的 loadOutboundMediaFromUrl 统一处理。
44
+ *
45
+ * @param mediaUrl - 媒体文件的 URL 或本地路径
46
+ * @param mediaLocalRoots - 允许读取本地文件的安全白名单目录
47
+ * @returns 解析后的媒体文件信息
48
+ */
49
+ export declare function resolveMediaFile(mediaUrl: string, mediaLocalRoots?: readonly string[]): Promise<ResolvedMedia>;
50
+ /**
51
+ * 检查文件大小并执行降级策略
52
+ *
53
+ * 降级规则:
54
+ * - voice 非 AMR 格式 → 降级为 file(企微后台仅支持 AMR)
55
+ * - image 超过 10MB → 降级为 file
56
+ * - video 超过 10MB → 降级为 file
57
+ * - voice 超过 2MB → 降级为 file
58
+ * - file 超过 20MB → 拒绝发送
59
+ *
60
+ * @param fileSize - 文件大小(字节)
61
+ * @param detectedType - 检测到的企微媒体类型
62
+ * @param contentType - 文件的 MIME 类型(用于语音格式校验)
63
+ * @returns 大小检查结果
64
+ */
65
+ export declare function applyFileSizeLimits(fileSize: number, detectedType: WeComMediaType, contentType?: string): FileSizeCheckResult;
66
+ /** uploadAndSendMedia 的参数 */
67
+ export interface UploadAndSendMediaOptions {
68
+ /** WSClient 实例 */
69
+ wsClient: WSClient;
70
+ /** 媒体文件的 URL 或本地路径 */
71
+ mediaUrl: string;
72
+ /** 目标会话 ID(用于 aibot_send_msg 主动发送) */
73
+ chatId: string;
74
+ /** 允许读取本地文件的安全白名单目录 */
75
+ mediaLocalRoots?: readonly string[];
76
+ /** 日志函数 */
77
+ log?: (...args: any[]) => void;
78
+ /** 错误日志函数 */
79
+ errorLog?: (...args: any[]) => void;
80
+ }
81
+ /** uploadAndSendMedia 的返回结果 */
82
+ export interface UploadAndSendMediaResult {
83
+ /** 是否发送成功 */
84
+ ok: boolean;
85
+ /** 发送后返回的 messageId */
86
+ messageId?: string;
87
+ /** 最终的企微媒体类型 */
88
+ finalType?: WeComMediaType;
89
+ /** 是否被拒绝(文件过大) */
90
+ rejected?: boolean;
91
+ /** 拒绝原因 */
92
+ rejectReason?: string;
93
+ /** 是否发生了降级 */
94
+ downgraded?: boolean;
95
+ /** 降级说明 */
96
+ downgradeNote?: string;
97
+ /** 错误信息 */
98
+ error?: string;
99
+ }
100
+ /**
101
+ * 公共媒体上传+发送流程
102
+ *
103
+ * 统一处理:resolveMediaFile → detectType → sizeCheck → uploadMedia → sendMediaMessage
104
+ * 媒体消息统一走 aibot_send_msg 主动发送,避免多文件场景下 reqId 只能用一次的问题。
105
+ * channel.ts 的 sendMedia 和 monitor.ts 的 deliver 回调都使用此函数。
106
+ */
107
+ export declare function uploadAndSendMedia(options: UploadAndSendMediaOptions): Promise<UploadAndSendMediaResult>;
108
+ /** uploadAndReplyMedia 的参数 */
109
+ export interface UploadAndReplyMediaOptions {
110
+ /** WSClient 实例 */
111
+ wsClient: WSClient;
112
+ /** 媒体文件的 URL 或本地路径 */
113
+ mediaUrl: string;
114
+ /** 原始 WebSocket 帧(用于 aibot_respond_msg 被动回复,携带 req_id) */
115
+ frame: WsFrameHeaders;
116
+ /** 允许读取本地文件的安全白名单目录 */
117
+ mediaLocalRoots?: readonly string[];
118
+ /** 日志函数 */
119
+ log?: (...args: any[]) => void;
120
+ /** 错误日志函数 */
121
+ errorLog?: (...args: any[]) => void;
122
+ }
123
+ /**
124
+ * 被动回复媒体上传+发送流程
125
+ *
126
+ * 统一处理:resolveMediaFile → detectType → sizeCheck → uploadMedia → replyMedia
127
+ * 通过 aibot_respond_msg 被动回复通道发送媒体消息,可以覆盖之前的 THINKING_MESSAGE。
128
+ *
129
+ * 适用场景:回包只有媒体没有文本时,第一个媒体文件用此方法发送以清理 thinking 状态。
130
+ */
131
+ export declare function uploadAndReplyMedia(options: UploadAndReplyMediaOptions): Promise<UploadAndSendMediaResult>;
@@ -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;