# Ring 通知功能实现计划 ## 一、功能概述 ### 1.1 使用场景 远程电脑执行长链任务(如 opencode、trae 等工具)时,通过 `ring.py` 脚本向主控电脑发送通知,提醒用户任务完成。 ### 1.2 调用方式 ```bash python ring.py "任务完成" python ring.py "编译成功" --title "Build" python ring.py "下载完成" --sound ``` ### 1.3 架构设计 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 主控电脑 (被控端) │ │ ┌─────────────────────────────────────────────────────────────┐│ │ │ Ring Server (端口 3002) ││ │ │ - HTTP POST /ring ││ │ │ - 接收通知请求 ││ │ │ - 触发系统通知 (Windows Toast) ││ │ │ - 可选:播放提示音 ││ │ └─────────────────────────────────────────────────────────────┘│ └─────────────────────────────────────────────────────────────────┘ ▲ │ HTTP POST │ ┌─────────────────────────────┴───────────────────────────────────┐ │ 远程电脑 (控制端) │ │ ┌─────────────────────────────────────────────────────────────┐│ │ │ ring.py ││ │ │ - 发送 HTTP 请求到主控电脑 ││ │ │ - 支持自定义消息、标题、提示音 ││ │ └─────────────────────────────────────────────────────────────┘│ └─────────────────────────────────────────────────────────────────┘ ``` --- ## 二、实现步骤 ### 步骤 1:创建 RingService 服务 **文件:** `src/services/ring/RingService.js` **功能:** - 使用 Windows Toast 通知 API - 支持自定义标题和消息 - 可选播放提示音 **实现方式:** - 使用 PowerShell 调用 Windows Toast 通知 - 或使用 `node-notifier` 库 ### 步骤 2:创建 Ring Server **文件:** `src/server/RingServer.js` **功能:** - 独立的 HTTP 服务器,监听端口 3002 - 提供 `/ring` POST 接口 - 调用 RingService 发送通知 **API 设计:** ``` POST /ring Content-Type: application/json { "message": "任务完成", "title": "Ring", // 可选,默认 "Ring" "sound": true // 可选,默认 false } ``` ### 步骤 3:集成到 App.js **修改文件:** `src/core/App.js` **修改内容:** - 注册 RingService - 注册 RingServer - 启动时同时启动 Ring Server ### 步骤 4:更新配置 **修改文件:** `src/config/schema.js` **新增配置:** ```javascript ring: { port: { type: 'number', default: 3002 }, enabled: { type: 'boolean', default: true } } ``` ### 步骤 5:创建 ring.py 客户端脚本 **文件:** `scripts/ring.py` **功能:** - 命令行参数解析 - 发送 HTTP 请求到主控电脑 - 支持配置目标地址 **使用方式:** ```bash python ring.py "消息内容" python ring.py "消息" --title "标题" --sound python ring.py "消息" --host 192.168.1.100 --port 3002 ``` --- ## 三、文件清单 ### 新增文件 | 文件路径 | 说明 | |---------|------| | `src/services/ring/RingService.js` | 通知服务,调用系统通知 | | `src/services/ring/index.js` | 服务导出 | | `src/server/RingServer.js` | 独立 HTTP 服务器 | | `scripts/ring.py` | Python 客户端脚本 | ### 修改文件 | 文件路径 | 修改内容 | |---------|---------| | `src/core/App.js` | 注册并启动 Ring 服务 | | `src/config/schema.js` | 添加 ring 配置项 | | `config/default.json` | 添加 ring 默认配置 | --- ## 四、详细实现 ### 4.1 RingService.js ```javascript const { spawn } = require('child_process'); const logger = require('../../utils/logger'); class RingService { async notify({ message, title = 'Ring', sound = false }) { // 使用 PowerShell 发送 Windows Toast 通知 const psScript = ` [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null $template = @" ${title} ${message} ${sound ? ' "@ $xml = New-Object Windows.Data.Xml.Dom.XmlDocument $xml.LoadXml($template) $toast = [Windows.UI.Notifications.ToastNotification]::new($xml) [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("Ring").Show($toast) `; return new Promise((resolve, reject) => { const ps = spawn('powershell', ['-NoProfile', '-Command', psScript]); ps.on('close', (code) => { if (code === 0) { logger.info('Ring notification sent', { title, message }); resolve(true); } else { resolve(false); } }); ps.on('error', (err) => { logger.error('Ring notification failed', { error: err.message }); resolve(false); }); }); } } module.exports = RingService; ``` ### 4.2 RingServer.js ```javascript const http = require('http'); const logger = require('../utils/logger'); class RingServer { constructor(config = {}) { this.port = config.port || 3002; this.host = config.host || '0.0.0.0'; this.server = null; this.ringService = config.ringService; } start() { this.server = http.createServer((req, res) => { if (req.method === 'POST' && req.url === '/ring') { this.handleRing(req, res); } else { res.writeHead(404); res.end('Not Found'); } }); return new Promise((resolve, reject) => { this.server.listen(this.port, this.host, () => { logger.info('Ring server started', { port: this.port }); resolve({ port: this.port, host: this.host }); }); this.server.on('error', reject); }); } async handleRing(req, res) { let body = ''; req.on('data', chunk => body += chunk); req.on('end', async () => { try { const data = JSON.parse(body); await this.ringService.notify(data); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: true })); } catch (err) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: err.message })); } }); } stop() { return new Promise((resolve) => { if (this.server) { this.server.close(() => { logger.info('Ring server stopped'); resolve(); }); } else { resolve(); } }); } } module.exports = RingServer; ``` ### 4.3 ring.py ```python #!/usr/bin/env python3 import argparse import requests import sys DEFAULT_HOST = "localhost" DEFAULT_PORT = 3002 def main(): parser = argparse.ArgumentParser(description='Send notification to remote computer') parser.add_argument('message', help='Notification message') parser.add_argument('--title', '-t', default='Ring', help='Notification title') parser.add_argument('--sound', '-s', action='store_true', help='Play notification sound') parser.add_argument('--host', default=DEFAULT_HOST, help='Target host') parser.add_argument('--port', type=int, default=DEFAULT_PORT, help='Target port') args = parser.parse_args() try: response = requests.post( f"http://{args.host}:{args.port}/ring", json={ "message": args.message, "title": args.title, "sound": args.sound }, timeout=5 ) if response.status_code == 200: print("Notification sent successfully") else: print(f"Failed to send notification: {response.text}") sys.exit(1) except Exception as e: print(f"Error: {e}") sys.exit(1) if __name__ == "__main__": main() ``` --- ## 五、使用示例 ### 5.1 基本使用 ```bash # 发送简单通知 python ring.py "任务完成" # 自定义标题 python ring.py "编译成功" --title "Build" # 带提示音 python ring.py "下载完成" --sound # 指定目标主机 python ring.py "远程任务完成" --host 192.168.1.100 --port 3002 ``` ### 5.2 在脚本中使用 ```bash # 长时间任务完成后通知 npm run build && python ring.py "Build completed" --sound # 或者在脚本中 long_running_command python ring.py "Command finished: $?" ``` --- ## 六、配置说明 ### config/default.json ```json { "ring": { "enabled": true, "port": 3002 } } ``` --- ## 七、安全考虑 1. **内网使用** - Ring Server 默认只监听内网,不暴露到公网 2. **可选认证** - 后续可添加简单 token 认证 3. **频率限制** - 防止通知轰炸 --- ## 八、测试计划 1. 启动应用,验证 Ring Server 在 3002 端口启动 2. 使用 curl 测试:`curl -X POST http://localhost:3002/ring -H "Content-Type: application/json" -d '{"message":"test"}'` 3. 验证 Windows 通知弹出 4. 测试 ring.py 脚本 5. 测试远程主机调用