771 lines
19 KiB
Markdown
771 lines
19 KiB
Markdown
# WXAuto Center 开发指南
|
||
|
||
## 开发环境搭建
|
||
|
||
### 环境要求
|
||
|
||
- Python 3.8+
|
||
- Node.js 14+ (可选,用于前端开发)
|
||
- Git
|
||
|
||
### 克隆与安装
|
||
|
||
```bash
|
||
# 克隆项目
|
||
git clone <repository-url>
|
||
cd wxauto_api/wxauto_center
|
||
|
||
# 创建虚拟环境(推荐)
|
||
python -m venv venv
|
||
source venv/bin/activate # Linux/Mac
|
||
# 或 venv\Scripts\activate # Windows
|
||
|
||
# 安装依赖
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
### 配置开发环境
|
||
|
||
```bash
|
||
# 复制环境变量示例文件
|
||
cp .env.example .env
|
||
|
||
# 编辑 .env 文件
|
||
vim .env
|
||
```
|
||
|
||
### 启动开发服务器
|
||
|
||
```bash
|
||
# 方式1:使用 run.py(推荐)
|
||
python run.py
|
||
|
||
# 方式2:单进程开发模式
|
||
python run.py --reload
|
||
|
||
# 方式3:使用 uvicorn
|
||
uvicorn main:app --host 0.0.0.0 --port 8080 --reload
|
||
|
||
# 方式4:多进程模式(需要 Redis)
|
||
python run.py --workers 4
|
||
```
|
||
|
||
### 访问服务
|
||
|
||
- 主界面:http://localhost:8080/static/index.html
|
||
- API 文档:http://localhost:8080/docs
|
||
- ReDoc:http://localhost:8080/redoc
|
||
|
||
---
|
||
|
||
## 项目结构详解
|
||
|
||
### 目录结构
|
||
|
||
```
|
||
wxauto_center/
|
||
├── main.py # FastAPI 应用入口
|
||
├── run.py # 多进程启动脚本
|
||
├── config.py # 配置管理模块
|
||
├── api/ # API 路由目录
|
||
│ ├── __init__.py
|
||
│ ├── nodes.py # 节点管理 API
|
||
│ ├── messages.py # 消息发送 API
|
||
│ ├── external.py # 外部扩展 API
|
||
│ ├── callback.py # 回调接口 API
|
||
│ └── plugins.py # 插件管理 API
|
||
├── services/ # 业务逻辑目录
|
||
│ ├── __init__.py
|
||
│ ├── node_manager.py # 节点管理器
|
||
│ ├── monitor_service.py # 监控服务
|
||
│ ├── log_service.py # 日志服务(支持Redis+文件持久化)
|
||
│ └── queue_service.py # 消息队列服务(支持Redis后端)
|
||
├── plugins/ # 插件目录
|
||
│ ├── __init__.py
|
||
│ ├── base.py # 插件基类
|
||
│ └── builtin.py # 内置插件
|
||
├── models/ # 数据模型目录
|
||
├── utils/ # 工具函数目录
|
||
├── static/ # 静态文件目录
|
||
│ ├── index.html # 主页面
|
||
│ ├── css/ # 样式目录
|
||
│ └── js/ # JavaScript 目录
|
||
├── logs/ # 日志目录
|
||
│ └── app.log # 应用日志
|
||
├── config.json # 配置文件
|
||
└── requirements.txt # 依赖列表
|
||
```
|
||
|
||
---
|
||
|
||
## 代码架构说明
|
||
|
||
### 主入口 (main.py)
|
||
|
||
`main.py` 是 FastAPI 应用的入口文件,负责:
|
||
|
||
1. 初始化 FastAPI 应用
|
||
2. 配置 CORS 中间件
|
||
3. 注册路由(nodes, messages, external, plugins)
|
||
4. 处理启动/关闭事件
|
||
|
||
关键代码流程:
|
||
|
||
```python
|
||
# 启动事件
|
||
@app.on_event("startup")
|
||
async def startup_event():
|
||
load_config_from_file("config.json") # 加载配置
|
||
register_builtin_plugins() # 注册内置插件
|
||
for node_data in config_manager.get_all_nodes():
|
||
await node_manager.add_node(...) # 添加节点
|
||
await monitor_service.start() # 启动监控服务
|
||
|
||
# 关闭事件
|
||
@app.on_event("shutdown")
|
||
async def shutdown_event():
|
||
await monitor_service.stop() # 停止监控
|
||
save_config_to_file("config.json") # 保存配置
|
||
```
|
||
|
||
### 配置管理 (config.py)
|
||
|
||
采用 Pydantic Settings 进行配置管理:
|
||
|
||
```python
|
||
# 配置模型
|
||
Settings # 主配置类
|
||
NodeConfig # 节点配置
|
||
PluginConfig # 插件配置
|
||
WebhookConfig # Webhook 配置
|
||
MonitorConfig # 监控配置
|
||
|
||
# 配置管理器
|
||
ConfigManager # 提供节点配置的增删改查
|
||
|
||
# 配置加载/保存
|
||
load_config_from_file() # 从文件加载
|
||
save_config_to_file() # 保存到文件
|
||
```
|
||
|
||
### 节点管理器 (services/node_manager.py)
|
||
|
||
`NodeManager` 是核心服务类,负责与 WXAuto-HTTP-API 节点通信。
|
||
|
||
```python
|
||
# 节点状态
|
||
class NodeStatus:
|
||
ACTIVE = "active" # API 可用
|
||
INACTIVE = "inactive" # API 不可用
|
||
ERROR = "error" # API 错误
|
||
|
||
# 节点类
|
||
class Node:
|
||
node_id: str # 节点唯一标识
|
||
name: str # 节点显示名称
|
||
api_url: str
|
||
api_key: str
|
||
description: str
|
||
group: str
|
||
enabled: bool
|
||
status: str # NodeStatus
|
||
wechat_status: str
|
||
is_healthy: bool
|
||
health_message: str
|
||
|
||
# NodeManager 方法
|
||
class NodeManager:
|
||
async def add_node(...) # 添加节点
|
||
async def remove_node(node_id) # 删除节点
|
||
async def get_node(node_id) # 获取单个节点
|
||
async def get_all_nodes() # 获取所有节点
|
||
async def get_enabled_nodes() # 获取已启用节点
|
||
async def get_nodes_by_group() # 按分组获取节点
|
||
async def update_node(node_id, **kwargs) # 更新节点
|
||
async def check_node_status(node_id) # 检查节点状态
|
||
async def is_node_healthy(node_id) # 检查节点健康状态
|
||
async def send_message(node_id, who, msg, msg_type) # 发送消息
|
||
async def send_message_to_group(node_id, group_name, msg, msg_type) # 发送群消息
|
||
async def get_friends_list(node_id) # 获取好友列表
|
||
async def get_groups_list(node_id) # 获取群列表
|
||
async def get_messages(node_id, chat_name, limit) # 获取消息历史
|
||
|
||
**注意**:已移除 `broadcast_message()` 方法,广播功能不再支持。
|
||
|
||
### 监控服务 (services/monitor_service.py)
|
||
|
||
`MonitorService` 负责定时检测节点和微信状态。
|
||
|
||
```python
|
||
class MonitorService:
|
||
async def start() # 启动监控
|
||
async def stop() # 停止监控
|
||
async def _monitor_loop() # 主监控循环
|
||
async def _check_all_nodes() # 检查所有节点
|
||
async def _check_node(node_id) # 检查单个节点
|
||
async def _send_webhook(event, data) # 发送Webhook通知
|
||
def get_last_status(node_id) # 获取最后状态
|
||
def get_all_status() # 获取所有状态
|
||
```
|
||
|
||
### 日志服务 (services/log_service.py)
|
||
|
||
`LogService` 提供日志记录和实时推送功能。
|
||
|
||
```python
|
||
class LogService:
|
||
def __init__(self, max_logs=500):
|
||
self.max_logs = max_logs
|
||
self._logs: deque = deque(maxlen=max_logs) # 限制500条
|
||
self._subscribers: List[asyncio.Queue] = []
|
||
|
||
# 日志方法
|
||
def info/warning/error/success(message, source)
|
||
|
||
# SSE 订阅
|
||
async def subscribe() -> asyncio.Queue # 订阅日志流
|
||
def unsubscribe(queue) # 取消订阅
|
||
|
||
# 日志查询
|
||
def get_recent_logs(count) # 获取最近日志
|
||
def get_all_logs() # 获取所有日志
|
||
```
|
||
|
||
---
|
||
|
||
## 插件开发指南
|
||
|
||
### 插件架构
|
||
|
||
系统采用插件化设计,支持扩展:
|
||
|
||
```
|
||
PluginBase (抽象基类)
|
||
├── MessageHandlerPlugin
|
||
├── DataSourcePlugin
|
||
├── ActionTriggerPlugin
|
||
└── AIAgentPlugin
|
||
```
|
||
|
||
### 开发自定义插件
|
||
|
||
#### 1. 创建插件类
|
||
|
||
```python
|
||
# my_plugin.py
|
||
from plugins.base import PluginBase, PluginType
|
||
|
||
class MyCustomPlugin(PluginBase):
|
||
plugin_name = "my_custom_plugin"
|
||
plugin_version = "1.0.0"
|
||
plugin_type = PluginType.CUSTOM
|
||
plugin_description = "我的自定义插件"
|
||
|
||
def initialize(self, config: Dict[str, Any]) -> bool:
|
||
self.config = config
|
||
# 初始化逻辑
|
||
return True
|
||
|
||
def execute(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
||
# 插件逻辑
|
||
return {"success": True, "result": "done"}
|
||
```
|
||
|
||
#### 2. 注册插件
|
||
|
||
```python
|
||
# 在 builtin.py 或 main.py 中注册
|
||
from plugins.base import PluginRegistry
|
||
|
||
my_plugin = MyCustomPlugin()
|
||
my_plugin.initialize({"key": "value"})
|
||
PluginRegistry.register(my_plugin)
|
||
```
|
||
|
||
### 插件类型详解
|
||
|
||
#### DataSourcePlugin
|
||
|
||
用于从外部数据源获取数据:
|
||
|
||
```python
|
||
class MyDataSourcePlugin(DataSourcePlugin):
|
||
plugin_name = "my_data_source"
|
||
|
||
def fetch_data(self, query: str, params: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||
# 获取数据逻辑
|
||
return [...]
|
||
```
|
||
|
||
#### AIAgentPlugin
|
||
|
||
用于 AI 消息处理:
|
||
|
||
```python
|
||
class MyAIAgentPlugin(AIAgentPlugin):
|
||
plugin_name = "my_ai_agent"
|
||
|
||
def process(self, input_text: str, context: Dict[str, Any]) -> str:
|
||
# 处理输入,返回 AI 回复
|
||
return "AI 回复"
|
||
|
||
def get_response(self, messages: List[Dict[str, str]]) -> str:
|
||
# 获取对话响应
|
||
return "AI 回复"
|
||
```
|
||
|
||
---
|
||
|
||
## API 调用示例
|
||
|
||
### 使用 Python 调用
|
||
|
||
```python
|
||
import httpx
|
||
|
||
API_KEY = "your-external-api-key"
|
||
BASE_URL = "http://localhost:8080/api/v1"
|
||
|
||
headers = {
|
||
"X-API-Key": API_KEY,
|
||
"Content-Type": "application/json"
|
||
}
|
||
|
||
# 添加节点
|
||
response = httpx.post(
|
||
f"{BASE_URL}/nodes",
|
||
json={
|
||
"node_id": "wx1",
|
||
"name": "微信1",
|
||
"api_url": "http://192.168.1.100:5000",
|
||
"api_key": "node-api-key"
|
||
},
|
||
headers=headers
|
||
)
|
||
print(response.json())
|
||
|
||
# 发送消息
|
||
response = httpx.post(
|
||
f"{BASE_URL}/messages/send",
|
||
json={
|
||
"node_id": "wx1",
|
||
"who": "wxid_friend",
|
||
"msg": "Hello!"
|
||
},
|
||
headers=headers
|
||
)
|
||
print(response.json())
|
||
```
|
||
|
||
### 使用 curl 调用
|
||
|
||
```bash
|
||
# 添加节点
|
||
curl -X POST http://localhost:8080/api/v1/nodes \
|
||
-H "X-API-Key: your-external-api-key" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"node_id": "wx1", "name": "微信1", "api_url": "http://192.168.1.100:5000", "api_key": "key"}'
|
||
|
||
# 发送消息
|
||
curl -X POST http://localhost:8080/api/v1/messages/send \
|
||
-H "X-API-Key: your-external-api-key" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"node_id": "wx1", "who": "wxid_friend", "msg": "Hello"}'
|
||
|
||
# 获取节点状态
|
||
curl http://localhost:8080/api/v1/nodes/wx1/status \
|
||
-H "X-API-Key: your-external-api-key"
|
||
|
||
# 获取监控状态
|
||
curl http://localhost:8080/api/v1/monitor/status \
|
||
-H "X-API-Key: your-external-api-key"
|
||
|
||
# 获取最近日志
|
||
curl "http://localhost:8080/api/v1/logs/recent?count=20" \
|
||
-H "X-API-Key: your-external-api-key"
|
||
|
||
# 重置节点微信状态(回调接口)
|
||
curl -X POST http://localhost:8080/api/v1/callback/node/reset \
|
||
-H "X-API-Key: your-external-api-key" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"node_id": "wx1"}'
|
||
```
|
||
|
||
### 使用 JavaScript 调用
|
||
|
||
```javascript
|
||
const API_KEY = 'your-external-api-key';
|
||
const BASE_URL = 'http://localhost:8080/api/v1';
|
||
|
||
async function sendMessage(nodeId, who, msg) {
|
||
const response = await fetch(`${BASE_URL}/messages/send`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-API-Key': API_KEY,
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
node_id: nodeId,
|
||
who: who,
|
||
msg: msg
|
||
})
|
||
});
|
||
return await response.json();
|
||
}
|
||
|
||
// 使用示例
|
||
sendMessage('wx1', 'wxid_friend', 'Hello!')
|
||
.then(result => console.log(result));
|
||
```
|
||
|
||
---
|
||
|
||
## 前端开发
|
||
|
||
### 前端架构
|
||
|
||
前端采用纯 JavaScript 模块化设计:
|
||
|
||
```
|
||
static/
|
||
├── index.html # 主页面
|
||
├── css/
|
||
│ └── style.css # 样式文件
|
||
└── js/
|
||
├── api.js # API 调用封装
|
||
├── app.js # 主逻辑
|
||
└── pages/
|
||
└── templates.js # 页面模板
|
||
```
|
||
|
||
### 页面模块
|
||
|
||
系统前端包含以下页面:
|
||
|
||
| 页面 | 功能 |
|
||
|------|------|
|
||
| dashboard | 系统概览 |
|
||
| nodes | 节点管理 |
|
||
| monitor | 监控状态 |
|
||
| send | 消息发送 |
|
||
| logs | 日志查看 |
|
||
| external | 外部接口 |
|
||
| docs | 文档 |
|
||
| settings | 设置 |
|
||
|
||
### API 封装 (api.js)
|
||
|
||
```javascript
|
||
// 基础配置
|
||
const API_BASE = '/api/v1';
|
||
const API_KEY = 'your-api-key';
|
||
|
||
// 通用请求函数
|
||
async function apiRequest(endpoint, options = {}) {
|
||
const url = `${API_BASE}${endpoint}`;
|
||
const headers = {
|
||
'X-API-Key': API_KEY,
|
||
'Content-Type': 'application/json',
|
||
...options.headers
|
||
};
|
||
|
||
const response = await fetch(url, {
|
||
...options,
|
||
headers
|
||
});
|
||
|
||
return response.json();
|
||
}
|
||
|
||
// API 方法封装
|
||
const API = {
|
||
// 节点管理
|
||
getNodes: (group) => apiRequest(`/nodes${group ? `?group=${group}` : ''}`),
|
||
addNode: (data) => apiRequest('/nodes', { method: 'POST', body: JSON.stringify(data) }),
|
||
deleteNode: (name) => apiRequest(`/nodes/${name}`, { method: 'DELETE' }),
|
||
|
||
// 消息发送
|
||
sendMessage: (data) => apiRequest('/messages/send', { method: 'POST', body: JSON.stringify(data) }),
|
||
sendGroupMessage: (data) => apiRequest('/messages/send_group', { method: 'POST', body: JSON.stringify(data) }),
|
||
batchSendMessage: (data) => apiRequest('/messages/batch', { method: 'POST', body: JSON.stringify(data) }),
|
||
|
||
// 监控状态
|
||
getMonitorStatus: () => apiRequest('/monitor/status'),
|
||
getRecentLogs: (count) => apiRequest(`/logs/recent?count=${count || 50}`)
|
||
};
|
||
```
|
||
|
||
### SSE 日志订阅
|
||
|
||
```javascript
|
||
async function subscribeLogs(callback) {
|
||
const eventSource = new EventSource('/api/v1/logs/stream');
|
||
|
||
eventSource.onmessage = (event) => {
|
||
const logEntry = JSON.parse(event.data);
|
||
callback(logEntry);
|
||
};
|
||
|
||
eventSource.onerror = (error) => {
|
||
console.error('SSE Error:', error);
|
||
eventSource.close();
|
||
};
|
||
|
||
return eventSource; // 使用完毕后调用 close()
|
||
}
|
||
|
||
// 使用示例
|
||
const es = subscribeLogs((log) => {
|
||
console.log(`[${log.level}] ${log.message}`);
|
||
});
|
||
// 关闭时: es.close();
|
||
```
|
||
|
||
---
|
||
|
||
## 测试
|
||
|
||
### 运行测试
|
||
|
||
```bash
|
||
# 安装测试依赖
|
||
pip install pytest pytest-asyncio httpx
|
||
|
||
# 运行所有测试
|
||
pytest
|
||
|
||
# 运行指定测试文件
|
||
pytest tests/test_nodes.py
|
||
|
||
# 带详细输出
|
||
pytest -v
|
||
```
|
||
|
||
### 编写测试
|
||
|
||
```python
|
||
# tests/test_nodes.py
|
||
import pytest
|
||
from httpx import AsyncClient
|
||
from main import app
|
||
|
||
@pytest.mark.asyncio
|
||
async def test_add_node():
|
||
async with AsyncClient(app=app, base_url="http://test") as client:
|
||
response = await client.post(
|
||
"/api/v1/nodes",
|
||
json={
|
||
"name": "test_node",
|
||
"api_url": "http://localhost:5000",
|
||
"api_key": "test_key"
|
||
},
|
||
headers={"X-API-Key": "test-key"}
|
||
)
|
||
assert response.status_code == 200
|
||
data = response.json()
|
||
assert data["success"] is True
|
||
```
|
||
|
||
---
|
||
|
||
## 调试技巧
|
||
|
||
### 启用调试模式
|
||
|
||
```bash
|
||
# 方式1:环境变量
|
||
export DEBUG=true
|
||
python main.py
|
||
|
||
# 方式2:命令行参数(如果支持)
|
||
uvicorn main:app --reload --log-level debug
|
||
|
||
# 方式3:修改 .env
|
||
DEBUG=true
|
||
```
|
||
|
||
### 查看详细日志
|
||
|
||
系统内置日志服务,可通过 API 查看:
|
||
|
||
```bash
|
||
# 获取最近日志
|
||
curl http://localhost:8080/api/v1/logs/recent?count=100 \
|
||
-H "X-API-Key: your-api-key"
|
||
|
||
# 订阅实时日志流
|
||
curl -N http://localhost:8080/api/v1/logs/stream \
|
||
-H "X-API-Key: your-api-key"
|
||
```
|
||
|
||
### API 测试工具
|
||
|
||
- FastAPI 内置 docs:http://localhost:8080/docs
|
||
- ReDoc:http://localhost:8080/redoc
|
||
- Postman / Insomnia
|
||
|
||
---
|
||
|
||
## 代码规范
|
||
|
||
### Python 代码规范
|
||
|
||
遵循 PEP 8 和项目规则:
|
||
|
||
- 使用 4 空格缩进
|
||
- 类名使用 PascalCase
|
||
- 函数/变量使用 camelCase
|
||
- 常量使用 UPPER_SNAKE_CASE
|
||
- 每个文件包含头部注释
|
||
|
||
### 注释要求
|
||
|
||
```python
|
||
# ///
|
||
# filename.py
|
||
# 描述:该文件负责 [核心功能]
|
||
# 作者:AI Generated
|
||
# 创建日期:YYYY-MM-DD
|
||
# ///
|
||
|
||
def function_name(param1, param2):
|
||
"""
|
||
函数简要描述
|
||
|
||
@param {type} param1 - 参数说明
|
||
@return {type} 返回值说明
|
||
@throws {异常类型} 异常情况说明
|
||
"""
|
||
pass
|
||
```
|
||
|
||
---
|
||
|
||
## 部署注意事项
|
||
|
||
### 生产环境配置
|
||
|
||
1. 修改 `secret_key` 为强密码
|
||
2. 设置 `debug: false`
|
||
3. 配置正确的 `cors_origins`
|
||
4. 使用 HTTPS
|
||
5. 配置防火墙规则
|
||
|
||
### 使用 Nginx 反向代理
|
||
|
||
```nginx
|
||
server {
|
||
listen 80;
|
||
server_name your-domain.com;
|
||
|
||
location / {
|
||
proxy_pass http://127.0.0.1:8080;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
}
|
||
|
||
location /api {
|
||
proxy_pass http://127.0.0.1:8080;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection 'upgrade';
|
||
proxy_cache_bypass $http_upgrade;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 使用 systemd 服务
|
||
|
||
```ini
|
||
# /etc/systemd/system/wxauto-center.service
|
||
[Unit]
|
||
Description=WXAuto Center Service
|
||
After=network.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
User=your-user
|
||
WorkingDirectory=/path/to/wxauto_center
|
||
ExecStart=/path/to/venv/bin/python main.py
|
||
Restart=always
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
```
|
||
|
||
```bash
|
||
sudo systemctl enable wxauto-center
|
||
sudo systemctl start wxauto-center
|
||
```
|
||
|
||
---
|
||
|
||
## 关键实现细节
|
||
|
||
### 1. 健康检测机制 (is_node_healthy)
|
||
|
||
每次发送消息前,系统会调用此方法检测节点健康状态:
|
||
|
||
```python
|
||
async def is_node_healthy(self, name: str) -> tuple[bool, str]:
|
||
# 1. 检查 API 状态
|
||
api_status = await self.check_node_status(name)
|
||
if api_status != NodeStatus.ACTIVE:
|
||
return False, f"API status: {api_status}"
|
||
|
||
# 2. 调用 /api/wechat/initialize 检测微信
|
||
init_result = await self.call_node_api(name, "/api/wechat/initialize", method="POST")
|
||
|
||
# 3. 解析返回状态
|
||
if not init_result.get("success"):
|
||
return False, f"WeChat check failed: {init_result.get('error')}"
|
||
|
||
# 4. 检查 code 和 status
|
||
wx_code = init_result.get("code") or init_result.get("data", {}).get("code")
|
||
wx_status = init_result.get("data", {}).get("data", {}).get("status")
|
||
|
||
if wx_code != 0:
|
||
return False, "WeChat error: ..."
|
||
|
||
if wx_status not in ["connected", "online"]:
|
||
return False, f"WeChat status: {wx_status}"
|
||
|
||
return True, "OK"
|
||
```
|
||
|
||
### 2. 消息发送流程
|
||
|
||
```python
|
||
async def send_message(self, name: str, who: str, msg: str, msg_type: str = "text"):
|
||
# 1. 先初始化微信
|
||
await self.call_node_api(name, "/api/wechat/initialize", method="POST")
|
||
|
||
# 2. 发送消息
|
||
return await self.call_node_api(
|
||
name,
|
||
"/api/message/send",
|
||
method="POST",
|
||
data={"receiver": who, "message": msg}
|
||
)
|
||
```
|
||
|
||
### 3. 监控告警流程
|
||
|
||
```python
|
||
async def _check_node(self, node_id: str):
|
||
# 检测 API 状态变化
|
||
api_status = await node_manager.check_node_status(node_id)
|
||
if old_api_status != api_status:
|
||
event = "node_online" if api_status == "active" else "node_offline"
|
||
await self._send_webhook(event, {...})
|
||
|
||
# 检测微信状态变化(通过发送消息检测)
|
||
wechat_status = node.wechat_status # 从节点对象获取
|
||
if old_wechat_status != wechat_status:
|
||
event = "wechat_online" if wechat_status == "online" else "wechat_offline"
|
||
await self._send_webhook(event, {...})
|
||
```
|
||
|
||
**注意**:微信状态现在通过消息发送结果自动更新,不需要主动检测。
|