diff --git a/electron/main.js b/electron/main.js
index 5accc26..71da660 100644
--- a/electron/main.js
+++ b/electron/main.js
@@ -1,17 +1,356 @@
- this.socket.on('open', () => {
- console.log('[OpenClaw] ✅ WebSocket 连接已建立');
- console.log('[OpenClaw] 就绪状态:', this.socket.readyState, '(1=OPEN)');
+const { app, BrowserWindow, ipcMain, dialog, Tray, Menu, nativeImage } = require('electron');
+const path = require('path');
+const fs = require('fs');
+const Store = require('electron-store');
+const { v4: uuidv4 } = require('uuid');
+const { WSClient } = require('@wecom/aibot-node-sdk');
+const WebSocket = require('ws');
+
+// 初始化配置存储
+const store = new Store({
+ name: 'config',
+ defaults: {
+ bots: [], // 多 Bot 配置 [{id, botId, secret, name, enabled}]
+ openclaw: {
+ url: 'ws://localhost:18789',
+ token: '',
+ enabled: true
+ }
+ }
+});
+
+let mainWindow;
+let wecomConnections = new Map(); // botId -> WeComConnection 实例
+let openclawConnection = null; // OpenClawConnection 实例
+let messageState = new Map(); // msgId -> { streamId, accumulatedText, frame, botId }
+let reqIdMap = new Map(); // chatId -> reqId (用于回复消息)
+
+// 创建主窗口
+function createWindow() {
+ mainWindow = new BrowserWindow({
+ width: 1200,
+ height: 800,
+ webPreferences: {
+ nodeIntegration: false,
+ contextIsolation: true,
+ preload: path.join(__dirname, 'preload.js')
+ },
+ icon: path.join(__dirname, '../resources/icon.png')
+ });
+
+ const isDev = process.env.NODE_ENV === 'development';
+ if (isDev) {
+ mainWindow.loadURL('http://localhost:3000');
+ mainWindow.webContents.openDevTools();
+ } else {
+ mainWindow.loadFile(path.join(__dirname, '../renderer/build/index.html'));
+ }
+
+ mainWindow.on('closed', () => {
+ mainWindow = null;
+ });
+}
+
+// 创建系统托盘
+function createTray() {
+ const iconPath = path.join(__dirname, '../resources/icon.png');
+ const trayIcon = nativeImage.createFromPath(iconPath).resize({ width: 16, height: 16 });
+ const tray = new Tray(trayIcon);
+
+ const contextMenu = Menu.buildFromTemplate([
+ { label: '显示主窗口', click: () => mainWindow.show() },
+ { label: '退出', click: () => app.quit() }
+ ]);
+
+ tray.setToolTip('WeCom OpenClaw Client');
+ tray.setContextMenu(contextMenu);
+
+ return tray;
+}
+
+// 生成唯一的请求 ID
+function generateReqId(prefix = 'msg') {
+ return `${prefix}_${uuidv4().substring(0, 8)}_${Date.now()}`;
+}
+
+// 企业微信 WebSocket 连接管理
+class WeComConnection {
+ constructor(botConfig, eventHandler) {
+ this.botId = botConfig.botId;
+ this.secret = botConfig.secret;
+ this.accountId = botConfig.id || botConfig.botId;
+ this.eventHandler = eventHandler;
+ this.client = null;
+ this.isConnected = false;
+ this.reconnectAttempts = 0;
+ this.maxReconnectAttempts = 100;
+ }
+
+ async connect() {
+ try {
+ console.log(`[WeCom] Connecting bot ${this.botId}...`);
+
+ this.client = new WSClient({
+ botId: this.botId,
+ secret: this.secret,
+ wsUrl: 'wss://openws.work.weixin.qq.com',
+ logger: {
+ debug: (msg, ...args) => console.log(`[WeCom:${this.botId}] DEBUG:`, msg, ...args),
+ info: (msg, ...args) => console.log(`[WeCom:${this.botId}] INFO:`, msg, ...args),
+ warn: (msg, ...args) => console.warn(`[WeCom:${this.botId}] WARN:`, msg, ...args),
+ error: (msg, ...args) => console.error(`[WeCom:${this.botId}] ERROR:`, msg, ...args)
+ },
+ heartbeatInterval: 30000, // 30 秒心跳
+ maxReconnectAttempts: this.maxReconnectAttempts
+ });
+
+ // 监听连接事件
+ this.client.on('connected', () => {
this.isConnected = true;
this.reconnectAttempts = 0;
+ console.log(`[WeCom:${this.botId}] Connected`);
+ wecomConnections.set(this.botId, this); // 存储实例本身
+ // 检查窗口是否存在
+ if (mainWindow && !mainWindow.isDestroyed()) {
+ this.eventHandler('connected', { botId: this.botId });
+ }
+ });
+
+ // 监听认证成功
+ this.client.on('authenticated', () => {
+ console.log(`[WeCom:${this.botId}] Authenticated`);
+ });
+
+ // 监听断开
+ this.client.on('disconnected', (reason) => {
+ this.isConnected = false;
+ console.log(`[WeCom:${this.botId}] Disconnected: ${reason}`);
+ // 检查窗口是否存在
+ if (mainWindow && !mainWindow.isDestroyed()) {
+ this.eventHandler('disconnected', { botId: this.botId, reason });
+ }
+ this.scheduleReconnect();
+ });
+
+ // 监听错误
+ this.client.on('error', (error) => {
+ console.error(`[WeCom:${this.botId}] Error:`, error);
+ // 检查窗口是否存在
+ if (mainWindow && !mainWindow.isDestroyed()) {
+ this.eventHandler('error', { botId: this.botId, error: error.message });
+ }
+
+ // 认证失败时拒绝重连
+ if (error.message.includes('Authentication failed')) {
+ return;
+ }
+ });
+
+ // 监听消息 - 核心:接收企业微信消息并转发到 OpenClaw
+ this.client.on('message', async (frame) => {
+ console.log(`[WeCom:${this.botId}] Received message:`, JSON.stringify(frame, null, 2));
+ // 检查窗口是否存在
+ if (mainWindow && !mainWindow.isDestroyed()) {
+ this.eventHandler('message', { botId: this.botId, frame });
+ }
+
+ // 将消息转发到 OpenClaw
+ await this.forwardMessageToOpenClaw(frame);
+ });
+
+ // 连接
+ await this.client.connect();
+ } catch (error) {
+ console.error(`[WeCom:${this.botId}] Connect error:`, error);
+ this.eventHandler('error', { botId: this.botId, error: error.message });
+ this.scheduleReconnect();
+ }
+ }
+
+ // 转发消息到 OpenClaw
+ async forwardMessageToOpenClaw(frame) {
+ if (!openclawConnection || !openclawConnection.isConnected) {
+ console.log('[WeCom] OpenClaw not connected, skipping forward');
+ return;
+ }
+
+ const body = frame.body;
+ const chatId = body.chatid || body.from.userid;
+ const chatType = body.chattype === 'group' ? 'group' : 'direct';
+ const messageId = body.msgid;
+ const reqId = frame.headers.req_id;
+
+ // 保存 reqId 用于后续回复
+ reqIdMap.set(chatId, reqId);
+
+ // 构建转发到 OpenClaw 的消息
+ // 参考 /home/wecom 插件的消息处理逻辑
+ const text = this.extractTextFromFrame(frame);
+ const hasMedia = this.hasMediaInFrame(frame);
+
+ // 构建 OpenClaw 消息格式
+ const openclawMessage = {
+ type: 'event',
+ event: 'message.inbound',
+ payload: {
+ channel: 'wecom',
+ accountId: this.accountId,
+ message: {
+ id: messageId,
+ from: body.from.userid,
+ chatId: chatId,
+ chatType: chatType,
+ text: text,
+ hasMedia: hasMedia,
+ timestamp: body.create_time || Date.now()
+ },
+ reqId: reqId,
+ botId: this.botId
+ }
+ };
+
+ // 发送到 OpenClaw
+ openclawConnection.send(JSON.stringify(openclawMessage));
+ console.log(`[Forward] WeCom -> OpenClaw: ${messageId}`);
+ }
+
+ // 从 frame 中提取文本
+ extractTextFromFrame(frame) {
+ const body = frame.body;
+ let text = '';
+
+ if (body.text?.content) {
+ text = body.text.content;
+ } else if (body.mixed?.msg_item) {
+ // 图文混排
+ for (const item of body.mixed.msg_item) {
+ if (item.msgtype === 'text' && item.text?.content) {
+ text += item.text.content + '\n';
+ }
+ }
+ } else if (body.voice?.content) {
+ // 语音转文字
+ text = body.voice.content;
+ }
+
+ // 群聊中移除 @机器人 提及
+ if (body.chattype === 'group') {
+ text = text.replace(/@\S+/g, '').trim();
+ }
+
+ return text.trim();
+ }
+
+ // 检查是否有媒体
+ hasMediaInFrame(frame) {
+ const body = frame.body;
+ return !!(body.image?.url || body.file?.url ||
+ (body.mixed?.msg_item?.some(item => item.msgtype === 'image')));
+ }
+
+ scheduleReconnect() {
+ if (this.reconnectAttempts >= this.maxReconnectAttempts) {
+ console.error(`[WeCom:${this.botId}] Max reconnect attempts reached`);
+ return;
+ }
+
+ this.reconnectAttempts++;
+ const delay = Math.min(1000 * Math.pow(1.5, this.reconnectAttempts), 60000);
+ console.log(`[WeCom:${this.botId}] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
+
+ setTimeout(() => this.connect(), delay);
+ }
+
+ // 发送消息到企业微信
+ async sendMessage(chatId, content, finish = true, streamId = null) {
+ if (!this.client || !this.isConnected) {
+ throw new Error('Not connected');
+ }
+
+ const actualStreamId = streamId || generateReqId('stream');
+ const reqId = reqIdMap.get(chatId) || generateReqId('req');
+
+ // 使用流式回复
+ const response = {
+ cmd: 'aibot_respond_msg',
+ headers: {
+ req_id: reqId
+ },
+ body: {
+ msgtype: 'stream',
+ stream: {
+ id: actualStreamId,
+ finish: finish,
+ content: content
+ }
+ }
+ };
+
+ console.log(`[WeCom:${this.botId}] Sending message to ${chatId}:`, content.substring(0, 100));
+ await this.client.send(response);
+
+ return actualStreamId;
+ }
+
+ disconnect() {
+ if (this.client) {
+ this.client.close();
+ this.isConnected = false;
+ wecomConnections.delete(this.botId);
+ }
+ }
+}
+
+// OpenClaw Gateway WebSocket 连接
+class OpenClawConnection {
+ constructor(url, token, eventHandler) {
+ // 清理 URL - 移除末尾斜杠
+ this.url = url?.trim().replace(/\/+$/, '') || 'ws://localhost:18789';
+ this.token = token;
+ this.eventHandler = eventHandler;
+ this.socket = null;
+ this.isConnected = false;
+ this.reconnectAttempts = 0;
+ this.maxReconnectAttempts = 100;
+ this.messageId = 0;
+ this.protocolVersion = 3;
+ this.deviceId = `wecome-client_${process.platform}_${uuidv4()}`;
+ }
+
+ async connect() {
+ try {
+ console.log('[OpenClaw] Connecting to', this.url);
+
+ const isSecure = this.url.startsWith('wss://');
+ console.log(`[OpenClaw] Using ${isSecure ? 'WSS (SSL)' : 'WS (non-SSL)'} connection`);
+
+ // 创建 WebSocket 配置
+ const wsOptions = {
+ rejectUnauthorized: false, // 允许自签名证书
+ followRedirects: true
+ };
+
+ // 如果是 wss://,添加更多 SSL 选项
+ if (isSecure) {
+ wsOptions.tls = {
+ rejectUnauthorized: false,
+ minVersion: 'TLSv1.2'
+ };
+ }
+
+ this.socket = new WebSocket(this.url, wsOptions);
+
+ this.socket.on('open', () => {
+ this.isConnected = true;
+ this.reconnectAttempts = 0;
+ console.log('[OpenClaw] WebSocket connected, waiting for challenge...');
// 检查窗口是否存在
if (mainWindow && !mainWindow.isDestroyed()) {
this.eventHandler('connected');
}
- // 主动发送 connect 请求(兼容模式:不等待 challenge)
- console.log('[OpenClaw] 📤 准备发送 connect 请求...');
- this.sendConnect();
+ // 注意:不立即发送 connect,等待服务器发送 connect.challenge 后再响应
});
this.socket.on('message', (data) => {
@@ -20,91 +359,42 @@
return;
}
try {
- const messageStr = data.toString();
- console.log('[OpenClaw] 📥 收到消息 (长度:', messageStr.length, '字节)');
- console.log('[OpenClaw] 原始数据:', messageStr.substring(0, 500) + (messageStr.length > 500 ? '...' : ''));
-
- const message = JSON.parse(messageStr);
- console.log('[OpenClaw] 消息类型:', message.type);
-
- if (message.type === 'event') {
- console.log('[OpenClaw] 事件名称:', message.event);
- } else if (message.type === 'res') {
- console.log('[OpenClaw] 响应 ID:', message.id);
- console.log('[OpenClaw] 响应状态:', message.ok ? '✅ 成功' : '❌ 失败');
- if (!message.ok) {
- console.log('[OpenClaw] 错误信息:', message.error);
- }
- }
+ const message = JSON.parse(data.toString());
+ console.log('[OpenClaw] Received:', JSON.stringify(message, null, 2));
// 处理 connect.challenge 质询
if (message.type === 'event' && message.event === 'connect.challenge') {
- console.log('[OpenClaw] 🔐 收到 challenge 质询');
- console.log('[OpenClaw] Nonce:', message.payload?.nonce);
- console.log('[OpenClaw] 发送 connect 请求响应 challenge...');
+ console.log('[OpenClaw] Received challenge, sending connect request...');
this.sendConnect(message.payload?.nonce);
return;
}
this.handleMessage(message);
} catch (error) {
- console.error('[OpenClaw] ❌ 解析消息错误:', error.message);
- console.error('[OpenClaw] 原始数据:', data.toString().substring(0, 200));
+ console.error('[OpenClaw] Parse error:', error);
}
});
- this.socket.on('close', (event) => {
- console.log('[OpenClaw] 🔴 WebSocket 连接已关闭');
- console.log('[OpenClaw] 关闭代码:', event.code);
- console.log('[OpenClaw] 关闭原因:', event.reason || '无');
- console.log('[OpenClaw] 是否干净关闭:', event.wasClean);
- console.log('[OpenClaw] 就绪状态:', this.socket.readyState, '(3=CLOSED)');
-
+ this.socket.on('close', () => {
this.isConnected = false;
-
+ console.log('[OpenClaw] Disconnected');
// 检查窗口是否存在
if (mainWindow && !mainWindow.isDestroyed()) {
this.eventHandler('disconnected');
}
-
- // 根据关闭代码给出建议
- if (event.code === 1006) {
- console.log('[OpenClaw] ⚠️ 异常关闭(1006)- 可能是网络问题或服务器拒绝连接');
- } else if (event.code === 1002) {
- console.log('[OpenClaw] ⚠️ 协议错误(1002)- 服务器不支持的协议');
- } else if (event.code === 1003) {
- console.log('[OpenClaw] ⚠️ 数据类型错误(1003)- 服务器不接受的数据格式');
- } else if (event.code === 1008) {
- console.log('[OpenClaw] ⚠️ 策略违规(1008)- 违反服务器策略');
- } else if (event.code === 1011) {
- console.log('[OpenClaw] ⚠️ 服务器错误(1011)- 服务器遇到问题');
- }
-
this.scheduleReconnect();
});
this.socket.on('error', (error) => {
- console.error('[OpenClaw] ❌ WebSocket 错误:', error.message);
- console.error('[OpenClaw] 错误类型:', error.constructor.name);
- console.error('[OpenClaw] 错误堆栈:', error.stack?.split('\n').slice(0, 3).join('\n'));
+ console.error('[OpenClaw] Error:', error.message);
// 检测 SSL/TLS 错误并给出建议
if (error.message?.includes('WRONG_VERSION_NUMBER')) {
- console.error('[OpenClaw] 🔴 SSL/TLS 协议不匹配!');
- console.error('[OpenClaw] 可能原因:');
- console.error('[OpenClaw] 1. 使用 wss:// 但服务器只支持 ws://');
- console.error('[OpenClaw] 2. 使用 ws:// 但服务器只支持 wss://');
- console.error('[OpenClaw] 3. SSL 证书问题(自签名/过期)');
+ console.error('[OpenClaw] ❗ SSL/TLS 协议不匹配!');
console.error('[OpenClaw] 建议:');
- console.error('[OpenClaw] - 尝试改用 ws://(非加密)');
- console.error('[OpenClaw] - 确认端口是否正确');
- console.error('[OpenClaw] - 检查服务器是否正常运行');
- } else if (error.message?.includes('ECONNREFUSED')) {
- console.error('[OpenClaw] 🔴 连接被拒绝 - 服务器可能未启动或防火墙阻止');
- } else if (error.message?.includes('ETIMEDOUT')) {
- console.error('[OpenClaw] 🔴 连接超时 - 网络问题或服务器无响应');
- } else if (error.message?.includes('ENOTFOUND')) {
- console.error('[OpenClaw] 🔴 域名解析失败 - 检查 URL 是否正确');
+ console.error('[OpenClaw] - 如果使用 wss://,请确认服务器支持 SSL');
+ console.error('[OpenClaw] - 如果服务器不支持 SSL,请改用 ws://');
+ console.error('[OpenClaw] - 检查端口是否正确(SSL 和非 SSL 可能使用不同端口)');
}
// 检查窗口是否存在
@@ -112,7 +402,317 @@
this.eventHandler('error', {
error: error.message,
hint: error.message?.includes('WRONG_VERSION_NUMBER') ?
- 'SSL 协议不匹配,请检查是否应该使用 ws://而不是 wss://' : null
+ 'SSL 协议不匹配,请检查是否应该使用 ws:// 而不是 wss://' : null
});
}
});
+
+ } catch (error) {
+ this.eventHandler('error', { error: error.message });
+ this.scheduleReconnect();
+ }
+ }
+
+ // 发送 connect 握手(响应 challenge)
+ sendConnect(nonce = null) {
+ const connectMessage = {
+ type: 'req',
+ id: this.nextId(),
+ method: 'connect',
+ params: {
+ minProtocol: this.protocolVersion,
+ maxProtocol: this.protocolVersion,
+ client: {
+ id: 'wecome-client',
+ version: '1.0.0',
+ platform: process.platform,
+ mode: 'operator'
+ },
+ role: 'operator',
+ scopes: ['operator.read', 'operator.write'],
+ caps: [],
+ commands: [],
+ permissions: {},
+ auth: this.token ? { token: this.token } : {},
+ locale: 'zh-CN',
+ userAgent: 'wecome-openclaw-client/1.0.0',
+ device: {
+ id: this.deviceId,
+ publicKey: '',
+ signature: '',
+ signedAt: Date.now(),
+ nonce: nonce || uuidv4() // 使用服务器的 nonce 如果存在
+ }
+ }
+ };
+
+ console.log('[OpenClaw] Sending connect request:', JSON.stringify(connectMessage, null, 2));
+ this.send(connectMessage);
+ }
+
+ // 处理来自 OpenClaw 的消息(回复到企业微信)
+ handleMessage(message) {
+ if (message.type === 'res' && message.ok) {
+ // connect 响应
+ if (message.payload?.type === 'hello-ok') {
+ console.log('[OpenClaw] ✅ Handshake successful! Protocol version:', message.payload.protocol);
+ console.log('[OpenClaw] Policy:', JSON.stringify(message.payload.policy));
+
+ // 握手成功后才真正标记为已连接
+ if (mainWindow && !mainWindow.isDestroyed()) {
+ // 发送一个特殊的握手成功事件
+ mainWindow.webContents.send('openclaw-event', {
+ eventType: 'handshake-ok',
+ data: { protocol: message.payload.protocol }
+ });
+ }
+ }
+ } else if (message.type === 'res' && !message.ok) {
+ console.error('[OpenClaw] ❌ Connect failed:', message.error);
+ } else if (message.type === 'event') {
+ // 处理 OpenClaw 的事件
+ if (message.event === 'message.outbound') {
+ // OpenClaw 回复消息,需要转发到企业微信
+ this.forwardMessageToWeCom(message);
+ }
+ }
+ }
+
+ // 转发 OpenClaw 回复到企业微信
+ async forwardMessageToWeCom(message) {
+ const payload = message.payload;
+ const { channel, text, chatId, botId, streamId, finish } = payload;
+
+ if (channel !== 'wecom') {
+ return;
+ }
+
+ const wecomConn = wecomConnections.get(botId);
+ if (!wecomConn) {
+ console.error(`[OpenClaw] Bot ${botId} not connected`);
+ return;
+ }
+
+ try {
+ await wecomConn.sendMessage(chatId, text, finish, streamId);
+ console.log(`[Forward] OpenClaw -> WeCom: ${chatId}`);
+ } catch (error) {
+ console.error('[Forward] Error:', error);
+ }
+ }
+
+ send(message) {
+ if (this.socket && this.socket.readyState === WebSocket.OPEN) {
+ this.socket.send(JSON.stringify(message));
+ }
+ }
+
+ nextId() {
+ return `msg_${++this.messageId}_${Date.now()}`;
+ }
+
+ scheduleReconnect() {
+ if (this.reconnectAttempts >= this.maxReconnectAttempts) {
+ console.error('[OpenClaw] Max reconnect attempts reached');
+ return;
+ }
+
+ this.reconnectAttempts++;
+ const delay = Math.min(1000 * Math.pow(1.5, this.reconnectAttempts), 60000);
+ console.log(`[OpenClaw] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
+
+ setTimeout(() => this.connect(), delay);
+ }
+
+ disconnect() {
+ if (this.socket) {
+ this.socket.close();
+ this.isConnected = false;
+ }
+ }
+}
+
+// IPC 处理器
+function setupIpcHandlers() {
+ // 获取配置
+ ipcMain.handle('get-config', () => {
+ return store.store;
+ });
+
+ // 保存配置
+ ipcMain.handle('save-config', (event, config) => {
+ store.set(config);
+ return { success: true };
+ });
+
+ // 连接企业微信 Bot
+ ipcMain.handle('connect-wecom', (event, botConfig) => {
+ const { botId, secret, id } = botConfig;
+
+ // 如果已存在,先断开
+ if (wecomConnections.has(botId)) {
+ wecomConnections.get(botId).disconnect();
+ }
+
+ const connection = new WeComConnection({ botId, secret, id }, (eventType, data) => {
+ mainWindow.webContents.send('wecom-event', { eventType, data });
+ });
+
+ connection.connect();
+
+ return { success: true };
+ });
+
+ // 断开企业微信 Bot
+ ipcMain.handle('disconnect-wecom', (event, botId) => {
+ const conn = wecomConnections.get(botId);
+ if (conn) {
+ conn.disconnect();
+ wecomConnections.delete(botId);
+ }
+ return { success: true };
+ });
+
+ // 连接 OpenClaw
+ ipcMain.handle('connect-openclaw', (event, config) => {
+ const { url, token } = config;
+
+ if (openclawConnection) {
+ openclawConnection.disconnect();
+ }
+
+ openclawConnection = new OpenClawConnection(url, token, (eventType, data) => {
+ mainWindow.webContents.send('openclaw-event', { eventType, data });
+ });
+
+ openclawConnection.connect();
+
+ return { success: true };
+ });
+
+ // 断开 OpenClaw
+ ipcMain.handle('disconnect-openclaw', () => {
+ if (openclawConnection) {
+ openclawConnection.disconnect();
+ openclawConnection = null;
+ }
+ return { success: true };
+ });
+
+ // 获取连接状态
+ ipcMain.handle('get-connection-status', () => {
+ const wecomStatus = {};
+ wecomConnections.forEach((conn, botId) => {
+ wecomStatus[botId] = {
+ connected: conn.isConnected,
+ reconnectAttempts: conn.reconnectAttempts
+ };
+ });
+
+ return {
+ wecom: wecomStatus,
+ openclaw: openclawConnection ? { connected: openclawConnection.isConnected } : { connected: false }
+ };
+ });
+
+ // 测试消息
+ ipcMain.handle('send-test-message', async (event, botId, chatId, text) => {
+ const conn = wecomConnections.get(botId);
+ if (!conn) {
+ return { success: false, error: 'Bot not connected' };
+ }
+
+ try {
+ await conn.sendMessage(chatId, text, true);
+ return { success: true };
+ } catch (error) {
+ return { success: false, error: error.message };
+ }
+ });
+
+ // 测试 OpenClaw 消息
+ ipcMain.handle('send-test-openclaw-message', async (event, text) => {
+ if (!openclawConnection || !openclawConnection.isConnected) {
+ return { success: false, message: 'OpenClaw 未连接' };
+ }
+
+ try {
+ // 构建测试消息
+ const testMessage = {
+ type: 'req',
+ id: openclawConnection.nextId(),
+ method: 'chat.send',
+ params: {
+ text: text,
+ sessionKey: `test_${Date.now()}`,
+ echo: true // 要求回显
+ }
+ };
+
+ console.log('[OpenClaw] Sending test message:', testMessage);
+ openclawConnection.send(testMessage);
+
+ return {
+ success: true,
+ message: `测试消息已发送:"${text}"`,
+ timestamp: new Date().toLocaleTimeString()
+ };
+ } catch (error) {
+ return { success: false, message: `发送失败:${error.message}` };
+ }
+ });
+}
+
+// 应用生命周期
+app.whenReady().then(() => {
+ createWindow();
+ createTray();
+ setupIpcHandlers();
+
+ // 自动加载配置并连接
+ setTimeout(async () => {
+ const config = store.store;
+
+ // 连接 OpenClaw
+ if (config.openclaw.enabled) {
+ await ipcMain.emit('connect-openclaw', null, config.openclaw);
+ }
+
+ // 连接所有启用的 Bot
+ for (const bot of config.bots) {
+ if (bot.enabled) {
+ await ipcMain.emit('connect-wecom', null, bot);
+ }
+ }
+ }, 1000);
+
+ app.on('activate', () => {
+ if (BrowserWindow.getAllWindows().length === 0) {
+ createWindow();
+ }
+ });
+});
+
+app.on('window-all-closed', () => {
+ // 清理所有连接
+ wecomConnections.forEach(conn => conn.disconnect());
+ if (openclawConnection) {
+ openclawConnection.disconnect();
+ }
+
+ if (process.platform !== 'darwin') {
+ app.quit();
+ }
+});
+
+app.on('quit', () => {
+ // 清理所有连接
+ console.log('[App] Quitting, cleaning up connections...');
+ wecomConnections.forEach(conn => conn.disconnect());
+ if (openclawConnection) {
+ openclawConnection.disconnect();
+ }
+});
+
+// 安全策略
+app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors');
diff --git a/renderer/src/App.js b/renderer/src/App.js
index af6e1ef..a2a984a 100644
--- a/renderer/src/App.js
+++ b/renderer/src/App.js
@@ -274,13 +274,6 @@ function App() {
-
{connectionStatus.openclaw.connected ? (