From 4510b65840c063be97e725fd22ad3dd073bd4092 Mon Sep 17 00:00:00 2001 From: zhinianboke <115088296+zhinianboke@users.noreply.github.com> Date: Mon, 28 Jul 2025 12:11:45 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Start.py | 7 ++- XianyuAutoAsync.py | 32 +++++++++-- cookie_manager.py | 59 ++++++++++++++++++++ 账号禁用功能修复说明.md | 121 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 214 insertions(+), 5 deletions(-) create mode 100644 账号禁用功能修复说明.md diff --git a/Start.py b/Start.py index 228cd42..f5021b2 100644 --- a/Start.py +++ b/Start.py @@ -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}") diff --git a/XianyuAutoAsync.py b/XianyuAutoAsync.py index e4e7e4e..033e4fb 100644 --- a/XianyuAutoAsync.py +++ b/XianyuAutoAsync.py @@ -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 diff --git a/cookie_manager.py b/cookie_manager.py index 7e3ba43..32e3f4e 100644 --- a/cookie_manager.py +++ b/cookie_manager.py @@ -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 \ No newline at end of file diff --git a/账号禁用功能修复说明.md b/账号禁用功能修复说明.md new file mode 100644 index 0000000..bb5432e --- /dev/null +++ b/账号禁用功能修复说明.md @@ -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. **向后兼容**: 修改不影响现有功能,保持向后兼容性