Todo: 补充nsis集成需要的依赖 并提交uninstall logo的图片资源 并集成关于GUI版本控制显示代码

This commit is contained in:
2025-09-29 15:38:34 +08:00
parent c58cec750f
commit 1ac50b99a9
7 changed files with 1017 additions and 250 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -40,7 +40,7 @@ from Utils.message_models import PlatformMessage
class Track: class Track:
"""滑块轨迹生成类""" """滑块轨迹生成类"""
@staticmethod @staticmethod
def get_track(distance): def get_track(distance):
distance = int(distance) + random.randint(7, 11) + 0.8 distance = int(distance) + random.randint(7, 11) + 0.8
@@ -853,7 +853,7 @@ class Track:
class ImgDistance: class ImgDistance:
"""滑块图像识别距离计算类""" """滑块图像识别距离计算类"""
def __init__(self, bg, tp): def __init__(self, bg, tp):
self.bg = bg self.bg = bg
self.tp = tp self.tp = tp
@@ -999,8 +999,7 @@ class ImgDistance:
save_path=save_path save_path=save_path
) )
# AutiContent类已移除 - 后端会提供所有必要的anti_content
# AutiContent类已移除 - 后端会提供所有必要的anti_content
def gzip_compress(self, data): def gzip_compress(self, data):
"""压缩数据""" """压缩数据"""
@@ -1011,7 +1010,7 @@ class ImgDistance:
return result return result
except Exception as e: except Exception as e:
logger.error(f"调用gzipCompress函数失败: {e}") logger.error(f"调用gzipCompress函数失败: {e}")
# fallback处理简单返回原数据 # fallback处理简单返回原数据
logger.warning("使用fallback处理返回原数据") logger.warning("使用fallback处理返回原数据")
return data return data
@@ -1019,7 +1018,7 @@ class ImgDistance:
class EncryptTool: class EncryptTool:
"""加密工具类""" """加密工具类"""
@staticmethod @staticmethod
def aes_encrypt(text, key, iv): def aes_encrypt(text, key, iv):
cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8')) cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
@@ -1059,7 +1058,7 @@ class EncryptTool:
class PddLogin: class PddLogin:
"""拼多多登录核心类""" """拼多多登录核心类"""
def __init__(self, log_callback=None): def __init__(self, log_callback=None):
super().__init__() super().__init__()
self.login_api = "https://mms.pinduoduo.com/janus/api/auth" self.login_api = "https://mms.pinduoduo.com/janus/api/auth"
@@ -1608,11 +1607,11 @@ class PddLogin:
def request_verification_code(self, username, store_id, backend_anti_content, phone_number=None): def request_verification_code(self, username, store_id, backend_anti_content, phone_number=None):
"""向后端请求获取手机验证码""" """向后端请求获取手机验证码"""
self._log(f"开始请求验证码,用户名: {username}, 店铺ID: {store_id}, 手机号: {phone_number}", "INFO") self._log(f"开始请求验证码,用户名: {username}, 店铺ID: {store_id}, 手机号: {phone_number}", "INFO")
# 使用后端下发的anti_content # 使用后端下发的anti_content
anti_content = backend_anti_content anti_content = backend_anti_content
self._log(f"使用后端下发的anti_content", "DEBUG") self._log(f"使用后端下发的anti_content", "DEBUG")
self.headers["anti-content"] = anti_content self.headers["anti-content"] = anti_content
url = "https://mms.pinduoduo.com/janus/api/user/getLoginVerificationCode" url = "https://mms.pinduoduo.com/janus/api/user/getLoginVerificationCode"
payload = { payload = {
@@ -1621,11 +1620,11 @@ class PddLogin:
} }
response = requests.post(url, headers=self.headers, json=payload, cookies=self.cookies) response = requests.post(url, headers=self.headers, json=payload, cookies=self.cookies)
self._log(f"发送验证码请求结果: {response.text}") self._log(f"发送验证码请求结果: {response.text}")
# 发送消息给后端,告知需要验证码(包含手机号) # 发送消息给后端,告知需要验证码(包含手机号)
self._log("准备向后端发送验证码需求通知", "INFO") self._log("准备向后端发送验证码需求通知", "INFO")
self._send_verification_needed_message(store_id, phone_number) self._send_verification_needed_message(store_id, phone_number)
# 这里需要等待后端重新下发包含验证码的登录参数 # 这里需要等待后端重新下发包含验证码的登录参数
# 实际实现中这个方法会在接收到新的登录参数后被重新调用 # 实际实现中这个方法会在接收到新的登录参数后被重新调用
return None return None
@@ -1637,7 +1636,7 @@ class PddLogin:
from WebSocket.backend_singleton import get_backend_client from WebSocket.backend_singleton import get_backend_client
backend = get_backend_client() backend = get_backend_client()
self._log(f"获取到后端客户端: {backend is not None}", "DEBUG") self._log(f"获取到后端客户端: {backend is not None}", "DEBUG")
if backend: if backend:
message = { message = {
"type": "connect_message", "type": "connect_message",
@@ -1662,7 +1661,7 @@ class PddLogin:
self._log(f"开始发送验证码错误通知店铺ID: {store_id}, 错误: {error_msg}", "INFO") self._log(f"开始发送验证码错误通知店铺ID: {store_id}, 错误: {error_msg}", "INFO")
from WebSocket.backend_singleton import get_backend_client from WebSocket.backend_singleton import get_backend_client
backend = get_backend_client() backend = get_backend_client()
if backend: if backend:
message = { message = {
"type": "connect_message", "type": "connect_message",
@@ -1686,12 +1685,13 @@ class PddLogin:
self._log(f"开始发送登录成功通知店铺ID: {store_id}", "INFO") self._log(f"开始发送登录成功通知店铺ID: {store_id}", "INFO")
from WebSocket.backend_singleton import get_backend_client from WebSocket.backend_singleton import get_backend_client
backend = get_backend_client() backend = get_backend_client()
if backend: if backend:
message = { message = {
"type": "connect_message", "type": "connect_message",
"store_id": store_id, "store_id": store_id,
"status": True # 登录成功 "status": True, # 登录成功
"cookies": self.cookies # 🔥 新增添加登录生成的cookie信息
} }
self._log(f"准备发送登录成功消息: {message}", "DEBUG") self._log(f"准备发送登录成功消息: {message}", "DEBUG")
backend.send_message(message) backend.send_message(message)
@@ -1709,7 +1709,7 @@ class PddLogin:
self._log(f"开始发送登录失败通知店铺ID: {store_id}, 错误: {error_msg}", "INFO") self._log(f"开始发送登录失败通知店铺ID: {store_id}, 错误: {error_msg}", "INFO")
from WebSocket.backend_singleton import get_backend_client from WebSocket.backend_singleton import get_backend_client
backend = get_backend_client() backend = get_backend_client()
if backend: if backend:
message = { message = {
"type": "connect_message", "type": "connect_message",
@@ -1732,10 +1732,12 @@ class PddLogin:
self._log("🚀 [PddLogin] 开始使用参数登录", "INFO") self._log("🚀 [PddLogin] 开始使用参数登录", "INFO")
# 检查验证码字段(兼容 code 和 verification_code # 检查验证码字段(兼容 code 和 verification_code
verification_code = login_params.get("verification_code") or login_params.get("code", "") verification_code = login_params.get("verification_code") or login_params.get("code", "")
self._log(f"📋 [PddLogin] 登录参数: username={login_params.get('username', 'N/A')}, 包含验证码={bool(verification_code)}", "DEBUG") self._log(
f"📋 [PddLogin] 登录参数: username={login_params.get('username', 'N/A')}, 包含验证码={bool(verification_code)}",
"DEBUG")
self.headers["anti-content"] = login_params.get("anti_content", "") self.headers["anti-content"] = login_params.get("anti_content", "")
# 直接使用后端提供的参数构建登录请求 # 直接使用后端提供的参数构建登录请求
ts = login_params.get("timestamp", int(round(time.time() * 1000))) ts = login_params.get("timestamp", int(round(time.time() * 1000)))
payload = { payload = {
@@ -1801,12 +1803,12 @@ class PddLogin:
self._log(f"🔍 [Debug] 登录请求URL: {self.login_api}", "DEBUG") self._log(f"🔍 [Debug] 登录请求URL: {self.login_api}", "DEBUG")
self._log(f"🔍 [Debug] 登录请求payload: {payload}", "DEBUG") self._log(f"🔍 [Debug] 登录请求payload: {payload}", "DEBUG")
self._log(f"🔍 [Debug] 登录请求headers: {dict(self.headers)}", "DEBUG") self._log(f"🔍 [Debug] 登录请求headers: {dict(self.headers)}", "DEBUG")
response = requests.post(self.login_api, headers=self.headers, json=payload, cookies=self.cookies) response = requests.post(self.login_api, headers=self.headers, json=payload, cookies=self.cookies)
self.cookies.update(response.cookies.get_dict()) self.cookies.update(response.cookies.get_dict())
self._log(f"登录响应状态码: {response.status_code}") self._log(f"登录响应状态码: {response.status_code}")
self._log(f"登录响应内容: {response.text}") self._log(f"登录响应内容: {response.text}")
# 检查响应内容 # 检查响应内容
if "需要手机验证" in response.text: if "需要手机验证" in response.text:
self._log("✅ 检测到需要手机验证的响应", "INFO") self._log("✅ 检测到需要手机验证的响应", "INFO")
@@ -1820,7 +1822,8 @@ class PddLogin:
salt = self.vc_pre_ck_b() # 获取生成aes key和iv 的密文值 salt = self.vc_pre_ck_b() # 获取生成aes key和iv 的密文值
pictures = self.obtain_captcha() # 获取验证码图片 pictures = self.obtain_captcha() # 获取验证码图片
distance = round((ImgDistance(bg=pictures[0], tp=pictures[1]).main() * (272 / 320)) + (48.75 / 2), 2) # 计算距离 distance = round((ImgDistance(bg=pictures[0], tp=pictures[1]).main() * (272 / 320)) + (48.75 / 2),
2) # 计算距离
track_list = Track.get_track(distance=distance) # 生成轨迹 track_list = Track.get_track(distance=distance) # 生成轨迹
captcha_collect = self.captcha_collect(salt=salt, track_list=track_list) # 生成captcha_collect参数 captcha_collect = self.captcha_collect(salt=salt, track_list=track_list) # 生成captcha_collect参数
@@ -1834,7 +1837,8 @@ class PddLogin:
success_count += 1 success_count += 1
# 如果滑块成功 success_count 计数一次 成功8次还是显示验证码则失败 返回False # 如果滑块成功 success_count 计数一次 成功8次还是显示验证码则失败 返回False
if success_count < 8: if success_count < 8:
return self.login_with_params(login_params=login_params, store_id=store_id, success_count=success_count) return self.login_with_params(login_params=login_params, store_id=store_id,
success_count=success_count)
else: else:
return False return False
else: else:
@@ -1854,7 +1858,7 @@ class PddLogin:
response_data = response.json() response_data = response.json()
error_msg = response_data.get('errorMsg', '验证码验证失败') error_msg = response_data.get('errorMsg', '验证码验证失败')
self._log(f"服务器返回错误: {error_msg}", "WARNING") self._log(f"服务器返回错误: {error_msg}", "WARNING")
# 不要重新发送验证码请求,直接报告验证失败 # 不要重新发送验证码请求,直接报告验证失败
self._send_verification_error_message(store_id, error_msg) # 直接使用官方错误信息 self._send_verification_error_message(store_id, error_msg) # 直接使用官方错误信息
return "verification_code_error" # 返回特殊状态,避免重复发送消息 return "verification_code_error" # 返回特殊状态,避免重复发送消息
@@ -1864,7 +1868,7 @@ class PddLogin:
username = login_params.get("username") username = login_params.get("username")
backend_anti_content = login_params.get("anti_content") backend_anti_content = login_params.get("anti_content")
self._log(f"为用户 {username} 发送验证码使用后端anti_content", "INFO") self._log(f"为用户 {username} 发送验证码使用后端anti_content", "INFO")
# 🔥 从响应中提取手机号 # 🔥 从响应中提取手机号
phone_number = None phone_number = None
try: try:
@@ -1876,7 +1880,7 @@ class PddLogin:
self._log("⚠️ 响应中的result字段不包含有效手机号", "WARNING") self._log("⚠️ 响应中的result字段不包含有效手机号", "WARNING")
except Exception as e: except Exception as e:
self._log(f"❌ 提取手机号时出错: {e}", "DEBUG") self._log(f"❌ 提取手机号时出错: {e}", "DEBUG")
# 传递后端下发的anti_content和手机号 # 传递后端下发的anti_content和手机号
self.request_verification_code(username, store_id, backend_anti_content, phone_number) self.request_verification_code(username, store_id, backend_anti_content, phone_number)
return "need_verification_code" return "need_verification_code"
@@ -1896,6 +1900,7 @@ class PddLogin:
self._send_login_failure_message(store_id, error_msg) self._send_login_failure_message(store_id, error_msg)
return "login_failure" # 返回特殊状态,避免重复发送消息 return "login_failure" # 返回特殊状态,避免重复发送消息
# ===== 登录相关类集成结束 ===== # ===== 登录相关类集成结束 =====
@@ -1983,13 +1988,13 @@ class ChatPdd:
raise FileNotFoundError(f"找不到必需的JS文件: {file_path}") raise FileNotFoundError(f"找不到必需的JS文件: {file_path}")
return True return True
@staticmethod @staticmethod
def _get_resource_path(relative_path): def _get_resource_path(relative_path):
"""获取资源文件的绝对路径兼容PyInstaller打包环境""" """获取资源文件的绝对路径兼容PyInstaller打包环境"""
try: try:
print(f"[DEBUG] 正在解析资源路径: {relative_path}") print(f"[DEBUG] 正在解析资源路径: {relative_path}")
# PyInstaller环境下的基础路径 # PyInstaller环境下的基础路径
if hasattr(sys, '_MEIPASS'): if hasattr(sys, '_MEIPASS'):
# PyInstaller 临时目录 # PyInstaller 临时目录
@@ -2005,10 +2010,10 @@ class ChatPdd:
# 向上两级目录到项目根目录 # 向上两级目录到项目根目录
base_path = os.path.dirname(os.path.dirname(base_path)) base_path = os.path.dirname(os.path.dirname(base_path))
print(f"[DEBUG] 开发环境,计算的项目根目录: {base_path}") print(f"[DEBUG] 开发环境,计算的项目根目录: {base_path}")
resource_path = os.path.join(base_path, relative_path) resource_path = os.path.join(base_path, relative_path)
print(f"[DEBUG] 拼接后的完整资源路径: {resource_path}") print(f"[DEBUG] 拼接后的完整资源路径: {resource_path}")
# 检查路径是否存在 # 检查路径是否存在
if os.path.exists(resource_path): if os.path.exists(resource_path):
print(f"[DEBUG] ✅ 资源路径存在: {resource_path}") print(f"[DEBUG] ✅ 资源路径存在: {resource_path}")
@@ -2025,7 +2030,7 @@ class ChatPdd:
print(f"[DEBUG] 📄 {item}") print(f"[DEBUG] 📄 {item}")
except Exception as e: except Exception as e:
print(f"[DEBUG] 无法列出目录内容: {e}") print(f"[DEBUG] 无法列出目录内容: {e}")
return resource_path return resource_path
except Exception as e: except Exception as e:
print(f"[ERROR] 获取资源路径失败: {e}") print(f"[ERROR] 获取资源路径失败: {e}")
@@ -2554,12 +2559,12 @@ class ChatPdd:
"""专门判断是否为机器人消息需要过滤""" """专门判断是否为机器人消息需要过滤"""
try: try:
message_info = message_data.get("message", {}) message_info = message_data.get("message", {})
# 1. 基于消息类型过滤机器人特殊消息 # 1. 基于消息类型过滤机器人特殊消息
msg_type = message_info.get("type") msg_type = message_info.get("type")
if msg_type == 31: # 机器人干预消息(如:机器人已暂停接待) if msg_type == 31: # 机器人干预消息(如:机器人已暂停接待)
return True return True
# 2. 基于模板名称识别机器人消息 # 2. 基于模板名称识别机器人消息
template_name = message_info.get("template_name", "") template_name = message_info.get("template_name", "")
robot_templates = [ robot_templates = [
@@ -2569,14 +2574,14 @@ class ChatPdd:
] ]
if template_name in robot_templates: if template_name in robot_templates:
return True return True
# 3. 基于机器人特殊标志过滤 # 3. 基于机器人特殊标志过滤
if message_info.get("conv_silent") is True: # 静默会话标志 if message_info.get("conv_silent") is True: # 静默会话标志
return True return True
if message_info.get("no_unreply_hint") == 1: # 无需回复提示标志 if message_info.get("no_unreply_hint") == 1: # 无需回复提示标志
return True return True
# 4. 基于消息内容识别机器人提示消息 # 4. 基于消息内容识别机器人提示消息
content = message_info.get("content", "") content = message_info.get("content", "")
robot_content_patterns = [ robot_content_patterns = [
@@ -2586,18 +2591,18 @@ class ChatPdd:
"点击添加", "点击添加",
"[当前用户来自", "[当前用户来自",
] ]
if any(pattern in content for pattern in robot_content_patterns): if any(pattern in content for pattern in robot_content_patterns):
return True return True
# 5. 基于biz_context中的机器人标识 # 5. 基于biz_context中的机器人标识
biz_context = message_info.get("biz_context", {}) biz_context = message_info.get("biz_context", {})
if biz_context.get("robot_msg_id"): # 有机器人消息ID if biz_context.get("robot_msg_id"): # 有机器人消息ID
return True return True
# 不是机器人消息,不过滤 # 不是机器人消息,不过滤
return False return False
except Exception as e: except Exception as e:
self._log(f"判断机器人消息时出错: {e}", "DEBUG") self._log(f"判断机器人消息时出错: {e}", "DEBUG")
return False # 出错时不过滤,保持原有行为 return False # 出错时不过滤,保持原有行为
@@ -2628,7 +2633,7 @@ class ChatPdd:
if self.should_filter_robot_message(message_data): if self.should_filter_robot_message(message_data):
self._log("🤖 检测到机器人消息,已过滤不发送给后端", "DEBUG") self._log("🤖 检测到机器人消息,已过滤不发送给后端", "DEBUG")
return return
message_info = message_data.get("message", {}) message_info = message_data.get("message", {})
if not message_info: if not message_info:
return return
@@ -2801,6 +2806,28 @@ class ChatPdd:
except Exception as e: except Exception as e:
self._log(f"❌ 获取或发送客服列表失败: {e}", "ERROR") self._log(f"❌ 获取或发送客服列表失败: {e}", "ERROR")
# 🔥 新增Cookie登录成功后发送登录成功报告与登录参数模式保持一致
try:
if self.backend_service and hasattr(self, 'store_id') and self.store_id:
# 构建cookie字典从cookies_str解析
cookie_dict = {}
if hasattr(self, 'cookie') and self.cookie:
cookie_dict = self.cookie
message = {
"type": "connect_message",
"store_id": self.store_id,
"status": True, # 登录成功
"cookies": cookie_dict # 添加cookie信息
}
# 🔥 修复:使用正确的方法名 send_message_to_backend
await self.backend_service.send_message_to_backend(message)
self._log("✅ [PDD] 已向后端发送Cookie登录成功报告", "SUCCESS")
else:
self._log("⚠️ [PDD] 无法发送登录成功报告backend_service或store_id缺失", "WARNING")
except Exception as e:
self._log(f"⚠️ [PDD] 发送登录成功报告失败: {e}", "WARNING")
# 启动消息监听和心跳 # 启动消息监听和心跳
await asyncio.gather( await asyncio.gather(
self.heartbeat(websocket), self.heartbeat(websocket),
@@ -2959,11 +2986,11 @@ class PddListenerForGUI:
self._log("🔄 [PDD] 开始创建PddLogin实例", "DEBUG") self._log("🔄 [PDD] 开始创建PddLogin实例", "DEBUG")
pdd_login = PddLogin(log_callback=self.log_callback) pdd_login = PddLogin(log_callback=self.log_callback)
self._log("✅ [PDD] PddLogin实例创建成功", "DEBUG") self._log("✅ [PDD] PddLogin实例创建成功", "DEBUG")
self._log("🔄 [PDD] 开始执行登录", "DEBUG") self._log("🔄 [PDD] 开始执行登录", "DEBUG")
login_result = pdd_login.login_with_params(params_dict, store_id) login_result = pdd_login.login_with_params(params_dict, store_id)
self._log(f"📊 [PDD] 登录结果: {login_result}", "DEBUG") self._log(f"📊 [PDD] 登录结果: {login_result}", "DEBUG")
if login_result == "need_verification_code": if login_result == "need_verification_code":
self._log("⚠️ [PDD] 需要手机验证码,已通知后端,等待重新下发包含验证码的登录参数", "WARNING") self._log("⚠️ [PDD] 需要手机验证码,已通知后端,等待重新下发包含验证码的登录参数", "WARNING")
return "need_verification_code" # 返回特殊标识,避免被覆盖 return "need_verification_code" # 返回特殊标识,避免被覆盖
@@ -2979,7 +3006,7 @@ class PddListenerForGUI:
elif isinstance(login_result, dict): elif isinstance(login_result, dict):
# 登录成功获取到cookies # 登录成功获取到cookies
self._log("✅ [PDD] 登录成功使用获取的cookies连接平台", "SUCCESS") self._log("✅ [PDD] 登录成功使用获取的cookies连接平台", "SUCCESS")
# 将cookies字典转换为字符串格式与原有逻辑兼容 # 将cookies字典转换为字符串格式与原有逻辑兼容
import json import json
cookies_str = json.dumps(login_result) cookies_str = json.dumps(login_result)
@@ -3080,6 +3107,7 @@ class PddListenerForGUI:
cookies_str=cookies, cookies_str=cookies,
store=store store=store
) )
self._log("✅ [PDD] 拼多多平台连接成功,开始监听消息", "SUCCESS") self._log("✅ [PDD] 拼多多平台连接成功,开始监听消息", "SUCCESS")
return True return True
except Exception as e: except Exception as e:
@@ -3125,8 +3153,6 @@ class PddListenerForGUI:
self._log(f"❌ [PDD] 解析登录参数失败: {e}", "ERROR") self._log(f"❌ [PDD] 解析登录参数失败: {e}", "ERROR")
return {} return {}
def get_status(self) -> Dict[str, Any]: def get_status(self) -> Dict[str, Any]:
return { return {
"running": self.running, "running": self.running,

View File

@@ -30,6 +30,7 @@ class BackendClient:
self.login_callback: Optional[Callable] = None # 新增平台登录下发cookies回调 self.login_callback: Optional[Callable] = None # 新增平台登录下发cookies回调
self.success_callback: Optional[Callable] = None # 新增:后端连接成功回调 self.success_callback: Optional[Callable] = None # 新增:后端连接成功回调
self.token_error_callback: Optional[Callable] = None # 新增token错误回调 self.token_error_callback: Optional[Callable] = None # 新增token错误回调
self.version_callback: Optional[Callable] = None # 新增:版本检查回调
self.is_connected = False self.is_connected = False
@@ -100,15 +101,15 @@ class BackendClient:
ping_timeout=WS_PING_TIMEOUT, # 可配置ping超时 ping_timeout=WS_PING_TIMEOUT, # 可配置ping超时
close_timeout=10, # 10秒关闭超时 close_timeout=10, # 10秒关闭超时
# 增加TCP keepalive配置 # 增加TCP keepalive配置
max_size=2**20, # 1MB最大消息大小 max_size=2 ** 20, # 1MB最大消息大小
max_queue=32, # 最大队列大小 max_queue=32, # 最大队列大小
compression=None # 禁用压缩以提高性能 compression=None # 禁用压缩以提高性能
) )
print(f"[连接] 已启用心跳ping_interval={WS_PING_INTERVAL}s, ping_timeout={WS_PING_TIMEOUT}s") print(f"[连接] 已启用心跳ping_interval={WS_PING_INTERVAL}s, ping_timeout={WS_PING_TIMEOUT}s")
else: else:
self.websocket = await websockets.connect( self.websocket = await websockets.connect(
self.url, self.url,
max_size=2**20, max_size=2 ** 20,
max_queue=32, max_queue=32,
compression=None compression=None
) )
@@ -118,13 +119,13 @@ class BackendClient:
self.reconnect_attempts = 0 # 重置重连计数 self.reconnect_attempts = 0 # 重置重连计数
self.is_reconnecting = False self.is_reconnecting = False
print("后端WebSocket连接成功") print("后端WebSocket连接成功")
# 等待连接稳定后再发送状态通知 # 等待连接稳定后再发送状态通知
await asyncio.sleep(0.5) await asyncio.sleep(0.5)
# 发送连接状态通知给后端 # 发送连接状态通知给后端
self._notify_connection_status(True) self._notify_connection_status(True)
self.on_connected() self.on_connected()
# 消息循环 # 消息循环
@@ -144,7 +145,7 @@ class BackendClient:
except websockets.ConnectionClosed as e: except websockets.ConnectionClosed as e:
self.is_connected = False self.is_connected = False
self._notify_connection_status(False) # 通知断开 self._notify_connection_status(False) # 通知断开
# 详细分析断开原因 # 详细分析断开原因
if e.code == 1006: if e.code == 1006:
print(f"[重连] WebSocket异常关闭 (1006): 可能是心跳超时或网络问题") print(f"[重连] WebSocket异常关闭 (1006): 可能是心跳超时或网络问题")
@@ -154,7 +155,7 @@ class BackendClient:
print(f"[重连] WebSocket关闭 (1001): 端点离开") print(f"[重连] WebSocket关闭 (1001): 端点离开")
else: else:
print(f"[重连] WebSocket关闭 ({e.code}): {e.reason}") print(f"[重连] WebSocket关闭 ({e.code}): {e.reason}")
self._handle_connection_closed(e) self._handle_connection_closed(e)
if not await self._should_reconnect(): if not await self._should_reconnect():
break break
@@ -263,7 +264,8 @@ class BackendClient:
error: Callable = None, error: Callable = None,
login: Callable = None, login: Callable = None,
success: Callable = None, success: Callable = None,
token_error: Callable = None): token_error: Callable = None,
version: Callable = None):
"""设置各种消息类型的回调函数""" """设置各种消息类型的回调函数"""
if store_list: if store_list:
self.store_list_callback = store_list self.store_list_callback = store_list
@@ -283,6 +285,8 @@ class BackendClient:
self.success_callback = success self.success_callback = success
if token_error: if token_error:
self.token_error_callback = token_error self.token_error_callback = token_error
if version:
self.version_callback = version
def on_connected(self): def on_connected(self):
"""连接成功时的处理""" """连接成功时的处理"""
@@ -337,14 +341,14 @@ class BackendClient:
try: try:
if not self.loop: if not self.loop:
return return
# 获取当前活跃平台的store_id # 获取当前活跃平台的store_id
active_store_id = None active_store_id = None
try: try:
from Utils.JD.JdUtils import WebsocketManager as JdManager from Utils.JD.JdUtils import WebsocketManager as JdManager
from Utils.Dy.DyUtils import DouYinWebsocketManager as DyManager from Utils.Dy.DyUtils import DouYinWebsocketManager as DyManager
from Utils.Pdd.PddUtils import WebsocketManager as PddManager from Utils.Pdd.PddUtils import WebsocketManager as PddManager
# 检查各平台是否有活跃连接 # 检查各平台是否有活跃连接
for mgr_class, platform_name in [(JdManager, "京东"), (DyManager, "抖音"), (PddManager, "拼多多")]: for mgr_class, platform_name in [(JdManager, "京东"), (DyManager, "抖音"), (PddManager, "拼多多")]:
try: try:
@@ -365,30 +369,30 @@ class BackendClient:
continue continue
except Exception as e: except Exception as e:
print(f"[状态] 获取活跃平台信息失败: {e}") print(f"[状态] 获取活跃平台信息失败: {e}")
status_message = { status_message = {
"type": "connection_status", "type": "connection_status",
"status": connected, "status": connected,
"timestamp": int(time.time()), "timestamp": int(time.time()),
"client_uuid": self.uuid "client_uuid": self.uuid
} }
# 如果有活跃平台添加store_id # 如果有活跃平台添加store_id
if active_store_id: if active_store_id:
status_message["store_id"] = active_store_id status_message["store_id"] = active_store_id
print(f"[状态] 添加store_id到状态消息: {active_store_id}") print(f"[状态] 添加store_id到状态消息: {active_store_id}")
else: else:
print(f"[状态] 未检测到活跃平台不添加store_id") print(f"[状态] 未检测到活跃平台不添加store_id")
# 异步发送状态通知 # 异步发送状态通知
asyncio.run_coroutine_threadsafe( asyncio.run_coroutine_threadsafe(
self._send_to_backend(status_message), self._send_to_backend(status_message),
self.loop self.loop
) )
status_text = "连接" if connected else "断开" status_text = "连接" if connected else "断开"
print(f"[状态] 已通知后端GUI客户端{status_text}") print(f"[状态] 已通知后端GUI客户端{status_text}")
except Exception as e: except Exception as e:
print(f"[状态] 发送状态通知失败: {e}") print(f"[状态] 发送状态通知失败: {e}")
import traceback import traceback
@@ -437,6 +441,8 @@ class BackendClient:
self._handle_token_error(message) self._handle_token_error(message)
elif msg_type == 'staff_list': elif msg_type == 'staff_list':
self._handle_staff_list(message) self._handle_staff_list(message)
elif msg_type == 'version_response': # 新增:版本检查响应
self._handle_version_response(message)
else: else:
print(f"未知消息类型: {msg_type}") print(f"未知消息类型: {msg_type}")
@@ -1151,12 +1157,14 @@ class BackendClient:
content = message.get('content', '') content = message.get('content', '')
data = message.get('data', {}) data = message.get('data', {})
# 判断是拼多多登录参数还是普通Cookie # 🔥 判断是登录参数模式还是普通Cookie模式(支持拼多多和抖音)
if platform_name == "拼多多" and ("拼多多登录" in content) and data.get('login_params'): if (platform_name in ["拼多多", "抖音"] and
# 拼多多登录参数模式 - 传递完整的消息JSON给处理器 (("拼多多登录" in content and data.get('login_params')) or
print(f"收到拼多多登录参数: 平台={platform_name}, 店铺={store_id}, 类型={content}") ("抖音登录" in content and data.get('login_flow')))):
# 登录参数模式 - 传递完整的消息JSON给处理器
print(f"收到{platform_name}登录参数: 平台={platform_name}, 店铺={store_id}, 类型={content}")
if self.login_callback: if self.login_callback:
# 传递完整的JSON消息拼多多处理器来解析login_params # 传递完整的JSON消息让处理器来解析登录参数
import json import json
full_message = json.dumps(message) full_message = json.dumps(message)
self.login_callback(platform_name, store_id, full_message) self.login_callback(platform_name, store_id, full_message)
@@ -1170,13 +1178,13 @@ class BackendClient:
"""处理错误消息""" """处理错误消息"""
error_msg = message.get('error', '未知错误') error_msg = message.get('error', '未知错误')
content = message.get('content', '') content = message.get('content', '')
# 检查是否为token错误无论type是error还是error_token # 检查是否为token错误无论type是error还是error_token
if content == "无效的exe_token" or "无效的exe_token" in content: if content == "无效的exe_token" or "无效的exe_token" in content:
print(f"[错误] 检测到token错误: {content}") print(f"[错误] 检测到token错误: {content}")
self._handle_token_error(message) self._handle_token_error(message)
return return
print(f"后端连接错误: {error_msg}") print(f"后端连接错误: {error_msg}")
if self.error_callback: if self.error_callback:
self.error_callback(error_msg, message) self.error_callback(error_msg, message)
@@ -1185,15 +1193,15 @@ class BackendClient:
"""处理token错误消息 - 无效token时停止重连并显示错误""" """处理token错误消息 - 无效token时停止重连并显示错误"""
error_content = message.get('content', '无效的exe_token') error_content = message.get('content', '无效的exe_token')
print(f"[错误] Token验证失败: {error_content}") print(f"[错误] Token验证失败: {error_content}")
# 停止重连机制 # 停止重连机制
self.should_stop = True self.should_stop = True
self.is_reconnecting = False self.is_reconnecting = False
# 触发token错误回调 # 触发token错误回调
if self.token_error_callback: if self.token_error_callback:
self.token_error_callback(error_content) self.token_error_callback(error_content)
# 主动关闭连接 # 主动关闭连接
if self.websocket: if self.websocket:
asyncio.run_coroutine_threadsafe(self.websocket.close(), self.loop) asyncio.run_coroutine_threadsafe(self.websocket.close(), self.loop)
@@ -1211,6 +1219,16 @@ class BackendClient:
if self.customers_callback: # 假设客服列表更新也触发客服列表回调 if self.customers_callback: # 假设客服列表更新也触发客服列表回调
self.customers_callback(staff_list, store_id) self.customers_callback(staff_list, store_id)
def _handle_version_response(self, message: Dict[str, Any]):
"""处理版本检查响应"""
latest_version = message.get('latest_version')
download_url = message.get('download_url')
print(f"收到版本检查响应: 最新版本={latest_version}, 下载地址={download_url}")
if self.version_callback:
self.version_callback(message)
# ==================== 辅助方法 ==================== # ==================== 辅助方法 ====================
def set_token(self, token: str): def set_token(self, token: str):

View File

@@ -27,9 +27,15 @@ class WebSocketManager:
"""WebSocket连接管理器统一处理后端连接和平台监听""" """WebSocket连接管理器统一处理后端连接和平台监听"""
def __init__(self): def __init__(self):
# 后端客户端
self.backend_client = None self.backend_client = None
self.is_connected = False
# 版本检查器
self.version_checker = None
self.gui_update_callback = None
self.platform_listeners = {} # 存储各平台的监听器 self.platform_listeners = {} # 存储各平台的监听器
self.connected_platforms = [] # 存储已连接的平台列表 # <- 新增 self.connected_platforms = [] # 存储已连接的平台列表 # <- 新增
self.callbacks = { self.callbacks = {
'log': None, 'log': None,
'success': None, 'success': None,
@@ -75,7 +81,6 @@ class WebSocketManager:
"""连接后端WebSocket""" """连接后端WebSocket"""
try: try:
# 1 保存token到配置 # 1 保存token到配置
try: try:
from config import set_saved_token from config import set_saved_token
@@ -98,13 +103,15 @@ class WebSocketManager:
else: else:
# 3 如果有客户端更新token并重连 # 3 如果有客户端更新token并重连
backend.set_token(token) backend.set_token(token)
# 设置回调函数 # 设置回调函数
def _on_backend_success(): def _on_backend_success():
try: try:
self._log("连接服务成功", "SUCCESS") self._log("连接服务成功", "SUCCESS")
if self.callbacks['success']: if self.callbacks['success']:
self.callbacks['success']() self.callbacks['success']()
# 启动版本检查器
self._start_version_checker()
except Exception as e: except Exception as e:
self._log(f"成功回调执行失败: {e}", "ERROR") self._log(f"成功回调执行失败: {e}", "ERROR")
@@ -119,11 +126,12 @@ class WebSocketManager:
if self.callbacks['token_error']: if self.callbacks['token_error']:
self.callbacks['token_error'](error_content) self.callbacks['token_error'](error_content)
backend.set_callbacks(success=_on_backend_success, login=_on_backend_login, token_error=_on_token_error) backend.set_callbacks(success=_on_backend_success, login=_on_backend_login,
token_error=_on_token_error)
if not backend.is_connected: if not backend.is_connected:
backend.connect() backend.connect()
self.backend_client = backend self.backend_client = backend
self._log("令牌已提交已连接后端。等待后端下发平台cookies后自动连接平台...", "SUCCESS") self._log("令牌已提交已连接后端。等待后端下发平台cookies后自动连接平台...", "SUCCESS")
return True return True
@@ -144,6 +152,8 @@ class WebSocketManager:
self._log("连接服务成功", "SUCCESS") self._log("连接服务成功", "SUCCESS")
if self.callbacks['success']: if self.callbacks['success']:
self.callbacks['success']() self.callbacks['success']()
# 启动版本检查器
self._start_version_checker()
except Exception as e: except Exception as e:
self._log(f"成功回调执行失败: {e}", "ERROR") self._log(f"成功回调执行失败: {e}", "ERROR")
@@ -176,7 +186,7 @@ class WebSocketManager:
self._log(f"🔄 检测到店铺 {store_id} 重连,清理 {len(keys_to_remove)} 个旧连接", "INFO") self._log(f"🔄 检测到店铺 {store_id} 重连,清理 {len(keys_to_remove)} 个旧连接", "INFO")
for key in keys_to_remove: for key in keys_to_remove:
self.platform_listeners.pop(key, None) self.platform_listeners.pop(key, None)
# 平台名称映射 # 平台名称映射
platform_map = { platform_map = {
"淘宝": "千牛", "淘宝": "千牛",
@@ -220,6 +230,27 @@ class WebSocketManager:
import traceback import traceback
self._log(f"错误详情: {traceback.format_exc()}", "DEBUG") self._log(f"错误详情: {traceback.format_exc()}", "DEBUG")
def _start_version_checker(self):
"""启动版本检查器"""
try:
from version_checker import VersionChecker
self.version_checker = VersionChecker(
backend_client=self.backend_client,
update_callback=self._on_update_available
)
self.backend_client.version_callback = self.version_checker.handle_version_response
self.version_checker.start()
self._log("✅ 版本检查器已启动将每10分钟检查一次更新", "SUCCESS")
except Exception as e:
self._log(f"❌ 启动版本检查器失败: {e}", "ERROR")
def _on_update_available(self, latest_version, download_url):
"""发现新版本时的处理"""
self._log(f"🔔 发现新版本 {latest_version}", "INFO")
# 通知主GUI显示更新提醒
if hasattr(self, 'gui_update_callback') and self.gui_update_callback:
self.gui_update_callback(latest_version, download_url)
def _start_jd_listener(self, store_id: str, cookies: str): def _start_jd_listener(self, store_id: str, cookies: str):
"""启动京东平台监听""" """启动京东平台监听"""
try: try:
@@ -275,15 +306,59 @@ class WebSocketManager:
def _runner(): def _runner():
try: try:
import json import json
self._log("🚀 开始创建抖音监听器实例", "DEBUG")
listener = DYListenerForGUI_WS() listener = DYListenerForGUI_WS()
# 将JSON字符串格式的cookies解析为字典 self._log("✅ 抖音监听器实例创建成功", "DEBUG")
try:
cookie_dict = json.loads(cookies) if isinstance(cookies, str) else cookies # 🔥 检查是否为登录参数模式(与拼多多保持一致)
except json.JSONDecodeError as e: if cookies and ('"login_flow"' in cookies or '"phone_number"' in cookies):
self._log(f"❌ Cookie JSON解析失败: {e}", "ERROR") # 使用登录参数模式
return False self._log("📋 使用登录参数启动抖音监听器", "INFO")
self._log("🔄 开始执行 start_with_login_params", "DEBUG")
result = asyncio.run(listener.start_with_login_params(store_id=store_id, login_params=cookies))
self._log(f"📊 start_with_login_params 执行结果: {result}", "DEBUG")
# 🔥 详细的结果分析(与拼多多完全一致)
if result == "need_verification_code":
self._log("✅ [DY] 登录流程正常,已发送验证码需求通知给后端", "SUCCESS")
elif result == "verification_code_error":
self._log("⚠️ [DY] 验证码错误,已发送错误通知给后端", "WARNING")
elif result:
self._log("✅ [DY] 登录成功,平台连接已建立", "SUCCESS")
self._notify_platform_connected("抖音")
else:
self._log("❌ [DY] 登录失败", "ERROR")
else:
# 传统cookie模式保留兼容性
self._log("🍪 使用Cookie启动抖音监听器", "INFO")
self._log("🔄 开始执行 start_with_cookies", "DEBUG")
try:
# 🔥 修复尝试JSON解析失败时用ast.literal_eval解析Python字典字符串
if isinstance(cookies, str):
try:
cookie_dict = json.loads(cookies)
except json.JSONDecodeError:
# 后端发送的是Python字典字符串格式使用ast.literal_eval
import ast
cookie_dict = ast.literal_eval(cookies)
self._log("✅ 使用ast.literal_eval成功解析cookies", "DEBUG")
else:
cookie_dict = cookies
except (json.JSONDecodeError, ValueError, SyntaxError) as e:
self._log(f"❌ Cookie解析失败: {e}", "ERROR")
return False
result = asyncio.run(listener.start_with_cookies(store_id=store_id, cookie_dict=cookie_dict))
self._log(f"📊 start_with_cookies 执行结果: {result}", "DEBUG")
# Cookie启动成功时也要通知GUI
if result:
self._log("✅ [DY] Cookie启动成功平台连接已建立", "SUCCESS")
self._notify_platform_connected("抖音")
# 🔥 移除不再在backend_singleton中发送connect_message
# 抖音的连接状态报告应该在DyUtils中的DyLogin类中发送与拼多多保持一致
# 所有特殊状态通知都已经在DyLogin中发送过了这里不需要重复发送
result = asyncio.run(listener.start_with_cookies(store_id=store_id, cookie_dict=cookie_dict))
return result return result
except Exception as e: except Exception as e:
self._log(f"抖音监听器运行异常: {e}", "ERROR") self._log(f"抖音监听器运行异常: {e}", "ERROR")
@@ -304,34 +379,13 @@ class WebSocketManager:
if f"抖音:{store_id}" in self.platform_listeners: if f"抖音:{store_id}" in self.platform_listeners:
self.platform_listeners[f"抖音:{store_id}"]['status'] = 'success' self.platform_listeners[f"抖音:{store_id}"]['status'] = 'success'
# 上报连接状态给后端
if self.backend_client:
try:
self.backend_client.send_message({
"type": "connect_message",
"store_id": store_id,
"status": True
})
self._log("已上报抖音平台连接状态: 成功", "INFO")
except Exception as e:
self._log(f"上报抖音平台连接状态失败: {e}", "WARNING")
self._log("已启动抖音平台监听", "SUCCESS") self._log("已启动抖音平台监听", "SUCCESS")
self._notify_platform_connected("抖音") # ← 新增
except Exception as e: except Exception as e:
self._log(f"启动抖音平台监听失败: {e}", "ERROR") self._log(f"启动抖音平台监听失败: {e}", "ERROR")
# 确保失败时也上报状态 # 🔥 移除:确保失败时也不在这里上报状态
if self.backend_client: # 失败状态应该在DyLogin中处理与拼多多保持一致
try:
self.backend_client.send_message({
"type": "connect_message",
"store_id": store_id,
"status": False
})
except Exception as send_e:
self._log(f"失败状态下报抖音平台连接状态也失败: {send_e}", "ERROR")
def _start_qianniu_listener(self, store_id: str, cookies: str): def _start_qianniu_listener(self, store_id: str, cookies: str):
"""启动千牛平台监听(单连接多店铺架构)""" """启动千牛平台监听(单连接多店铺架构)"""
@@ -401,7 +455,7 @@ class WebSocketManager:
self._log("🚀 开始创建拼多多监听器实例", "DEBUG") self._log("🚀 开始创建拼多多监听器实例", "DEBUG")
listener = PDDListenerForGUI_WS(log_callback=self._log) listener = PDDListenerForGUI_WS(log_callback=self._log)
self._log("✅ 拼多多监听器实例创建成功", "DEBUG") self._log("✅ 拼多多监听器实例创建成功", "DEBUG")
# 判断是登录参数还是Cookie # 判断是登录参数还是Cookie
if self._is_pdd_login_params(data): if self._is_pdd_login_params(data):
# 使用登录参数启动 # 使用登录参数启动
@@ -409,7 +463,7 @@ class WebSocketManager:
self._log("🔄 开始执行 start_with_login_params", "DEBUG") self._log("🔄 开始执行 start_with_login_params", "DEBUG")
result = asyncio.run(listener.start_with_login_params(store_id=store_id, login_params=data)) result = asyncio.run(listener.start_with_login_params(store_id=store_id, login_params=data))
self._log(f"📊 start_with_login_params 执行结果: {result}", "DEBUG") self._log(f"📊 start_with_login_params 执行结果: {result}", "DEBUG")
# 详细的结果分析 # 详细的结果分析
if result == "need_verification_code": if result == "need_verification_code":
self._log("✅ [PDD] 登录流程正常,已发送验证码需求通知给后端", "SUCCESS") self._log("✅ [PDD] 登录流程正常,已发送验证码需求通知给后端", "SUCCESS")
@@ -426,14 +480,15 @@ class WebSocketManager:
self._log("🔄 开始执行 start_with_cookies", "DEBUG") self._log("🔄 开始执行 start_with_cookies", "DEBUG")
result = asyncio.run(listener.start_with_cookies(store_id=store_id, cookies=data)) result = asyncio.run(listener.start_with_cookies(store_id=store_id, cookies=data))
self._log(f"📊 start_with_cookies 执行结果: {result}", "DEBUG") self._log(f"📊 start_with_cookies 执行结果: {result}", "DEBUG")
# Cookie启动成功时也要通知GUI # Cookie启动成功时也要通知GUI
if result: if result:
self._log("✅ [PDD] Cookie启动成功平台连接已建立", "SUCCESS") self._log("✅ [PDD] Cookie启动成功平台连接已建立", "SUCCESS")
self._notify_platform_connected("拼多多") self._notify_platform_connected("拼多多")
# 根据实际登录结果上报状态给后端 # 根据实际登录结果上报状态给后端
if self.backend_client and result not in ["need_verification_code", "verification_code_error", "login_failure"]: if self.backend_client and result not in ["need_verification_code", "verification_code_error",
"login_failure"]:
# 如果是特殊状态说明通知已经在PddLogin中发送了不需要重复发送 # 如果是特殊状态说明通知已经在PddLogin中发送了不需要重复发送
try: try:
message = { message = {
@@ -443,7 +498,8 @@ class WebSocketManager:
} }
self.backend_client.send_message(message) self.backend_client.send_message(message)
status_text = "成功" if result else "失败" status_text = "成功" if result else "失败"
self._log(f"上报拼多多平台连接状态{status_text}: {message}", "SUCCESS" if result else "ERROR") self._log(f"上报拼多多平台连接状态{status_text}: {message}",
"SUCCESS" if result else "ERROR")
except Exception as send_e: except Exception as send_e:
self._log(f"上报拼多多平台连接状态失败: {send_e}", "ERROR") self._log(f"上报拼多多平台连接状态失败: {send_e}", "ERROR")
elif result == "need_verification_code": elif result == "need_verification_code":
@@ -452,9 +508,9 @@ class WebSocketManager:
self._log("验证码错误错误通知已由PddLogin发送等待后端处理", "INFO") self._log("验证码错误错误通知已由PddLogin发送等待后端处理", "INFO")
elif result == "login_failure": elif result == "login_failure":
self._log("登录失败失败通知已由PddLogin发送等待后端处理", "INFO") self._log("登录失败失败通知已由PddLogin发送等待后端处理", "INFO")
return result return result
except Exception as e: except Exception as e:
self._log(f"拼多多监听器运行异常: {e}", "ERROR") self._log(f"拼多多监听器运行异常: {e}", "ERROR")
import traceback import traceback
@@ -509,24 +565,24 @@ class WebSocketManager:
try: try:
self._log(f"🔍 [DEBUG] 检查是否为登录参数,数据长度: {len(data)}", "DEBUG") self._log(f"🔍 [DEBUG] 检查是否为登录参数,数据长度: {len(data)}", "DEBUG")
self._log(f"🔍 [DEBUG] 数据前100字符: {data[:100]}", "DEBUG") self._log(f"🔍 [DEBUG] 数据前100字符: {data[:100]}", "DEBUG")
import json import json
parsed_data = json.loads(data) parsed_data = json.loads(data)
self._log(f"🔍 [DEBUG] JSON解析成功键: {list(parsed_data.keys())}", "DEBUG") self._log(f"🔍 [DEBUG] JSON解析成功键: {list(parsed_data.keys())}", "DEBUG")
login_params = parsed_data.get("data", {}).get("login_params", {}) login_params = parsed_data.get("data", {}).get("login_params", {})
self._log(f"🔍 [DEBUG] login_params存在: {bool(login_params)}", "DEBUG") self._log(f"🔍 [DEBUG] login_params存在: {bool(login_params)}", "DEBUG")
if not login_params: if not login_params:
self._log("🔍 [DEBUG] login_params为空返回False", "DEBUG") self._log("🔍 [DEBUG] login_params为空返回False", "DEBUG")
return False return False
# 检查必需的登录参数字段 # 检查必需的登录参数字段
required_fields = ["username", "password", "anti_content", "risk_sign", "timestamp"] required_fields = ["username", "password", "anti_content", "risk_sign", "timestamp"]
has_all_fields = all(field in login_params for field in required_fields) has_all_fields = all(field in login_params for field in required_fields)
self._log(f"🔍 [DEBUG] 包含所有必需字段: {has_all_fields}", "DEBUG") self._log(f"🔍 [DEBUG] 包含所有必需字段: {has_all_fields}", "DEBUG")
self._log(f"🔍 [DEBUG] 现有字段: {list(login_params.keys())}", "DEBUG") self._log(f"🔍 [DEBUG] 现有字段: {list(login_params.keys())}", "DEBUG")
return has_all_fields return has_all_fields
except Exception as e: except Exception as e:
self._log(f"🔍 [DEBUG] 解析失败: {e}", "DEBUG") self._log(f"🔍 [DEBUG] 解析失败: {e}", "DEBUG")

View File

@@ -9,13 +9,13 @@ import json # 用于将令牌保存为 JSON 格式
# 后端服务器配置 # 后端服务器配置
# BACKEND_HOST = "192.168.5.233" # BACKEND_HOST = "192.168.5.233"
BACKEND_HOST = "192.168.5.12" # BACKEND_HOST = "192.168.5.12"
# BACKEND_HOST = "shuidrop.com" BACKEND_HOST = "shuidrop.com"
# BACKEND_HOST = "test.shuidrop.com" # BACKEND_HOST = "test.shuidrop.com"
BACKEND_PORT = "8000" # BACKEND_PORT = "8000"
# BACKEND_PORT = "" BACKEND_PORT = ""
BACKEND_WS_URL = f"ws://{BACKEND_HOST}:{BACKEND_PORT}" # BACKEND_WS_URL = f"ws://{BACKEND_HOST}:{BACKEND_PORT}"
# BACKEND_WS_URL = f"wss://{BACKEND_HOST}" BACKEND_WS_URL = f"wss://{BACKEND_HOST}"
# WebSocket配置 # WebSocket配置
WS_CONNECT_TIMEOUT = 16.0 WS_CONNECT_TIMEOUT = 16.0

51
main.py
View File

@@ -43,6 +43,9 @@ class LoginWindow(QMainWindow):
self.initUI() self.initUI()
# 延迟设置版本检查器确保WebSocket连接已建立
QTimer.singleShot(5000, self.setup_version_checker)
def initUI(self): def initUI(self):
# 设置窗口基本属性 # 设置窗口基本属性
self.setWindowTitle('AI客服智能助手') self.setWindowTitle('AI客服智能助手')
@@ -406,22 +409,22 @@ class LoginWindow(QMainWindow):
"""处理token错误 - 显示红色错误信息并停止所有操作""" """处理token错误 - 显示红色错误信息并停止所有操作"""
try: try:
self.add_log(f"Token验证失败: {error_content}", "ERROR") self.add_log(f"Token验证失败: {error_content}", "ERROR")
# 在状态标签显示红色错误信息 # 在状态标签显示红色错误信息
self.status_label.setText(f"🔴 {error_content}") self.status_label.setText(f"🔴 {error_content}")
self.status_label.setStyleSheet( self.status_label.setStyleSheet(
"color: #dc3545; background: rgba(220, 53, 69, 0.1); border-radius: 12px; padding: 5px 10px; font-weight: bold;") "color: #dc3545; background: rgba(220, 53, 69, 0.1); border-radius: 12px; padding: 5px 10px; font-weight: bold;")
# 重置按钮状态 # 重置按钮状态
self.login_btn.setEnabled(True) self.login_btn.setEnabled(True)
self.login_btn.setText("重新连接") self.login_btn.setText("重新连接")
self.login_btn.setObjectName("loginButton") # 恢复原始样式 self.login_btn.setObjectName("loginButton") # 恢复原始样式
# 清空已连接平台列表 # 清空已连接平台列表
self.connected_platforms.clear() self.connected_platforms.clear()
self.add_log("由于token无效已停止所有连接操作", "ERROR") self.add_log("由于token无效已停止所有连接操作", "ERROR")
except Exception as e: except Exception as e:
self.add_log(f"处理token错误失败: {e}", "ERROR") self.add_log(f"处理token错误失败: {e}", "ERROR")
@@ -699,6 +702,44 @@ class LoginWindow(QMainWindow):
self.quit_application() self.quit_application()
event.accept() event.accept()
def setup_version_checker(self):
"""设置版本检查器的GUI回调"""
try:
from WebSocket.backend_singleton import get_websocket_manager
ws_manager = get_websocket_manager()
if ws_manager:
ws_manager.gui_update_callback = self.show_update_notification
self.add_log("✅ 版本检查器GUI回调已设置", "SUCCESS")
else:
self.add_log("⚠️ WebSocket管理器未初始化", "WARNING")
except Exception as e:
self.add_log(f"❌ 设置版本检查器失败: {e}", "ERROR")
def show_update_notification(self, latest_version, download_url):
"""显示版本更新通知"""
try:
reply = QMessageBox.question(
self,
"版本更新",
f"发现新版本 {latest_version},是否立即更新?\n\n点击确定将打开下载页面。",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.Yes
)
if reply == QMessageBox.Yes:
self.trigger_update(download_url)
except Exception as e:
self.add_log(f"❌ 显示更新通知失败: {e}", "ERROR")
def trigger_update(self, download_url):
"""触发更新下载"""
import webbrowser
try:
webbrowser.open(download_url)
self.add_log("✅ 已打开更新下载页面", "SUCCESS")
except Exception as e:
self.add_log(f"❌ 打开下载页面失败: {e}", "ERROR")
def main(): def main():
"""主函数,用于测试界面""" """主函数,用于测试界面"""

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 KiB