From b06ef2d8ca7d2d4dc87fedd7a3750be9fe8b4371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=80=BB?= Date: Tue, 10 Mar 2026 00:38:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=AF=A6=E7=BB=86?= =?UTF-8?q?=E7=9A=84=20OpenClaw=20=E8=BF=9E=E6=8E=A5=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 日志增强: - 连接开始:显示目标地址、协议版本、Token 状态 - WebSocket 打开:显示就绪状态 - 发送消息:显示完整请求内容 - 接收消息:显示原始数据(前 500 字节)、消息类型、事件名称 - 响应处理:显示响应状态、成功/失败、错误信息 - 连接关闭:显示关闭代码、原因、是否干净关闭 - 错误处理:显示错误类型、堆栈跟踪、针对性建议 - 关闭代码诊断:1006/1002/1003/1008/1011 等代码的含义 - SSL 错误诊断:WRONG_VERSION_NUMBER/ECONNREFUSED/ETIMEDOUT 等 方便快速定位 OpenClaw Gateway 连接问题 --- electron/main.js | 730 +++++------------------------------------------ 1 file changed, 64 insertions(+), 666 deletions(-) diff --git a/electron/main.js b/electron/main.js index b49ae24..5accc26 100644 --- a/electron/main.js +++ b/electron/main.js @@ -1,349 +1,8 @@ -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', () => { + console.log('[OpenClaw] ✅ WebSocket 连接已建立'); + console.log('[OpenClaw] 就绪状态:', this.socket.readyState, '(1=OPEN)'); this.isConnected = true; this.reconnectAttempts = 0; - console.log('[OpenClaw] WebSocket connected'); // 检查窗口是否存在 if (mainWindow && !mainWindow.isDestroyed()) { @@ -351,7 +10,7 @@ class OpenClawConnection { } // 主动发送 connect 请求(兼容模式:不等待 challenge) - console.log('[OpenClaw] Sending connect request...'); + console.log('[OpenClaw] 📤 准备发送 connect 请求...'); this.sendConnect(); }); @@ -361,42 +20,91 @@ class OpenClawConnection { return; } try { - const message = JSON.parse(data.toString()); - console.log('[OpenClaw] Received:', JSON.stringify(message, null, 2)); + 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); + } + } // 处理 connect.challenge 质询 if (message.type === 'event' && message.event === 'connect.challenge') { - console.log('[OpenClaw] Received challenge, sending connect request...'); + console.log('[OpenClaw] 🔐 收到 challenge 质询'); + console.log('[OpenClaw] Nonce:', message.payload?.nonce); + console.log('[OpenClaw] 发送 connect 请求响应 challenge...'); this.sendConnect(message.payload?.nonce); return; } this.handleMessage(message); } catch (error) { - console.error('[OpenClaw] Parse error:', error); + console.error('[OpenClaw] ❌ 解析消息错误:', error.message); + console.error('[OpenClaw] 原始数据:', data.toString().substring(0, 200)); } }); - this.socket.on('close', () => { + 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.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] Error:', error.message); + console.error('[OpenClaw] ❌ WebSocket 错误:', error.message); + console.error('[OpenClaw] 错误类型:', error.constructor.name); + console.error('[OpenClaw] 错误堆栈:', error.stack?.split('\n').slice(0, 3).join('\n')); // 检测 SSL/TLS 错误并给出建议 if (error.message?.includes('WRONG_VERSION_NUMBER')) { - console.error('[OpenClaw] ❗ SSL/TLS 协议不匹配!'); + 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] 建议:'); - console.error('[OpenClaw] - 如果使用 wss://,请确认服务器支持 SSL'); - console.error('[OpenClaw] - 如果服务器不支持 SSL,请改用 ws://'); - console.error('[OpenClaw] - 检查端口是否正确(SSL 和非 SSL 可能使用不同端口)'); + 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 是否正确'); } // 检查窗口是否存在 @@ -404,317 +112,7 @@ class OpenClawConnection { 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');