日常工作线上化后,很多时候需要自动化给团队的同学推送消息。基本上会采用企微机器人的方式,但公司企微机器人服务因为撑不住,故不给非业务诉求使用了,只能退而求其次,想到使用 node-mail 来发邮件,但腾讯企业邮箱又有发信限制,其他没有发信限制的邮箱又不够安全合规。最终有了用 Electron 做一个桌面工具给团队同学安装使用的想法,后台(软件最小化)获取 websocket 消息推送,借助 Electron 能力,弹出 mac 消息提示。
举例一个实际的场景:系统发版申请,现有一个 web 页面,用来填写发版申请单,填写完成后推送提醒给发版执行人
核心处理:
websocket 服务:使用 Node 提供 websocket 服务,web 系统/ node 服务 完成消息交互测试
Eleectron 应用搭建:
使用 Electron 加载 web 系统,登录后传入 用户ID 连接 socket
使用 Electron 中 Node 线程获取 sokcket 消息并弹出提醒
websocket 服务
创建 ws-server 目录,存放 server 相关项目内容;创建 node-server/app.js 用作 ws server。
1 2 npm init -y pnpm install ws query-string
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 const WebSocket = require ('ws' )const qs = require ('querystring' )const wss = new WebSocket .Server ({ port : 8080 })wss.on ('connection' , (ws, req ) => { const params = qs.parse (req.url .split ('?' )[1 ]) console .log (`客户端请求建立连接,参数:${JSON .stringify(params)} ` ) ws.id = params.id ws.on ('message' , message => { console .log (`服务端收到消息: ${message} ` ) console .log (`消息来源: ${ws.id} ` ) }) ws.send ('建立 ws 连接成功' ) }) const http = require ('http' )http.createServer ((req, res ) => { res.writeHead (200 , { 'Content-Type' : 'text/plain' }) res.end ('触发广播消息发送' ) console .log ('触发广播消息发送' ) wss.clients .forEach ((client ) => { if (client.readyState === WebSocket .OPEN ) { client.send ('广播消息,你好' + client.id ) } }) }).listen (8800 ) console .log ('服务已启动' )
创建 index.html 用作 ws client。前端建立 ws 连接参考:https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <meta http-equiv ="X-UA-Compatible" content ="IE=edge" > <meta name ="viewport" content ="width=device-width, initial-scale=1.0" > <title > Document</title > </head > <body > <section > WS 连接标识(id):<input id ="wsKey" /> <button id ="connect" > 连接</button > </section > <section > <input id ="msg" placeholder ="消息内容" /> <button id ="send" > 发送消息</button > </section > <script > let socket = null ; document .getElementById ('connect' ).onclick = function ( ) { if (socket) { alert ('已经建立连接,无需重复连接' ) return ; } const wsKey = document .getElementById ('wsKey' ).value ; if (wsKey !== '' ) { socket = new WebSocket (`ws://localhost:8080?id=${wsKey} ` ); socket.addEventListener ("open" , function (event ) { console .log ("连接已打开" ); }); socket.addEventListener ("message" , function (event ) { console .log ("获取到服务端消息:" , event.data ); }); return ; } alert ('请输入连接标识' ) } document .getElementById ('send' ).onclick = function ( ) { const msg = document .getElementById ('msg' ).value ; if (msg !== '' && socket) { socket.send (JSON .stringify ({ data : { msg } })); return ; } alert ('请先建立链接并输入消息内容' ) } </script > </body > </html >
可扩展的地方还有很多,除了广播也可以单点推送,通过 wss.clients 中 id 标识(实际ID表示可能是 login user token),推送到指定 chient/用户 等等。
ws 服务部署说明 https 页面发起 ws 链接需要使用 wss 协议,否则会报错。如果需要使用 ng 代理,相关配置如下:
1 2 3 4 5 6 7 location ~* ^/socket/ { rewrite ^/socket/(.*) /$1 break ; proxy_pass http://127.0.0.1:8080; proxy_http_version 1 .1 ; proxy_set_header Upgrade $http_upgrade ; proxy_set_header Connection "upgrade" ; }
如上配置后,可使用 ws://demo.com/socket/
链接 ws 服务。
node ws client 示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const WebSocket = require ('ws' )const url = 'ws://demo.com/socket/?id=local_node' const connection = new WebSocket (url)connection.onopen = () => { connection.send ('Message From Client' ) } connection.onerror = (error ) => { console .log (`WebSocket error: ${error} ` ) console .log ('error: ' , error); } connection.onmessage = (e ) => { console .log (e.data ) }
Electron 应用搭建 Electron 应用模板代码,可参考:https://github.sheincorp.cn/zzyxka/electron-demo-app
核心步骤:
使用 Electron 加载 web 系统,登录后传入 用户ID 连接 socket
使用 Electron 中 Node 线程获取 sokcket 消息并弹出提醒
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function showNotification ({ title, body } ) { new Notification ({ title, body }).show () } const createWindow = ( ) => { win.loadFile ('index.html' ) } app.whenReady ().then (() => { ipcMain.handle ('login' , (event, args ) => { const WebSocket = require ('ws' ) const url = `ws://127.0.0.1:8080/socket/?id=${args.id} ` const connection = new WebSocket (url) connection.onopen = () => { connection.send ('Message From Client' ) } connection.onmessage = (e ) => { showNotification ({ title : 'new msg' , body : e.data }) } return { data : 'login success' } }) createWindow () })