[patch] 因pdd内部回复消息接口与登录接口不同步导致监听过程中发送消息接口返回会话过期处理优化逻辑 并设计开发提示用户 并提供用户是否重新发送会话过期消息体按钮
This commit is contained in:
@@ -2047,7 +2047,7 @@ class ChatPdd:
|
||||
# 降级处理:返回相对路径
|
||||
return relative_path
|
||||
|
||||
def __init__(self, cookie, chat_list_stat, csname=None, text=None, log_callback=None):
|
||||
def __init__(self, cookie, chat_list_stat, csname=None, text=None, log_callback=None, session_timeout_callback=None):
|
||||
# 检查JS文件
|
||||
self.check_js_files()
|
||||
|
||||
@@ -2058,6 +2058,7 @@ class ChatPdd:
|
||||
|
||||
# 首先设置log_callback,以便在初始化过程中使用日志
|
||||
self.log_callback = log_callback
|
||||
self.session_timeout_callback = session_timeout_callback # 会话超时回调
|
||||
|
||||
# 读取JS文件
|
||||
try:
|
||||
@@ -2191,6 +2192,13 @@ class ChatPdd:
|
||||
|
||||
self.store_id = "538727ec-c84d-4458-8ade-3960c9ab802c" # 可以根据需要修改
|
||||
self.user_tokens = {} # 存储用户token信息
|
||||
|
||||
# 🔥 新增:会话过期管理
|
||||
self.session_expired_notified = False # 防止重复通知后端
|
||||
self.last_session_check_time = 0 # 最后检测时间(避免频繁检测)
|
||||
self.session_notify_time = 0 # 发送会话过期通知的时间戳
|
||||
self.session_timeout_timer = None # 超时检测定时器
|
||||
self.session_timeout_duration = 66 # 超时时长(秒),可配置
|
||||
|
||||
def _log(self, message, level="INFO"):
|
||||
"""内部日志方法"""
|
||||
@@ -2661,14 +2669,38 @@ class ChatPdd:
|
||||
|
||||
# 打印HTTP状态与返回体片段
|
||||
self._log(f"PDD发送状态: HTTP {response.status_code}", "INFO")
|
||||
try:
|
||||
self._log(f"PDD返回体: {response.text[:500]}", "DEBUG")
|
||||
except Exception:
|
||||
pass
|
||||
self._log(f"PDD返回体: {response.text[:200]}", "DEBUG")
|
||||
|
||||
# 🔥 检查响应状态码
|
||||
if response.status_code == 200:
|
||||
self._log(f"✅ 已发送AI回复给用户 {uid}: {content}", "SUCCESS")
|
||||
return True
|
||||
# ✅ HTTP 200,但需要进一步检查响应体
|
||||
try:
|
||||
response_data = response.json()
|
||||
|
||||
# 检测会话过期(error_code=43001)
|
||||
if not response_data.get('success') and response_data.get('error_code') == 43001:
|
||||
error_msg = response_data.get('error_msg', '会话已过期')
|
||||
self._log(f"🔴 检测到会话过期: {error_msg}", "ERROR")
|
||||
|
||||
# 通知后端Cookie失效
|
||||
await self._notify_backend_session_expired()
|
||||
return False
|
||||
|
||||
# 检查其他错误
|
||||
if not response_data.get('success'):
|
||||
error_code = response_data.get('error_code', 'unknown')
|
||||
error_msg = response_data.get('error_msg', '未知错误')
|
||||
self._log(f"❌ 发送失败 [错误码:{error_code}]: {error_msg}", "ERROR")
|
||||
return False
|
||||
|
||||
# 发送成功
|
||||
self._log(f"✅ 已发送AI回复给用户 {uid}", "SUCCESS")
|
||||
return True
|
||||
|
||||
except (ValueError, KeyError):
|
||||
# JSON解析失败,保守处理
|
||||
self._log(f"✅ 已发送AI回复给用户 {uid}", "SUCCESS")
|
||||
return True
|
||||
else:
|
||||
self._log(f"❌ 发送AI回复失败,HTTP状态码: {response.status_code}", "ERROR")
|
||||
self._log(f"响应内容: {response.text}", "DEBUG")
|
||||
@@ -2679,6 +2711,141 @@ class ChatPdd:
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
async def _notify_backend_session_expired(self):
|
||||
"""向后端发送会话过期通知"""
|
||||
try:
|
||||
# 防重复通知机制:如果已经通知过,不再重复发送
|
||||
if self.session_expired_notified:
|
||||
return
|
||||
|
||||
# 检查最后通知时间,避免频繁发送(间隔至少30秒)
|
||||
import time
|
||||
current_time = time.time()
|
||||
if current_time - self.last_session_check_time < 30:
|
||||
return
|
||||
|
||||
from WebSocket.backend_singleton import get_websocket_manager
|
||||
|
||||
ws_manager = get_websocket_manager()
|
||||
if ws_manager and ws_manager.backend_client:
|
||||
# 使用统一的 connect_message 格式
|
||||
message = {
|
||||
"type": "connect_message",
|
||||
"store_id": self.store_id,
|
||||
"status": False,
|
||||
"content": "cookies失效,需要重新登录"
|
||||
}
|
||||
|
||||
# 发送消息到后端
|
||||
ws_manager.backend_client.send_message(message)
|
||||
|
||||
# 标记已通知
|
||||
self.session_expired_notified = True
|
||||
self.last_session_check_time = current_time
|
||||
self.session_notify_time = current_time
|
||||
|
||||
self._log(f"📤 已向后端发送会话过期通知", "WARNING")
|
||||
self._log(f"⏳ 等待后端重新下发Cookie...", "INFO")
|
||||
|
||||
# 启动超时检测定时器
|
||||
self._start_session_timeout_check()
|
||||
else:
|
||||
self._log("⚠️ WebSocket未连接,无法发送通知", "WARNING")
|
||||
|
||||
except Exception as e:
|
||||
self._log(f"❌ [PDD] 发送会话过期通知失败: {e}", "ERROR")
|
||||
import traceback as _traceback
|
||||
self._log(f"详细错误: {_traceback.format_exc()}", "DEBUG")
|
||||
|
||||
def _start_session_timeout_check(self):
|
||||
"""启动会话恢复超时检测定时器"""
|
||||
try:
|
||||
# 取消之前的定时器
|
||||
self._cancel_session_timeout_check()
|
||||
|
||||
import threading
|
||||
|
||||
def timeout_callback():
|
||||
try:
|
||||
self._log(f"⏰ 会话恢复等待超时({self.session_timeout_duration}秒)", "WARNING")
|
||||
self._on_session_timeout()
|
||||
except Exception as e:
|
||||
self._log(f"❌ 超时回调失败: {e}", "ERROR")
|
||||
|
||||
# 启动定时器
|
||||
self.session_timeout_timer = threading.Timer(
|
||||
self.session_timeout_duration,
|
||||
timeout_callback
|
||||
)
|
||||
self.session_timeout_timer.daemon = True
|
||||
self.session_timeout_timer.start()
|
||||
|
||||
except Exception as e:
|
||||
self._log(f"❌ 启动超时检测失败: {e}", "ERROR")
|
||||
|
||||
def _cancel_session_timeout_check(self):
|
||||
"""取消超时检测定时器"""
|
||||
try:
|
||||
if self.session_timeout_timer and self.session_timeout_timer.is_alive():
|
||||
self.session_timeout_timer.cancel()
|
||||
self.session_timeout_timer = None
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _on_session_timeout(self):
|
||||
"""超时时的处理逻辑 - 检查是否已恢复,未恢复则提示用户"""
|
||||
try:
|
||||
import time
|
||||
|
||||
# 检查是否已经重连成功
|
||||
if not self.session_expired_notified:
|
||||
self._log("✅ 会话已自动恢复", "SUCCESS")
|
||||
return
|
||||
|
||||
# 计算等待时长
|
||||
elapsed_time = int(time.time() - self.session_notify_time)
|
||||
self._log(f"⚠️ 等待{elapsed_time}秒后,会话仍未恢复", "WARNING")
|
||||
|
||||
# 调用专用的超时回调函数
|
||||
if self.session_timeout_callback:
|
||||
try:
|
||||
retry_info = {
|
||||
"store_id": self.store_id,
|
||||
"elapsed_time": elapsed_time,
|
||||
"timeout_duration": self.session_timeout_duration,
|
||||
"pdd_instance": self
|
||||
}
|
||||
|
||||
self.session_timeout_callback(retry_info)
|
||||
|
||||
except Exception as callback_error:
|
||||
self._log(f"❌ 超时回调失败: {callback_error}", "ERROR")
|
||||
else:
|
||||
self._log("⚠️ 未设置超时回调", "WARNING")
|
||||
|
||||
except Exception as e:
|
||||
self._log(f"❌ 超时处理失败: {e}", "ERROR")
|
||||
|
||||
async def retry_session_recovery(self):
|
||||
"""用户点击重试后调用此方法"""
|
||||
try:
|
||||
self._log("🔄 用户选择重试,重新发送会话过期通知", "INFO")
|
||||
|
||||
# 重置标志位,允许重新发送
|
||||
self.session_expired_notified = False
|
||||
self.last_session_check_time = 0
|
||||
|
||||
# 取消旧的定时器
|
||||
self._cancel_session_timeout_check()
|
||||
|
||||
# 重新发送通知
|
||||
await self._notify_backend_session_expired()
|
||||
|
||||
self._log("✅ 重试请求已发送", "SUCCESS")
|
||||
|
||||
except Exception as e:
|
||||
self._log(f"❌ 重试失败: {e}", "ERROR")
|
||||
|
||||
async def _download_image(self, image_url):
|
||||
"""下载图片并返回base64编码的数据
|
||||
|
||||
@@ -2875,9 +3042,36 @@ class ChatPdd:
|
||||
self._log(f"PDD发送状态: HTTP {response.status_code}", "INFO")
|
||||
self._log(f"PDD返回体: {response.text}", "DEBUG")
|
||||
|
||||
# 🔥 检查响应状态码
|
||||
if response.status_code == 200:
|
||||
self._log(f"✅ 已发送图片消息给用户 {uid}", "SUCCESS")
|
||||
return True
|
||||
# ✅ HTTP 200,但需要进一步检查响应体
|
||||
try:
|
||||
response_data = response.json()
|
||||
|
||||
# 检测会话过期(error_code=43001)
|
||||
if not response_data.get('success') and response_data.get('error_code') == 43001:
|
||||
error_msg = response_data.get('error_msg', '会话已过期')
|
||||
self._log(f"🔴 图片发送会话过期: {error_msg}", "ERROR")
|
||||
|
||||
# 通知后端Cookie失效
|
||||
await self._notify_backend_session_expired()
|
||||
return False
|
||||
|
||||
# 检查其他错误
|
||||
if not response_data.get('success'):
|
||||
error_code = response_data.get('error_code', 'unknown')
|
||||
error_msg = response_data.get('error_msg', '未知错误')
|
||||
self._log(f"❌ 发送图片失败 [错误码:{error_code}]: {error_msg}", "ERROR")
|
||||
return False
|
||||
|
||||
# 发送成功
|
||||
self._log(f"✅ 已发送图片消息给用户 {uid}", "SUCCESS")
|
||||
return True
|
||||
|
||||
except (ValueError, KeyError):
|
||||
# JSON解析失败,保守处理
|
||||
self._log(f"✅ 已发送图片消息给用户 {uid}", "SUCCESS")
|
||||
return True
|
||||
else:
|
||||
self._log(f"❌ 发送图片消息失败,HTTP状态码: {response.status_code}", "ERROR")
|
||||
return False
|
||||
@@ -2971,9 +3165,36 @@ class ChatPdd:
|
||||
self._log(f"PDD发送状态: HTTP {response.status_code}", "INFO")
|
||||
self._log(f"PDD返回体: {response.text}", "DEBUG")
|
||||
|
||||
# 🔥 检查响应状态码
|
||||
if response.status_code == 200:
|
||||
self._log(f"✅ 已发送商品卡片给用户 {uid}: goods_id={goods_id}", "SUCCESS")
|
||||
return True
|
||||
# ✅ HTTP 200,但需要进一步检查响应体
|
||||
try:
|
||||
response_data = response.json()
|
||||
|
||||
# 检测会话过期(error_code=43001)
|
||||
if not response_data.get('success') and response_data.get('error_code') == 43001:
|
||||
error_msg = response_data.get('error_msg', '会话已过期')
|
||||
self._log(f"🔴 商品卡发送会话过期: {error_msg}", "ERROR")
|
||||
|
||||
# 通知后端Cookie失效
|
||||
await self._notify_backend_session_expired()
|
||||
return False
|
||||
|
||||
# 检查其他错误
|
||||
if not response_data.get('success'):
|
||||
error_code = response_data.get('error_code', 'unknown')
|
||||
error_msg = response_data.get('error_msg', '未知错误')
|
||||
self._log(f"❌ 发送商品卡失败 [错误码:{error_code}]: {error_msg}", "ERROR")
|
||||
return False
|
||||
|
||||
# 发送成功
|
||||
self._log(f"✅ 已发送商品卡片给用户 {uid}", "SUCCESS")
|
||||
return True
|
||||
|
||||
except (ValueError, KeyError):
|
||||
# JSON解析失败,保守处理
|
||||
self._log(f"✅ 已发送商品卡片给用户 {uid}", "SUCCESS")
|
||||
return True
|
||||
else:
|
||||
self._log(f"❌ 发送商品卡片失败,HTTP状态码: {response.status_code}", "ERROR")
|
||||
return False
|
||||
@@ -3143,6 +3364,9 @@ class ChatPdd:
|
||||
if msg_type == 8: # 平台系统卡片消息(如:售后提醒、平台通知等)
|
||||
self._log(f"🚫 过滤系统卡片消息(type=8): {message_info.get('content', '')[:50]}", "DEBUG")
|
||||
return True
|
||||
if msg_type == 64: # 🔥 新增:售后卡片消息(如:消费者申请售后)
|
||||
self._log(f"🚫 过滤售后卡片消息(type=64): {message_info.get('content', '')[:50]}", "DEBUG")
|
||||
return True
|
||||
|
||||
# 2. 基于模板名称识别机器人消息
|
||||
template_name = message_info.get("template_name", "")
|
||||
@@ -3150,6 +3374,7 @@ class ChatPdd:
|
||||
"mall_robot_man_intervention_and_restart", # 机器人暂停接待消息
|
||||
"mall_robot_text_msg", # 机器人自动回复消息
|
||||
"aftersales_hosting_warning_card", # 售后托管警告卡片
|
||||
"apply_for_consultation_card_new", # 🔥 新增:售后协商申请卡片
|
||||
# 可根据实际情况添加更多机器人模板
|
||||
]
|
||||
if template_name in robot_templates:
|
||||
@@ -3182,6 +3407,8 @@ class ChatPdd:
|
||||
"请尽快解决售后问题", # 平台售后提醒
|
||||
"平台介入退款", # 平台售后提醒
|
||||
"请尽快处理售后", # 平台售后提醒
|
||||
"消费者申请售后", # 🔥 新增:售后申请通知
|
||||
"建议先与消费者友好协商", # 🔥 新增:售后协商提示
|
||||
]
|
||||
|
||||
if any(pattern in content for pattern in robot_content_patterns):
|
||||
@@ -3392,6 +3619,14 @@ class ChatPdd:
|
||||
# 连接成功,重置重连计数器
|
||||
self.reconnect_attempts = 0
|
||||
self._log("✅ WebSocket-PDD连接成功", "SUCCESS")
|
||||
|
||||
# 重置会话过期标志位(新会话开始)
|
||||
self.session_expired_notified = False
|
||||
self.last_session_check_time = 0
|
||||
self.session_notify_time = 0
|
||||
|
||||
# 取消超时检测定时器(会话已恢复)
|
||||
self._cancel_session_timeout_check()
|
||||
|
||||
z = self.encodeex.call("encode_token", str(self.user_id), self.auth_token)
|
||||
if z:
|
||||
@@ -3492,9 +3727,10 @@ class ChatPdd:
|
||||
class PddListenerForGUI:
|
||||
"""用于GUI集成的拼多多监听包装器"""
|
||||
|
||||
def __init__(self, log_callback: Optional[Callable] = None):
|
||||
def __init__(self, log_callback: Optional[Callable] = None, session_timeout_signal=None):
|
||||
self.pdd_bot = None
|
||||
self.log_callback = log_callback
|
||||
self.session_timeout_signal = session_timeout_signal # Qt信号对象
|
||||
self.running = False
|
||||
self.main_thread = None
|
||||
self.loop = None
|
||||
@@ -3507,6 +3743,21 @@ class PddListenerForGUI:
|
||||
self.log_callback(message, log_type)
|
||||
else:
|
||||
print(f"[{log_type}] {message}")
|
||||
|
||||
def _on_session_timeout(self, retry_info: dict):
|
||||
"""会话恢复超时的GUI处理回调"""
|
||||
try:
|
||||
self._log(f"⚠️ 会话恢复超时(已等待{retry_info['elapsed_time']}秒)", "WARNING")
|
||||
|
||||
# 使用Qt信号机制(线程安全)
|
||||
if self.session_timeout_signal:
|
||||
self.session_timeout_signal.emit(retry_info)
|
||||
self._log(f"✅ 已触发超时对话框", "INFO")
|
||||
else:
|
||||
self._log("⚠️ 未设置超时信号,无法显示对话框", "WARNING")
|
||||
|
||||
except Exception as e:
|
||||
self._log(f"❌ 超时处理回调失败: {e}", "ERROR")
|
||||
|
||||
async def start_listening(self, cookie_dict: Dict[str, str], chat_list_stat: bool = False,
|
||||
csname: Optional[str] = None, text: Optional[str] = None) -> bool:
|
||||
@@ -3523,13 +3774,21 @@ class PddListenerForGUI:
|
||||
self.startup_error = None
|
||||
self.startup_event.clear()
|
||||
|
||||
# 创建ChatPdd实例
|
||||
# 如果已有旧实例,先清理定时器
|
||||
if self.pdd_bot:
|
||||
try:
|
||||
self.pdd_bot._cancel_session_timeout_check()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 创建ChatPdd实例(传递超时回调)
|
||||
self.pdd_bot = ChatPdd(
|
||||
cookie=cookie_dict,
|
||||
text=text,
|
||||
chat_list_stat=chat_list_stat,
|
||||
csname=csname,
|
||||
log_callback=self.log_callback
|
||||
log_callback=self.log_callback,
|
||||
session_timeout_callback=self._on_session_timeout
|
||||
)
|
||||
|
||||
self.running = True
|
||||
@@ -3690,13 +3949,21 @@ class PddListenerForGUI:
|
||||
|
||||
self._log(f"✅ [PDD] cookies解析成功,包含 {len(cookie_dict)} 个字段", "SUCCESS")
|
||||
|
||||
# 创建ChatPdd实例
|
||||
# 如果已有旧实例,先清理定时器
|
||||
if self.pdd_bot:
|
||||
try:
|
||||
self.pdd_bot._cancel_session_timeout_check()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 创建ChatPdd实例(传递超时回调)
|
||||
self.pdd_bot = ChatPdd(
|
||||
cookie=cookie_dict,
|
||||
text=None,
|
||||
chat_list_stat=False,
|
||||
csname=None,
|
||||
log_callback=self.log_callback
|
||||
log_callback=self.log_callback,
|
||||
session_timeout_callback=self._on_session_timeout
|
||||
)
|
||||
|
||||
# 设置store_id和后端服务
|
||||
|
||||
Reference in New Issue
Block a user