feat: 使用正确的 Gateway API - chat.send
关键修复:
- 之前错误:直接发送 message.inbound 事件
- 现在正确:使用 Gateway 的 chat.send 方法
Gateway API 调用:
{
"type": "req",
"method": "chat.send",
"params": {
"sessionKey": "wecom:group:chatId",
"message": "文本内容",
"attachments": [{"type": "image", "path": "..."}],
"deliver": true,
"idempotencyKey": "messageId"
}
}
会话管理:
- sessionKey 格式:wecom:chatType:chatId
- 例如:wecom:group:123456789 或 wecom:direct:zhangsan
- OpenClaw 会自动创建或复用会话
媒体文件处理:
- 下载企业微信图片/文件
- 保存到本地媒体目录
- 通过 attachments 参数传递给 chat.send
这样才符合 OpenClaw Gateway 协议规范!
This commit is contained in:
686
electron/main-backup.js
Normal file
686
electron/main-backup.js
Normal file
@@ -0,0 +1,686 @@
|
||||
const { app, BrowserWindow, ipcMain, dialog, Tray, Menu, nativeImage } = require('electron');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const https = require('https');
|
||||
const http = require('http');
|
||||
const Store = require('electron-store');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const { WSClient } = require('@wecom/aibot-node-sdk');
|
||||
const WebSocket = require('ws');
|
||||
const crypto = require('crypto');
|
||||
|
||||
// 初始化配置存储
|
||||
const store = new Store({
|
||||
name: 'config',
|
||||
defaults: {
|
||||
bots: [],
|
||||
openclaw: {
|
||||
url: 'ws://localhost:18789',
|
||||
token: '',
|
||||
enabled: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mainWindow;
|
||||
let wecomConnections = new Map();
|
||||
let openclawConnection = null;
|
||||
let reqIdMap = new Map();
|
||||
|
||||
// 下载文件到本地
|
||||
async function downloadFile(url, aesKey = null) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const chunks = [];
|
||||
const req = (url.startsWith('https') ? https : http).get(url, (res) => {
|
||||
res.on('data', chunk => chunks.push(chunk));
|
||||
res.on('end', () => {
|
||||
const buffer = Buffer.concat(chunks);
|
||||
resolve({ buffer, filename: `file_${Date.now()}` });
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
req.setTimeout(30000, () => {
|
||||
req.destroy();
|
||||
reject(new Error('Download timeout'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 保存媒体文件
|
||||
async function saveMediaFile(buffer, filename, mediaType = 'inbound') {
|
||||
const mediaDir = path.join(app.getPath('userData'), 'media', mediaType);
|
||||
fs.mkdirSync(mediaDir, { recursive: true });
|
||||
|
||||
const ext = path.extname(filename) || '.dat';
|
||||
const savePath = path.join(mediaDir, `${filename}_${Date.now()}${ext}`);
|
||||
|
||||
fs.writeFileSync(savePath, buffer);
|
||||
return { path: savePath, contentType: 'application/octet-stream' };
|
||||
}
|
||||
|
||||
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) => console.log(`[WeCom:${this.botId}] DEBUG:`, msg),
|
||||
info: (msg) => console.log(`[WeCom:${this.botId}] INFO:`, msg),
|
||||
warn: (msg) => console.warn(`[WeCom:${this.botId}] WARN:`, msg),
|
||||
error: (msg) => console.error(`[WeCom:${this.botId}] ERROR:`, msg)
|
||||
},
|
||||
heartbeatInterval: 30000,
|
||||
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('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;
|
||||
});
|
||||
|
||||
this.client.on('message', async (frame) => {
|
||||
console.log(`[WeCom:${this.botId}] Received message`);
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
this.eventHandler('message', { botId: this.botId, frame });
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
reqIdMap.set(chatId, reqId);
|
||||
|
||||
// 提取文本
|
||||
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();
|
||||
}
|
||||
|
||||
// 处理媒体文件
|
||||
const mediaList = [];
|
||||
|
||||
// 下载图片
|
||||
if (body.image?.url) {
|
||||
try {
|
||||
console.log('[WeCom] Downloading image:', body.image.url);
|
||||
const { buffer } = await downloadFile(body.image.url, body.image.aeskey);
|
||||
const saved = await saveMediaFile(buffer, 'image', 'inbound');
|
||||
mediaList.push({ type: 'image', path: saved.path });
|
||||
console.log('[WeCom] Image saved:', saved.path);
|
||||
} catch (error) {
|
||||
console.error('[WeCom] Failed to download image:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
if (body.file?.url) {
|
||||
try {
|
||||
console.log('[WeCom] Downloading file:', body.file.url);
|
||||
const { buffer } = await downloadFile(body.file.url, body.file.aeskey);
|
||||
const saved = await saveMediaFile(buffer, 'file', 'inbound');
|
||||
mediaList.push({ type: 'file', path: saved.path });
|
||||
console.log('[WeCom] File saved:', saved.path);
|
||||
} catch (error) {
|
||||
console.error('[WeCom] Failed to download file:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 构建转发消息
|
||||
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,
|
||||
media: mediaList,
|
||||
timestamp: body.create_time || Date.now()
|
||||
},
|
||||
reqId: reqId,
|
||||
botId: this.botId
|
||||
}
|
||||
};
|
||||
|
||||
openclawConnection.send(JSON.stringify(openclawMessage));
|
||||
console.log(`[Forward] WeCom -> OpenClaw: ${messageId}`);
|
||||
}
|
||||
|
||||
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`);
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OpenClawConnection {
|
||||
constructor(url, token, eventHandler) {
|
||||
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.deviceId = `wecome-client_${process.platform}_${uuidv4()}`;
|
||||
}
|
||||
|
||||
async connect() {
|
||||
try {
|
||||
console.log('='.repeat(60));
|
||||
console.log('[OpenClaw] ========== 开始连接 ==========');
|
||||
console.log('[OpenClaw] 目标地址:', this.url);
|
||||
|
||||
const isSecure = this.url.startsWith('wss://');
|
||||
console.log(`[OpenClaw] 连接类型:${isSecure ? 'WSS (SSL)' : 'WS (非加密)'}`);
|
||||
|
||||
const wsOptions = {
|
||||
rejectUnauthorized: false,
|
||||
followRedirects: true,
|
||||
handshakeTimeout: 10000
|
||||
};
|
||||
|
||||
if (isSecure) {
|
||||
wsOptions.tls = {
|
||||
rejectUnauthorized: false,
|
||||
minVersion: 'TLSv1.2'
|
||||
};
|
||||
console.log('[OpenClaw] SSL 配置:已启用(允许自签名证书)');
|
||||
}
|
||||
|
||||
console.log('[OpenClaw] 创建 WebSocket 连接...');
|
||||
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;
|
||||
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
this.eventHandler('connected');
|
||||
}
|
||||
|
||||
console.log('[OpenClaw] 📤 发送 connect 请求...');
|
||||
this.sendConnect();
|
||||
});
|
||||
|
||||
this.socket.on('message', (data) => {
|
||||
if (!mainWindow || mainWindow.isDestroyed()) return;
|
||||
|
||||
const messageStr = data.toString();
|
||||
console.log('[OpenClaw] 📥 收到消息 (长度:', messageStr.length, '字节)');
|
||||
console.log('[OpenClaw] 原始数据:', messageStr.substring(0, 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] 错误信息:', JSON.stringify(message.error));
|
||||
}
|
||||
}
|
||||
|
||||
if (message.type === 'event' && message.event === 'connect.challenge') {
|
||||
console.log('[OpenClaw] 🔐 收到 challenge 质询');
|
||||
console.log('[OpenClaw] Nonce:', message.payload?.nonce);
|
||||
this.sendConnect(message.payload?.nonce);
|
||||
return;
|
||||
}
|
||||
|
||||
this.handleMessage(message);
|
||||
});
|
||||
|
||||
this.socket.on('close', (event) => {
|
||||
console.log('[OpenClaw] 🔴 连接已关闭');
|
||||
console.log('[OpenClaw] 关闭代码:', event.code);
|
||||
console.log('[OpenClaw] 关闭原因:', event.reason || '无');
|
||||
console.log('[OpenClaw] 是否干净:', event.wasClean);
|
||||
|
||||
this.isConnected = false;
|
||||
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
this.eventHandler('disconnected');
|
||||
}
|
||||
|
||||
if (event.code === 1006) {
|
||||
console.log('[OpenClaw] ⚠️ 异常关闭(1006)- 网络问题或服务器拒绝');
|
||||
}
|
||||
|
||||
this.scheduleReconnect();
|
||||
});
|
||||
|
||||
this.socket.on('error', (error) => {
|
||||
console.error('[OpenClaw] ❌ WebSocket 错误:', error.message);
|
||||
|
||||
if (error.message?.includes('WRONG_VERSION_NUMBER')) {
|
||||
console.error('[OpenClaw] 🔴 SSL/TLS 协议不匹配!');
|
||||
console.error('[OpenClaw] 建议:尝试改用 ws://(非加密)');
|
||||
}
|
||||
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
this.eventHandler('error', { error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('[OpenClaw] ❌ 连接异常:', error.message);
|
||||
this.eventHandler('error', { error: error.message });
|
||||
this.scheduleReconnect();
|
||||
}
|
||||
}
|
||||
|
||||
sendConnect(nonce = null) {
|
||||
const tempPublicKey = crypto.randomBytes(32).toString('hex');
|
||||
const tempSignature = crypto.randomBytes(64).toString('hex');
|
||||
|
||||
const connectMessage = {
|
||||
type: 'req',
|
||||
id: this.nextId(),
|
||||
method: 'connect',
|
||||
params: {
|
||||
minProtocol: 3,
|
||||
maxProtocol: 3,
|
||||
client: {
|
||||
id: 'cli',
|
||||
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: tempPublicKey,
|
||||
signature: tempSignature,
|
||||
signedAt: Date.now(),
|
||||
nonce: nonce || crypto.randomUUID()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
console.log('[OpenClaw] 发送 connect 请求:', JSON.stringify(connectMessage, null, 2));
|
||||
this.send(connectMessage);
|
||||
}
|
||||
|
||||
handleMessage(message) {
|
||||
if (message.type === 'res' && message.ok) {
|
||||
if (message.payload?.type === 'hello-ok') {
|
||||
console.log('[OpenClaw] ✅ Handshake 成功!协议版本:', 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 失败:', JSON.stringify(message.error));
|
||||
} else if (message.type === 'event') {
|
||||
if (message.event === 'message.outbound') {
|
||||
this.forwardMessageToWeCom(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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`);
|
||||
setTimeout(() => this.connect(), delay);
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
if (this.socket) {
|
||||
this.socket.close();
|
||||
this.isConnected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function generateReqId(prefix = 'msg') {
|
||||
return `${prefix}_${uuidv4().substring(0, 8)}_${Date.now()}`;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
function setupIpcHandlers() {
|
||||
ipcMain.handle('get-config', () => store.store);
|
||||
|
||||
ipcMain.handle('save-config', (event, config) => {
|
||||
store.set(config);
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
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 };
|
||||
});
|
||||
|
||||
ipcMain.handle('disconnect-wecom', (event, botId) => {
|
||||
const conn = wecomConnections.get(botId);
|
||||
if (conn) {
|
||||
conn.disconnect();
|
||||
}
|
||||
return { success: true };
|
||||
});
|
||||
|
||||
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 };
|
||||
});
|
||||
|
||||
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 };
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
if (config.openclaw.enabled) {
|
||||
await ipcMain.emit('connect-openclaw', null, config.openclaw);
|
||||
}
|
||||
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');
|
||||
Reference in New Issue
Block a user