diff --git a/README.md b/README.md index e536ecb..7aabfb5 100644 --- a/README.md +++ b/README.md @@ -98,20 +98,34 @@ npm i wechaty-puppet-bridge ## 更多示例代码 -- 运行在[cixingguangming55555/wechat-bot](https://github.com/cixingguangming55555/wechat-bot)上的v3.9.2.23 [示例代码](./examples/ripe-wechaty-3090223.ts) +|源|版本|使用| +|--|--|--| +|[cixingguangming55555/wechat-bot](https://github.com/cixingguangming55555/wechat-bot)|v3.9.2.23|[示例代码](./examples/ripe-wechaty-3090223.ts)| +|[jwping/wxbot](https://github.com/jwping/wxbot)|v3.9.8.25|[示例代码](./examples/ripe-wechaty-jwping-wxbot-3090825.ts)| +|[ttttupup/wxhelper](https://github.com/ttttupup/wxhelper/tree/dev-3.9.2.23)|v3.9.2.23|[示例代码](./examples/ripe-wechaty-ttttupup-wxhelper-3090223.ts) (需要【以管理员身份运行】WeChat客户端)| +|[ttttupup/wxhelper](https://github.com/ttttupup/wxhelper/tree/dev-3.9.5.81)|v3.9.5.81|[示例代码](./examples/ripe-wechaty-ttttupup-wxhelper-3090581.ts) (需要【以管理员身份运行】WeChat客户端)| +|[ttttupup/wxhelper](https://github.com/ttttupup/wxhelper/tree/dev-3.9.8.25)|v3.9.8.25|[示例代码](./examples/ripe-wechaty-atorber-fused-3090825.ts) (需要【以管理员身份运行】WeChat客户端)| +|[ttttupup/wxhelper](https://github.com/ttttupup/wxhelper/tree/dev-3.9.10.19)|v3.9.10.19|[示例代码](./examples/ripe-bridge-ttttupup-wxhelper-3091019.ts) (需要【以管理员身份运行】WeChat客户端)| -- 运行在[jwping/wxbot](https://github.com/jwping/wxbot)上的v3.9.8.25 [示例代码](./examples/ripe-wechaty-jwping-wxbot-3090825.ts) +## API接口 -- 运行在[ttttupup/wxhelper](https://github.com/ttttupup/wxhelper/tree/dev-3.9.2.23)上的v3.9.2.23 [示例代码](./examples/ripe-wechaty-ttttupup-wxhelper-3090223.ts) (需要【以管理员身份运行】WeChat客户端) +Puppet Bridge是对开源bot项目的API封装,并不会对原生的API进行修改,因此你依然可以使用底层bot的原生API进行开发,以下主要对wxhelper原生API进行说明 -- 运行在[ttttupup/wxhelper](https://github.com/ttttupup/wxhelper/tree/dev-3.9.8.25)上的v3.9.8.25 [示例代码](./examples/ripe-wechaty-atorber-fused-3090825.ts) (需要【以管理员身份运行】WeChat客户端) +### HTTP API -- 运行在[ttttupup/wxhelper](https://github.com/ttttupup/wxhelper/tree/dev-3.9.5.81)上的v3.9.5.81 [示例代码](./examples/ripe-wechaty-ttttupup-wxhelper-3090581.ts) (需要【以管理员身份运行】WeChat客户端) +[3.9.9.43版本接口](https://github.com/ttttupup/wxhelper/blob/dev-3.9.10.19/doc/3.9.9.43.md)兼容3.9.10.19 -- 运行在[ttttupup/wxhelper](https://github.com/ttttupup/wxhelper/tree/dev-3.9.10.19)上的v3.9.10.19 [示例代码](./examples/ripe-bridge-ttttupup-wxhelper-3091019.ts) (需要【以管理员身份运行】WeChat客户端) +### WEB HOOK + +wxhelper要求用户启动一个websoket服务接收数据,Puppet Bridge中已经启动了一个websoket服务并将从wxhelper接收到的数据转发给所有的客户端,你只需要连接该服务即可订阅消息推送,端口号保持wxhelper默认的19099 ## 更新日志 +### v0.14.0 + +- 优化websoket服务为客户端模式,支持多端订阅 +- 保留wxhelper的原生API,其他编程语言可直接调用 + ### v0.12.0 - 增加wxhelper-3.9.10.19-v1.dll支持 diff --git a/package.json b/package.json index 1d05c96..a0fd45f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wechaty-puppet-bridge", - "version": "0.12.1", + "version": "0.14.0", "description": "Puppet Bridge for Wechaty", "type": "module", "exports": { diff --git a/src/agents/ttttupup-wxhelper-3091019.ts b/src/agents/ttttupup-wxhelper-3091019.ts index 72d158a..833e7ed 100644 --- a/src/agents/ttttupup-wxhelper-3091019.ts +++ b/src/agents/ttttupup-wxhelper-3091019.ts @@ -62,7 +62,7 @@ class Bridge extends EventEmitter { const timer = setInterval(() => { if (this.isLoggedIn) { log.info('已登录,清除定时器...') - this.server = this.createWebSocket(this.wsUrl.split(':')[2] as string) + this.createWebSocketClient(this.wsUrl.split(':')[2] as string, '127.0.0.1') // 启动wxbot-sidecar-3.9.8.25.exe clearInterval(timer) } else { @@ -122,7 +122,10 @@ class Bridge extends EventEmitter { } // C:\Users\tyutl\Documents\GitHub\chatflow\node_modules\wechaty-puppet-bridge\src\assets\funtool_wx_3.9.2.23.exe - const dllPath = join(dirname, 'src', 'assets', 'wxhelper-3.9.10.19-v1.dll') + // const dllPath = join(dirname, 'src', 'assets', 'wxhelper-3.9.10.19-v1.dll') + const dllPath = join(dirname, 'src', 'assets', 'wxhelper-native.dll') + // const dllPath = join(dirname, 'src', 'assets', 'wxhelper-vs22.dll') + // const execString = `${injectorPath} --process-name WeChat.exe --inject ${dllPath}` const execString = `${injectorPath} -p ${pid} --inject ${dllPath}` @@ -206,28 +209,26 @@ class Bridge extends EventEmitter { } private createWebSocket (port: string) { - const server = net.createServer((socket: any) => { - let messageStore = readMsgStore() + const clients:net.Socket[] = [] // 用来存储所有客户端 + + const server = net.createServer() + server.on('connection', (socket: net.Socket) => { + clients.push(socket) // 新增,将新连接的客户端加入到clients数组中 // const data = Buffer.from('') + socket.on('connect', () => { + log.verbose('Client connected') + }) + socket.on('data', (data: any) => { log.verbose(`Received data: ${data}`) - try { - data = data.toString() - const dataJson = JSON.parse(data) - - // log.info('原始dataJson:\n', JSON.stringify(dataJson, undefined, 2)) - - // 缓存消息 - messageStore = writeMsgStore(messageStore, dataJson) - - const j = JSON.parse(data) - // log.info('ws message hook type:', j.type, JSON.stringify(j, undefined, 2)) - this.handleReceiveMessage(j) - } catch (e) { - log.error('Received data error:', e) - } + // 将接收到的消息发送给所有客户端 + clients.forEach((client: net.Socket) => { + if (client !== socket) { + client.write(data) + } + }) }) @@ -237,6 +238,11 @@ class Bridge extends EventEmitter { socket.on('close', () => { log.verbose('Client disconnected') + // 将断开连接的客户端从clients数组中删除 + const index = clients.indexOf(socket) + if (index !== -1) { + clients.splice(index, 1) + } }) socket.on('error', (err: any) => { @@ -245,6 +251,11 @@ class Bridge extends EventEmitter { }) }) + server.on('error', (err: any) => { + log.error('Server error:', err) + this.emit('error', err) + }) + server.listen(Number(port), () => { log.info(`Server listening on port ${port}`) }) @@ -253,46 +264,7 @@ class Bridge extends EventEmitter { log.info('ip:', ip) log.info('port:', port) - // this.wxhelper.unhookSyncMsg().then((res) => { - // log.info('unhookSyncMsg success:', JSON.stringify(res.data)) - // this.wxhelper.hookSyncMsg({ - // port, - // ip, - // url: '', - // timeout: '3000', - // enableHttp: '0', - // }) - // .then(async (res) => { - // log.info('hookSyncMsg success:', JSON.stringify(res.data)) - // const checkLoginRes = await this.wxhelper.checkLogin() - // log.info('checkLogin success:', JSON.stringify(checkLoginRes.data)) - - // if (checkLoginRes.data && checkLoginRes.data.code === 1 && checkLoginRes.data.msg === 'success') { - // log.info('login success') - // // 如果非首次登录,且当前状态为未登录,则触发登录事件 - // if (!this.isLoggedIn) { - // this.isLoggedIn = true - // this.emit('login', 'login') - // } else { - // this.emit('heartbeat', 'heartbeat') - // } - // } else { - // if (this.isLoggedIn) { - // this.isLoggedIn = false - // this.emit('logout', 'logout') - // } else { - // throw new Error('启动失败,请检查微信是否已经处于登录状态') - // } - // } - // return res - // }) - // .catch((e) => { - // log.error('hookSyncMsg error:', e) - // }) - // return res - // }).catch((e) => { - // log.error('unhookSyncMsg error:', e) - // }) + this.createWebSocketClient(port, ip) this.wxhelper.hookSyncMsg({ port, @@ -328,48 +300,42 @@ class Bridge extends EventEmitter { .catch((e) => { log.error('hookSyncMsg error:', e) }) - - // 每隔30s发送心跳消息 - // setTimeout(() => { - // log.info('send heartbeat...') - // this.wxhelper.hookSyncMsg({ - // port, - // ip, - // url: '', - // timeout: '3000', - // enableHttp: '0', - // }) - // .then(async (res) => { - // log.info('hookSyncMsg success:', JSON.stringify(res.data)) - // const checkLoginRes = await this.wxhelper.checkLogin() - // log.info('checkLogin success:', JSON.stringify(checkLoginRes.data)) - - // if (checkLoginRes.data && checkLoginRes.data.code !== 0 && checkLoginRes.data.msg === 'success') { - // log.info('login success') - // // 如果非首次登录,且当前状态为未登录,则触发登录事件 - // if (!this.isLoggedIn) { - // this.isLoggedIn = true - // this.emit('login', 'login') - // } else { - // this.emit('heartbeat', 'heartbeat') - // } - // } else { - // if (this.isLoggedIn) { - // this.isLoggedIn = false - // this.emit('logout', 'logout') - // } else { - // throw new Error('启动失败,请检查微信是否已经处于登录状态') - // } - // } - // return res - // }) - // .catch((e) => { - // log.error('hookSyncMsg error:', e) - // }) - // }, 10000) return server } + private createWebSocketClient (port: string, host: string) { + let messageStore = readMsgStore() + const client = new net.Socket() + // 连接到服务器并接收消息 + client.connect(Number(port), host, () => { + log.verbose('Connected to server') + }) + + client.on('data', (data: any) => { + log.verbose(`Received data: ${data}`) + try { + data = data.toString() + const dataJson = JSON.parse(data) + + // log.info('原始dataJson:\n', JSON.stringify(dataJson, undefined, 2)) + + // 缓存消息 + messageStore = writeMsgStore(messageStore, dataJson) + + const j = JSON.parse(data) + // log.info('ws message hook type:', j.type, JSON.stringify(j, undefined, 2)) + this.handleReceiveMessage(j) + } catch (e) { + log.error('Received data error:', e) + } + }) + + client.on('error', (err: any) => { + log.error('Socket error:', err) + this.createWebSocket(port) + }) + } + private doLogin = async () => { // 初始化数据库信息 await this.wxhelper.initDBInfo() diff --git a/src/assets/wxhelper-native.dll b/src/assets/wxhelper-native.dll new file mode 100644 index 0000000..d7b7511 Binary files /dev/null and b/src/assets/wxhelper-native.dll differ diff --git a/src/assets/wxhelper-vs22.dll b/src/assets/wxhelper-vs22.dll new file mode 100644 index 0000000..8a12886 Binary files /dev/null and b/src/assets/wxhelper-vs22.dll differ diff --git a/src/puppet-bridge-ttttupup-wxhelper-3091019.ts b/src/puppet-bridge-ttttupup-wxhelper-3091019.ts index ead9ce1..4e77799 100644 --- a/src/puppet-bridge-ttttupup-wxhelper-3091019.ts +++ b/src/puppet-bridge-ttttupup-wxhelper-3091019.ts @@ -697,6 +697,7 @@ class PuppetBridge extends PUPPET.Puppet { private async loadContactList () { const contactListRes = await this.bridge.wxhelper.getContactList() + // console.info('contactListRes:', JSON.stringify(contactListRes.data)) const contactList = contactListRes.data.data as wxhelper.ContactRaw[] // log.info('contactList get success, wait for contactList init ...', JSON.stringify(contactList, undefined, 2)) for (const contactInfo of contactList) {