OpenClaw 完整备份 - 2026-03-21

This commit is contained in:
huan
2026-03-21 15:31:06 +08:00
commit 8dd73a1d62
569 changed files with 76792 additions and 0 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,11 @@
import { OpenClawPluginApi } from 'openclaw/plugin-sdk';
declare const plugin: {
id: string;
name: string;
description: string;
configSchema: any;
register(api: OpenClawPluginApi): void;
};
export { plugin as default };
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
import { type ChannelPlugin } from "openclaw/plugin-sdk";
import type { ResolvedWeComAccount } from "./utils.js";
export declare const wecomPlugin: ChannelPlugin<ResolvedWeComAccount>;
@@ -0,0 +1,66 @@
/**
* 企业微信渠道常量定义
*/
/**
* 企业微信渠道 ID
*/
export declare const CHANNEL_ID: "wecom";
/**
* 企业微信 WebSocket 命令枚举
*/
export declare enum WeComCommand {
/** 认证订阅 */
SUBSCRIBE = "aibot_subscribe",
/** 心跳 */
PING = "ping",
/** 企业微信推送消息 */
AIBOT_CALLBACK = "aibot_callback",
/** clawdbot 响应消息 */
AIBOT_RESPONSE = "aibot_response"
}
/** 图片下载超时时间(毫秒) */
export declare const IMAGE_DOWNLOAD_TIMEOUT_MS = 30000;
/** 文件下载超时时间(毫秒) */
export declare const FILE_DOWNLOAD_TIMEOUT_MS = 60000;
/** 消息发送超时时间(毫秒) */
export declare const REPLY_SEND_TIMEOUT_MS = 15000;
/** 消息处理总超时时间(毫秒) */
export declare const MESSAGE_PROCESS_TIMEOUT_MS: number;
/** WebSocket 心跳间隔(毫秒) */
export declare const WS_HEARTBEAT_INTERVAL_MS = 30000;
/** WebSocket 最大重连次数 */
export declare const WS_MAX_RECONNECT_ATTEMPTS = 100;
/** messageStates Map 条目的最大 TTL(毫秒),防止内存泄漏 */
export declare const MESSAGE_STATE_TTL_MS: number;
/** messageStates Map 清理间隔(毫秒) */
export declare const MESSAGE_STATE_CLEANUP_INTERVAL_MS = 60000;
/** messageStates Map 最大条目数 */
export declare const MESSAGE_STATE_MAX_SIZE = 500;
/** "思考中"流式消息占位内容 */
export declare const THINKING_MESSAGE = "<think></think>";
/** 仅包含图片时的消息占位符 */
export declare const MEDIA_IMAGE_PLACEHOLDER = "<media:image>";
/** 仅包含文件时的消息占位符 */
export declare const MEDIA_DOCUMENT_PLACEHOLDER = "<media:document>";
/** 获取 MCP 配置的 WebSocket 命令 */
export declare const MCP_GET_CONFIG_CMD = "aibot_get_mcp_config";
/** MCP 配置拉取超时时间(毫秒) */
export declare const MCP_CONFIG_FETCH_TIMEOUT_MS = 15000;
export declare const PLUGIN_JSON_FILENAME = "openclaw.plugin.json";
export declare const PLUGIN_JSON_PATH: string;
/** 默认媒体大小上限(MB */
export declare const DEFAULT_MEDIA_MAX_MB = 5;
/** 文本分块大小上限 */
export declare const TEXT_CHUNK_LIMIT = 4000;
/** 图片大小上限(字节):10MB */
export declare const IMAGE_MAX_BYTES: number;
/** 视频大小上限(字节):10MB */
export declare const VIDEO_MAX_BYTES: number;
/** 语音大小上限(字节):2MB */
export declare const VOICE_MAX_BYTES: number;
/** 文件大小上限(字节):20MB */
export declare const FILE_MAX_BYTES: number;
/** 文件绝对上限(字节):超过此值无法发送,等于 FILE_MAX_BYTES */
export declare const ABSOLUTE_MAX_BYTES: number;
/** 上传分片大小(字节,Base64 编码前):512KB */
export declare const UPLOAD_CHUNK_SIZE: number;
@@ -0,0 +1,29 @@
/**
* 企业微信 DM(私聊)访问控制模块
*
* 负责私聊策略检查、配对流程
*/
import type { RuntimeEnv } from "openclaw/plugin-sdk";
import type { WSClient, WsFrame } from "@wecom/aibot-node-sdk";
import type { ResolvedWeComAccount } from "./utils.js";
/**
* DM Policy 检查结果
*/
export interface DmPolicyCheckResult {
/** 是否允许继续处理消息 */
allowed: boolean;
/** 是否已发送配对消息(仅在 pairing 模式下) */
pairingSent?: boolean;
}
/**
* 检查 DM Policy 访问控制
* @returns 检查结果,包含是否允许继续处理
*/
export declare function checkDmPolicy(params: {
senderId: string;
isGroup: boolean;
account: ResolvedWeComAccount;
wsClient: WSClient;
frame: WsFrame;
runtime: RuntimeEnv;
}): Promise<DmPolicyCheckResult>;
@@ -0,0 +1,29 @@
/**
* 企业微信群组访问控制模块
*
* 负责群组策略检查(groupPolicy、群组白名单、群内发送者白名单)
*/
import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk";
import type { ResolvedWeComAccount } from "./utils.js";
/**
* 群组策略检查结果
*/
export interface GroupPolicyCheckResult {
/** 是否允许继续处理消息 */
allowed: boolean;
}
/**
* 检查群组策略访问控制
* @returns 检查结果,包含是否允许继续处理
*/
export declare function checkGroupPolicy(params: {
chatId: string;
senderId: string;
account: ResolvedWeComAccount;
config: OpenClawConfig;
runtime: RuntimeEnv;
}): GroupPolicyCheckResult;
/**
* 检查发送者是否在允许列表中(通用)
*/
export declare function isSenderAllowed(senderId: string, allowFrom: string[]): boolean;
@@ -0,0 +1,167 @@
/**
* 企业微信渠道类型定义
*/
import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk";
import type { ResolvedWeComAccount } from "./utils.js";
import { WeComCommand } from "./const.js";
/**
* Monitor 配置选项
*/
export type WeComMonitorOptions = {
account: ResolvedWeComAccount;
config: OpenClawConfig;
runtime: RuntimeEnv;
abortSignal?: AbortSignal;
};
/**
* 消息状态
*/
export interface MessageState {
accumulatedText: string;
/** 流式回复的 streamId,用于保持同一个流式回复使用相同的 streamId */
streamId?: string;
/** 是否有用户可见的文本内容(不包括 <think>...</think> 标签) */
hasText?: boolean;
/** 是否已成功发送过媒体文件 */
hasMedia?: boolean;
/** 是否有媒体发送失败(权限不足、文件过大等) */
hasMediaFailed?: boolean;
/** 媒体发送失败时的纯文本错误摘要(用于替换 thinking 流展示给用户) */
mediaErrorSummary?: string;
/** deliver 回调是否被调用过(用于区分"核心无回复"和"核心回复了空内容" */
deliverCalled?: boolean;
}
/**
* MCP 配置响应体
*/
export interface McpConfigBody {
/** MCP Server 的 StreamableHttp URL */
url: string;
/** 连接类型,如 "streamable-http" */
type?: string;
/** 是否已授权 */
is_authed?: boolean;
/** mcp业务类型 */
biz_type?: string;
}
/**
* WebSocket 请求消息基础格式
*/
export interface WeComRequest {
cmd: string;
headers: {
req_id: string;
};
body: any;
}
/**
* WebSocket 响应消息格式
*/
export interface WeComResponse {
headers: {
req_id: string;
};
errcode: number;
errmsg: string;
}
/**
* 企业微信认证请求
*/
export interface WeComSubscribeRequest extends WeComRequest {
cmd: WeComCommand.SUBSCRIBE;
body: {
secret: string;
bot_id: string;
};
}
/**
* 企业微信推送消息格式
*/
export interface WeComCallbackMessage {
cmd: WeComCommand.AIBOT_CALLBACK;
headers: {
req_id: string;
};
body: {
msgid: string;
aibotid: string;
chatid: string;
chattype: "single" | "group";
from: {
userid: string;
};
response_url: string;
msgtype: "text" | "image" | "voice" | "video" | "file" | "stream" | "mixed";
text?: {
content: string;
};
image?: {
/** 图片 URL(通过 URL 方式接收图片时) */
url?: string;
/** 图片 base64 数据(直接传输时) */
base64?: string;
md5?: string;
};
/** 图文混排消息 */
mixed?: {
msg_item: Array<{
msgtype: "text" | "image";
text?: {
content: string;
};
image?: {
url?: string;
base64?: string;
md5?: string;
};
}>;
};
quote?: {
msgtype: string;
text?: {
content: string;
};
image?: {
url?: string;
aeskey?: string;
};
file?: {
url?: string;
aeskey?: string;
};
};
stream?: {
id: string;
};
};
}
/**
* 企业微信响应消息格式
*/
export interface WeComResponseMessage extends WeComRequest {
cmd: WeComCommand.AIBOT_RESPONSE;
body: {
msgtype: "stream" | "text" | "markdown";
stream?: {
id: string;
finish: boolean;
content: string;
msg_item?: Array<{
msgtype: "image" | "file";
image?: {
base64: string;
md5: string;
};
}>;
feedback?: {
id: string;
};
};
text?: {
content: string;
};
markdown?: {
content: string;
};
};
}
@@ -0,0 +1,29 @@
/**
* MCP 配置拉取与持久化模块
*
* 负责:
* - 通过 WSClient 发送 aibot_get_mcp_config 请求
* - 解析服务端响应,提取 MCP 配置(url、type、is_authed)
* - 将配置写入 openclaw.plugin.json 的 wecomMcp 字段
*/
import { type WSClient } from "@wecom/aibot-node-sdk";
import type { RuntimeEnv } from "openclaw/plugin-sdk";
import type { McpConfigBody } from "./interface.js";
/**
* 通过 WSClient 发送 aibot_get_mcp_config 命令,获取 MCP 配置
*
* @param wsClient - 已认证的 WSClient 实例
* @returns MCP 配置(url、type、is_authed)
* @throws 响应错误码非 0 或缺少 url 字段时抛出错误
*/
export declare function fetchMcpConfig(wsClient: WSClient): Promise<McpConfigBody>;
/**
* 拉取 MCP 配置并持久化到 openclaw.plugin.json
*
* 认证成功后调用。失败仅记录日志,不影响 WebSocket 消息正常收发。
*
* @param wsClient - 已认证的 WSClient 实例
* @param accountId - 账户 ID(用于日志)
* @param runtime - 运行时环境(用于日志)
*/
export declare function fetchAndSaveMcpConfig(wsClient: WSClient, accountId: string, runtime: RuntimeEnv): Promise<void>;
@@ -0,0 +1,36 @@
/**
* 企业微信媒体(图片)下载和保存模块
*
* 负责下载、检测格式、保存图片到本地,包含超时保护
*/
import type { OpenClawConfig, RuntimeEnv } from "openclaw/plugin-sdk";
import type { WSClient } from "@wecom/aibot-node-sdk";
import type { ResolvedWeComAccount } from "./utils.js";
/**
* 下载并保存所有图片到本地,每张图片的下载带超时保护
*/
export declare function downloadAndSaveImages(params: {
imageUrls: string[];
imageAesKeys?: Map<string, string>;
account: ResolvedWeComAccount;
config: OpenClawConfig;
runtime: RuntimeEnv;
wsClient: WSClient;
}): Promise<Array<{
path: string;
contentType?: string;
}>>;
/**
* 下载并保存所有文件到本地,每个文件的下载带超时保护
*/
export declare function downloadAndSaveFiles(params: {
fileUrls: string[];
fileAesKeys?: Map<string, string>;
account: ResolvedWeComAccount;
config: OpenClawConfig;
runtime: RuntimeEnv;
wsClient: WSClient;
}): Promise<Array<{
path: string;
contentType?: string;
}>>;
@@ -0,0 +1,131 @@
/**
* 企业微信出站媒体上传工具模块
*
* 负责:
* - 从 mediaUrl 加载文件 buffer(远程 URL 或本地路径均支持)
* - 检测 MIME 类型并映射为企微媒体类型
* - 文件大小检查与降级策略
*/
import type { WeComMediaType, WSClient, WsFrameHeaders } from "@wecom/aibot-node-sdk";
/** 媒体文件解析结果 */
export interface ResolvedMedia {
/** 文件数据 */
buffer: Buffer;
/** 检测到的 MIME 类型 */
contentType: string;
/** 文件名(从 URL 提取或默认生成) */
fileName: string;
}
/** 文件大小检查结果 */
export interface FileSizeCheckResult {
/** 最终确定的企微媒体类型(可能被降级) */
finalType: WeComMediaType;
/** 是否需要拒绝(超过绝对限制) */
shouldReject: boolean;
/** 拒绝原因(仅 shouldReject=true 时有值) */
rejectReason?: string;
/** 是否发生了降级 */
downgraded: boolean;
/** 降级说明(仅 downgraded=true 时有值) */
downgradeNote?: string;
}
/**
* 根据 MIME 类型检测企微媒体类型
*
* @param mimeType - MIME 类型字符串
* @returns 企微媒体类型
*/
export declare function detectWeComMediaType(mimeType: string): WeComMediaType;
/**
* 从 mediaUrl 加载媒体文件
*
* 支持远程 URLhttp/https)和本地路径(file:// 或绝对路径),
* 利用 openclaw plugin-sdk 的 loadOutboundMediaFromUrl 统一处理。
*
* @param mediaUrl - 媒体文件的 URL 或本地路径
* @param mediaLocalRoots - 允许读取本地文件的安全白名单目录
* @returns 解析后的媒体文件信息
*/
export declare function resolveMediaFile(mediaUrl: string, mediaLocalRoots?: readonly string[]): Promise<ResolvedMedia>;
/**
* 检查文件大小并执行降级策略
*
* 降级规则:
* - voice 非 AMR 格式 → 降级为 file(企微后台仅支持 AMR)
* - image 超过 10MB → 降级为 file
* - video 超过 10MB → 降级为 file
* - voice 超过 2MB → 降级为 file
* - file 超过 20MB → 拒绝发送
*
* @param fileSize - 文件大小(字节)
* @param detectedType - 检测到的企微媒体类型
* @param contentType - 文件的 MIME 类型(用于语音格式校验)
* @returns 大小检查结果
*/
export declare function applyFileSizeLimits(fileSize: number, detectedType: WeComMediaType, contentType?: string): FileSizeCheckResult;
/** uploadAndSendMedia 的参数 */
export interface UploadAndSendMediaOptions {
/** WSClient 实例 */
wsClient: WSClient;
/** 媒体文件的 URL 或本地路径 */
mediaUrl: string;
/** 目标会话 ID(用于 aibot_send_msg 主动发送) */
chatId: string;
/** 允许读取本地文件的安全白名单目录 */
mediaLocalRoots?: readonly string[];
/** 日志函数 */
log?: (...args: any[]) => void;
/** 错误日志函数 */
errorLog?: (...args: any[]) => void;
}
/** uploadAndSendMedia 的返回结果 */
export interface UploadAndSendMediaResult {
/** 是否发送成功 */
ok: boolean;
/** 发送后返回的 messageId */
messageId?: string;
/** 最终的企微媒体类型 */
finalType?: WeComMediaType;
/** 是否被拒绝(文件过大) */
rejected?: boolean;
/** 拒绝原因 */
rejectReason?: string;
/** 是否发生了降级 */
downgraded?: boolean;
/** 降级说明 */
downgradeNote?: string;
/** 错误信息 */
error?: string;
}
/**
* 公共媒体上传+发送流程
*
* 统一处理:resolveMediaFile → detectType → sizeCheck → uploadMedia → sendMediaMessage
* 媒体消息统一走 aibot_send_msg 主动发送,避免多文件场景下 reqId 只能用一次的问题。
* channel.ts 的 sendMedia 和 monitor.ts 的 deliver 回调都使用此函数。
*/
export declare function uploadAndSendMedia(options: UploadAndSendMediaOptions): Promise<UploadAndSendMediaResult>;
/** uploadAndReplyMedia 的参数 */
export interface UploadAndReplyMediaOptions {
/** WSClient 实例 */
wsClient: WSClient;
/** 媒体文件的 URL 或本地路径 */
mediaUrl: string;
/** 原始 WebSocket 帧(用于 aibot_respond_msg 被动回复,携带 req_id */
frame: WsFrameHeaders;
/** 允许读取本地文件的安全白名单目录 */
mediaLocalRoots?: readonly string[];
/** 日志函数 */
log?: (...args: any[]) => void;
/** 错误日志函数 */
errorLog?: (...args: any[]) => void;
}
/**
* 被动回复媒体上传+发送流程
*
* 统一处理:resolveMediaFile → detectType → sizeCheck → uploadMedia → replyMedia
* 通过 aibot_respond_msg 被动回复通道发送媒体消息,可以覆盖之前的 THINKING_MESSAGE。
*
* 适用场景:回包只有媒体没有文本时,第一个媒体文件用此方法发送以清理 thinking 状态。
*/
export declare function uploadAndReplyMedia(options: UploadAndReplyMediaOptions): Promise<UploadAndSendMediaResult>;
@@ -0,0 +1,72 @@
/**
* 企业微信消息内容解析模块
*
* 负责从 WsFrame 中提取文本、图片、引用等内容
*/
export interface MessageBody {
msgid: string;
aibotid?: string;
chatid?: string;
chattype: "single" | "group";
from: {
userid: string;
};
response_url?: string;
msgtype: string;
text?: {
content: string;
};
image?: {
url?: string;
aeskey?: string;
};
voice?: {
content?: string;
};
mixed?: {
msg_item: Array<{
msgtype: "text" | "image";
text?: {
content: string;
};
image?: {
url?: string;
aeskey?: string;
};
}>;
};
file?: {
url?: string;
aeskey?: string;
};
quote?: {
msgtype: string;
text?: {
content: string;
};
voice?: {
content: string;
};
image?: {
url?: string;
aeskey?: string;
};
file?: {
url?: string;
aeskey?: string;
};
};
}
export interface ParsedMessageContent {
textParts: string[];
imageUrls: string[];
imageAesKeys: Map<string, string>;
fileUrls: string[];
fileAesKeys: Map<string, string>;
quoteContent: string | undefined;
}
/**
* 解析消息内容(支持单条消息、图文混排和引用消息)
* @returns 提取的文本数组、图片URL数组和引用消息内容
*/
export declare function parseMessageContent(body: MessageBody): ParsedMessageContent;
@@ -0,0 +1,23 @@
/**
* 企业微信消息发送模块
*
* 负责通过 WSClient 发送回复消息,包含超时保护
*/
import type { RuntimeEnv } from "openclaw/plugin-sdk";
import { type WSClient, type WsFrame } from "@wecom/aibot-node-sdk";
/**
* 发送企业微信回复消息
* 供 monitor 内部和 channel outbound 使用
*
* @returns messageId (streamId)
*/
export declare function sendWeComReply(params: {
wsClient: WSClient;
frame: WsFrame;
text?: string;
runtime: RuntimeEnv;
/** 是否为流式回复的最终消息,默认为 true */
finish?: boolean;
/** 指定 streamId,用于流式回复时保持相同的 streamId */
streamId?: string;
}): Promise<string>;
@@ -0,0 +1,27 @@
/**
* 企业微信 WebSocket 监控器主模块
*
* 负责:
* - 建立和管理 WebSocket 连接
* - 协调消息处理流程(解析→策略检查→下载图片→路由回复)
* - 资源生命周期管理
*
* 子模块:
* - message-parser.ts : 消息内容解析
* - message-sender.ts : 消息发送(带超时保护)
* - media-handler.ts : 图片下载和保存(带超时保护)
* - group-policy.ts : 群组访问控制
* - dm-policy.ts : 私聊访问控制
* - state-manager.ts : 全局状态管理(带 TTL 清理)
* - timeout.ts : 超时工具
*/
import type { WeComMonitorOptions } from "./interface.js";
export type { WeComMonitorOptions } from "./interface.js";
export { WeComCommand } from "./const.js";
export { getWeComWebSocket, setReqIdForChat, getReqIdForChatAsync, getReqIdForChat, deleteReqIdForChat, warmupReqIdStore, flushReqIdStore, } from "./state-manager.js";
export { sendWeComReply } from "./message-sender.js";
/**
* 监听企业微信 WebSocket 连接
* 使用 aibot-node-sdk 简化连接管理
*/
export declare function monitorWeComProvider(options: WeComMonitorOptions): Promise<void>;
@@ -0,0 +1,5 @@
/**
* 企业微信 onboarding adapter for CLI setup wizard.
*/
import { type ChannelOnboardingAdapter } from "openclaw/plugin-sdk";
export declare const wecomOnboardingAdapter: ChannelOnboardingAdapter;
@@ -0,0 +1,48 @@
/**
* openclaw plugin-sdk 高版本方法兼容层
*
* 部分方法(如 loadOutboundMediaFromUrl、detectMime、getDefaultMediaLocalRoots
* 仅在较新版本的 openclaw plugin-sdk 中才导出。
*
* 本模块在加载时一次性探测 SDK 导出,存在则直接 re-export SDK 版本,
* 不存在则导出 fallback 实现。其他模块统一从本文件导入,无需关心底层兼容细节。
*/
/** 与 openclaw plugin-sdk 中 WebMediaResult 兼容的类型 */
export type WebMediaResult = {
buffer: Buffer;
contentType?: string;
kind?: string;
fileName?: string;
};
export type OutboundMediaLoadOptions = {
maxBytes?: number;
mediaLocalRoots?: readonly string[];
};
export type DetectMimeOptions = {
buffer?: Buffer;
headerMime?: string | null;
filePath?: string;
};
/**
* 检测 MIME 类型(兼容入口)
*
* 支持两种调用签名以兼容不同使用场景:
* - detectMime(buffer) → 旧式调用
* - detectMime({ buffer, headerMime, filePath }) → 完整参数
*
* 优先使用 SDK 版本,不可用时使用 fallback。
*/
export declare function detectMime(bufferOrOpts: Buffer | DetectMimeOptions): Promise<string | undefined>;
/**
* 从 URL 或本地路径加载媒体文件(兼容入口)
*
* 优先使用 SDK 版本,不可用时使用 fallback。
* SDK 版本抛出的业务异常(如 LocalMediaAccessError)会直接透传。
*/
export declare function loadOutboundMediaFromUrl(mediaUrl: string, options?: OutboundMediaLoadOptions): Promise<WebMediaResult>;
/**
* 获取默认媒体本地路径白名单(兼容入口)
*
* 优先使用 SDK 版本,不可用时手动构建白名单(与 weclaw/src/media/local-roots.ts 逻辑一致)。
*/
export declare function getDefaultMediaLocalRoots(): Promise<readonly string[]>;
@@ -0,0 +1,31 @@
/** Store 配置 */
interface ReqIdStoreOptions {
/** TTL 毫秒数,超时的 reqId 视为过期(默认 24 小时) */
ttlMs?: number;
/** 内存最大条目数(默认 200) */
memoryMaxSize?: number;
/** 磁盘最大条目数(默认 500) */
fileMaxEntries?: number;
/** 磁盘写入防抖时间(毫秒),默认 1000ms */
flushDebounceMs?: number;
}
export interface PersistentReqIdStore {
/** 设置 chatId 对应的 reqId(写入内存 + 防抖写磁盘) */
set(chatId: string, reqId: string): void;
/** 获取 chatId 对应的 reqId(异步:优先内存,miss 时查磁盘并回填内存) */
get(chatId: string): Promise<string | undefined>;
/** 同步获取 chatId 对应的 reqId(仅内存) */
getSync(chatId: string): string | undefined;
/** 删除 chatId 对应的 reqId */
delete(chatId: string): void;
/** 启动时从磁盘预热内存,返回加载条目数 */
warmup(onError?: (error: unknown) => void): Promise<number>;
/** 立即将内存数据刷写到磁盘(用于优雅退出) */
flush(): Promise<void>;
/** 清空内存缓存 */
clearMemory(): void;
/** 返回内存中的条目数 */
memorySize(): number;
}
export declare function createPersistentReqIdStore(accountId: string, options?: ReqIdStoreOptions): PersistentReqIdStore;
export {};
@@ -0,0 +1,3 @@
import type { PluginRuntime } from "openclaw/plugin-sdk";
export declare function setWeComRuntime(r: PluginRuntime): void;
export declare function getWeComRuntime(): PluginRuntime;
@@ -0,0 +1,76 @@
/**
* 企业微信全局状态管理模块
*
* 负责管理 WSClient 实例、消息状态(带 TTL 清理)、ReqId 存储
* 解决全局 Map 的内存泄漏问题
*/
import type { WSClient } from "@wecom/aibot-node-sdk";
import type { MessageState } from "./interface.js";
/**
* 获取指定账户的 WSClient 实例
*/
export declare function getWeComWebSocket(accountId: string): WSClient | null;
/**
* 设置指定账户的 WSClient 实例
*/
export declare function setWeComWebSocket(accountId: string, client: WSClient): void;
/**
* 删除指定账户的 WSClient 实例
*/
export declare function deleteWeComWebSocket(accountId: string): void;
/**
* 启动消息状态定期清理(自动 TTL 清理 + 容量限制)
*/
export declare function startMessageStateCleanup(): void;
/**
* 停止消息状态定期清理
*/
export declare function stopMessageStateCleanup(): void;
/**
* 设置消息状态
*/
export declare function setMessageState(messageId: string, state: MessageState): void;
/**
* 获取消息状态
*/
export declare function getMessageState(messageId: string): MessageState | undefined;
/**
* 删除消息状态
*/
export declare function deleteMessageState(messageId: string): void;
/**
* 清空所有消息状态
*/
export declare function clearAllMessageStates(): void;
/**
* 设置 chatId 对应的 reqId(写入内存 + 防抖写磁盘)
*/
export declare function setReqIdForChat(chatId: string, reqId: string, accountId?: string): void;
/**
* 获取 chatId 对应的 reqId(异步:优先内存,miss 时查磁盘并回填内存)
*/
export declare function getReqIdForChatAsync(chatId: string, accountId?: string): Promise<string | undefined>;
/**
* 获取 chatId 对应的 reqId(同步:仅内存,保留向后兼容)
*/
export declare function getReqIdForChat(chatId: string, accountId?: string): string | undefined;
/**
* 删除 chatId 对应的 reqId
*/
export declare function deleteReqIdForChat(chatId: string, accountId?: string): void;
/**
* 启动时预热 reqId 缓存(从磁盘加载到内存)
*/
export declare function warmupReqIdStore(accountId?: string, log?: (...args: unknown[]) => void): Promise<number>;
/**
* 立即将 reqId 数据刷写到磁盘(用于优雅退出)
*/
export declare function flushReqIdStore(accountId?: string): Promise<void>;
/**
* 清理指定账户的所有资源
*/
export declare function cleanupAccount(accountId: string): Promise<void>;
/**
* 清理所有资源(用于进程退出)
*/
export declare function cleanupAll(): Promise<void>;
@@ -0,0 +1,20 @@
/**
* 超时控制工具模块
*
* 为异步操作提供统一的超时保护机制
*/
/**
* 为 Promise 添加超时保护
*
* @param promise - 原始 Promise
* @param timeoutMs - 超时时间(毫秒)
* @param message - 超时错误消息
* @returns 带超时保护的 Promise
*/
export declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number, message?: string): Promise<T>;
/**
* 超时错误类型
*/
export declare class TimeoutError extends Error {
constructor(message: string);
}
@@ -0,0 +1,53 @@
/**
* 企业微信公共工具函数
*/
import type { OpenClawConfig } from "openclaw/plugin-sdk";
/**
* 企业微信群组配置
*/
export interface WeComGroupConfig {
/** 群组内发送者白名单(仅列表中的成员消息会被处理) */
allowFrom?: Array<string | number>;
}
/**
* 企业微信配置类型
*/
export interface WeComConfig {
enabled?: boolean;
websocketUrl?: string;
botId?: string;
secret?: string;
name?: string;
allowFrom?: Array<string | number>;
dmPolicy?: "open" | "allowlist" | "pairing" | "disabled";
/** 群组访问策略:"open" = 允许所有群组(默认),"allowlist" = 仅允许 groupAllowFrom 中的群组,"disabled" = 禁用群组消息 */
groupPolicy?: "open" | "allowlist" | "disabled";
/** 群组白名单(仅 groupPolicy="allowlist" 时生效) */
groupAllowFrom?: Array<string | number>;
/** 每个群组的详细配置(如群组内发送者白名单) */
groups?: Record<string, WeComGroupConfig>;
/** 是否发送"思考中"消息,默认为 true */
sendThinkingMessage?: boolean;
/** 额外允许访问的本地媒体路径白名单(支持 ~ 表示 home 目录),如 ["~/Downloads", "~/Documents"] */
mediaLocalRoots?: string[];
}
export declare const DefaultWsUrl = "wss://openws.work.weixin.qq.com";
export interface ResolvedWeComAccount {
accountId: string;
name: string;
enabled: boolean;
websocketUrl: string;
botId: string;
secret: string;
/** 是否发送"思考中"消息,默认为 true */
sendThinkingMessage: boolean;
config: WeComConfig;
}
/**
* 解析企业微信账户配置
*/
export declare function resolveWeComAccount(cfg: OpenClawConfig): ResolvedWeComAccount;
/**
* 设置企业微信账户配置
*/
export declare function setWeComAccount(cfg: OpenClawConfig, account: Partial<WeComConfig>): OpenClawConfig;