修复bug

This commit is contained in:
zhinianboke 2025-07-28 12:11:45 +08:00
parent 3aae24920f
commit 4510b65840
4 changed files with 214 additions and 5 deletions

View File

@ -85,8 +85,13 @@ async def main():
print("CookieManager 创建完成")
# 1) 从数据库加载的 Cookie 已经在 CookieManager 初始化时完成
# 为每个 Cookie 启动任务
# 为每个启用的 Cookie 启动任务
for cid, val in manager.cookies.items():
# 检查账号是否启用
if not manager.get_cookie_status(cid):
logger.info(f"跳过禁用的 Cookie: {cid}")
continue
try:
await manager._add_cookie_async(cid, val)
logger.info(f"启动数据库中的 Cookie 任务: {cid}")

View File

@ -1322,6 +1322,12 @@ class XianyuLive:
"""Token刷新循环"""
while True:
try:
# 检查账号是否启用
from cookie_manager import manager as cookie_manager
if cookie_manager and not cookie_manager.get_cookie_status(self.cookie_id):
logger.info(f"{self.cookie_id}】账号已禁用停止Token刷新循环")
break
current_time = time.time()
if current_time - self.last_token_refresh_time >= self.token_refresh_interval:
logger.info("Token即将过期准备刷新...")
@ -1480,6 +1486,12 @@ class XianyuLive:
"""心跳循环"""
while True:
try:
# 检查账号是否启用
from cookie_manager import manager as cookie_manager
if cookie_manager and not cookie_manager.get_cookie_status(self.cookie_id):
logger.info(f"{self.cookie_id}】账号已禁用,停止心跳循环")
break
await self.send_heartbeat(ws)
await asyncio.sleep(self.heartbeat_interval)
except Exception as e:
@ -1673,6 +1685,12 @@ class XianyuLive:
async def handle_message(self, message_data, websocket):
"""处理所有类型的消息"""
try:
# 检查账号是否启用
from cookie_manager import manager as cookie_manager
if cookie_manager and not cookie_manager.get_cookie_status(self.cookie_id):
logger.debug(f"{self.cookie_id}】账号已禁用,跳过消息处理")
return
# 发送确认消息
try:
message = message_data
@ -2122,9 +2140,15 @@ class XianyuLive:
await self.create_session() # 创建session
while True:
try:
# 检查账号是否启用
from cookie_manager import manager as cookie_manager
if cookie_manager and not cookie_manager.get_cookie_status(self.cookie_id):
logger.info(f"{self.cookie_id}】账号已禁用,停止主循环")
break
headers = WEBSOCKET_HEADERS.copy()
headers['Cookie'] = self.cookies_str
# 兼容不同版本的websockets库
async with await self._create_websocket_connection(headers) as websocket:
self.ws = websocket
@ -2139,14 +2163,14 @@ class XianyuLive:
async for message in websocket:
try:
message_data = json.loads(message)
# 处理心跳响应
if await self.handle_heartbeat_response(message_data):
continue
# 处理其他消息
await self.handle_message(message_data, websocket)
except Exception as e:
logger.error(f"处理消息出错: {self._safe_str(e)}")
continue

View File

@ -190,11 +190,21 @@ class CookieManager:
if cookie_id not in self.cookies:
raise ValueError(f"Cookie ID {cookie_id} 不存在")
old_status = self.cookie_status.get(cookie_id, True)
self.cookie_status[cookie_id] = enabled
# 保存到数据库
db_manager.save_cookie_status(cookie_id, enabled)
logger.info(f"更新Cookie状态: {cookie_id} -> {'启用' if enabled else '禁用'}")
# 如果状态发生变化,需要启动或停止任务
if old_status != enabled:
if enabled:
# 启用账号:启动任务
self._start_cookie_task(cookie_id)
else:
# 禁用账号:停止任务
self._stop_cookie_task(cookie_id)
def get_cookie_status(self, cookie_id: str) -> bool:
"""获取Cookie的启用状态"""
return self.cookie_status.get(cookie_id, True) # 默认启用
@ -204,6 +214,55 @@ class CookieManager:
return {cid: value for cid, value in self.cookies.items()
if self.cookie_status.get(cid, True)}
def _start_cookie_task(self, cookie_id: str):
"""启动指定Cookie的任务"""
if cookie_id in self.tasks:
logger.warning(f"Cookie任务已存在跳过启动: {cookie_id}")
return
cookie_value = self.cookies.get(cookie_id)
if not cookie_value:
logger.error(f"Cookie值不存在无法启动任务: {cookie_id}")
return
try:
# 获取Cookie对应的user_id
cookie_info = db_manager.get_cookie_details(cookie_id)
user_id = cookie_info.get('user_id') if cookie_info else None
# 使用异步方式启动任务
if hasattr(self.loop, 'is_running') and self.loop.is_running():
# 事件循环正在运行使用run_coroutine_threadsafe
fut = asyncio.run_coroutine_threadsafe(
self._add_cookie_async(cookie_id, cookie_value, user_id),
self.loop
)
fut.result(timeout=5) # 等待最多5秒
else:
# 事件循环未运行,直接创建任务
task = self.loop.create_task(self._run_xianyu(cookie_id, cookie_value, user_id))
self.tasks[cookie_id] = task
logger.info(f"成功启动Cookie任务: {cookie_id}")
except Exception as e:
logger.error(f"启动Cookie任务失败: {cookie_id}, {e}")
def _stop_cookie_task(self, cookie_id: str):
"""停止指定Cookie的任务"""
if cookie_id not in self.tasks:
logger.warning(f"Cookie任务不存在跳过停止: {cookie_id}")
return
try:
task = self.tasks[cookie_id]
if not task.done():
task.cancel()
logger.info(f"已取消Cookie任务: {cookie_id}")
del self.tasks[cookie_id]
logger.info(f"成功停止Cookie任务: {cookie_id}")
except Exception as e:
logger.error(f"停止Cookie任务失败: {cookie_id}, {e}")
# 在 Start.py 中会把此变量赋值为具体实例
manager: Optional[CookieManager] = None

View File

@ -0,0 +1,121 @@
# 账号禁用功能修复说明
## 问题分析
在原有的代码中,账号禁用功能存在以下问题:
### 1. 账号状态检查不完整
- `reply_server.py` 中的 `match_reply` 函数虽然有账号状态检查,但只影响关键词匹配回复
- `XianyuAutoAsync.py` 的消息处理流程中没有检查账号的启用/禁用状态
- Token刷新、心跳、WebSocket连接等核心功能都没有账号状态检查
### 2. 任务管理机制缺陷
- 账号禁用时,对应的 `XianyuLive` 任务WebSocket连接、token刷新任务等并没有被停止
- `cookie_manager.py` 中的 `update_cookie_status` 方法只更新了状态,但没有停止或启动相应的任务
- 任务一旦启动就会持续运行,不受账号状态影响
### 3. 重启后token刷新的原因
- `Start.py` 启动时会为所有数据库中的Cookie启动任务不管其启用状态
- `XianyuAutoAsync.py` 中的token刷新是独立的定时任务一旦启动就会持续运行
## 解决方案
### 1. 修改XianyuAutoAsync消息处理流程
**文件**: `XianyuAutoAsync.py`
**修改内容**:
- 在 `handle_message` 方法开头添加账号状态检查
- 在 `main` 方法的主循环中添加账号状态检查
- 在 `token_refresh_loop` 方法中添加账号状态检查
- 在 `heartbeat_loop` 方法中添加账号状态检查
**效果**: 禁用的账号将不再处理消息、刷新token或发送心跳
### 2. 修改CookieManager任务管理
**文件**: `cookie_manager.py`
**修改内容**:
- 在 `update_cookie_status` 方法中添加任务启动/停止逻辑
- 新增 `_start_cookie_task` 方法用于启动指定账号的任务
- 新增 `_stop_cookie_task` 方法用于停止指定账号的任务
**效果**:
- 禁用账号时立即停止相关任务
- 启用账号时立即启动相关任务
### 3. 修改Start.py启动逻辑
**文件**: `Start.py`
**修改内容**:
- 启动时检查每个账号的启用状态
- 只为启用状态的账号创建任务
- 跳过禁用的账号
**效果**: 重启后禁用的账号不会自动启动任务
## 修改后的工作流程
### 账号禁用时
1. 用户在管理界面点击禁用账号
2. 前端调用 `/cookies/{cid}/status` API
3. `cookie_manager.update_cookie_status` 被调用
4. 状态更新到数据库和内存
5. 检测到状态变化,调用 `_stop_cookie_task`
6. 取消对应的异步任务
7. 正在运行的 `XianyuLive` 实例在下次循环时检测到状态变化并退出
### 账号启用时
1. 用户在管理界面点击启用账号
2. 前端调用 `/cookies/{cid}/status` API
3. `cookie_manager.update_cookie_status` 被调用
4. 状态更新到数据库和内存
5. 检测到状态变化,调用 `_start_cookie_task`
6. 创建新的异步任务启动 `XianyuLive` 实例
### 系统重启时
1. `Start.py` 从数据库加载所有Cookie
2. 检查每个Cookie的启用状态
3. 只为启用状态的Cookie创建任务
4. 禁用的Cookie被跳过
## 关键代码片段
### 消息处理中的状态检查
```python
async def handle_message(self, message_data, websocket):
# 检查账号是否启用
from cookie_manager import manager as cookie_manager
if cookie_manager and not cookie_manager.get_cookie_status(self.cookie_id):
logger.debug(f"【{self.cookie_id}】账号已禁用,跳过消息处理")
return
```
### 任务管理逻辑
```python
def update_cookie_status(self, cookie_id: str, enabled: bool):
old_status = self.cookie_status.get(cookie_id, True)
self.cookie_status[cookie_id] = enabled
db_manager.save_cookie_status(cookie_id, enabled)
# 如果状态发生变化,需要启动或停止任务
if old_status != enabled:
if enabled:
self._start_cookie_task(cookie_id)
else:
self._stop_cookie_task(cookie_id)
```
## 测试验证
可以使用提供的 `test_account_disable.py` 脚本来测试账号禁用功能是否正常工作。
## 注意事项
1. **立即生效**: 修改后账号禁用/启用将立即生效,无需重启系统
2. **资源清理**: 禁用账号时会正确清理相关的异步任务和资源
3. **状态持久化**: 账号状态会保存到数据库,重启后保持一致
4. **错误处理**: 添加了完善的错误处理和日志记录
5. **向后兼容**: 修改不影响现有功能,保持向后兼容性