修复停止按钮死循环和消息落库问题

This commit is contained in:
youpaishidifu
2026-08-30 14:03:53 +08:00
parent 96f07353ba
commit 52013d28c1
2 changed files with 37 additions and 13 deletions
+22 -2
View File
@@ -599,6 +599,9 @@ class MsgBotApp(ctk.CTk):
self.ar.set_enabled(self.cfg.get("auto_reply_enabled", False))
self.ar.set_min_score(self.cfg.get("auto_reply_min_score", 0.35))
# ⭐ 防止 _update_stop_ui() 中 deselect() 触发 _toggle_monitor() 回调的标志
self._suppress_toggle_callback = False
self._build_ui()
# 定时从队列取日志刷新
@@ -805,12 +808,21 @@ class MsgBotApp(ctk.CTk):
# ── 回调 ──────────────────────────────
def _toggle_monitor(self):
# ⭐ 防止 _update_stop_ui() 中 deselect() 触发的回调
if self._suppress_toggle_callback:
return
enabled = self._toggle_btn.get()
if enabled:
# 检查知识库是否已加载
if not self.kb.is_ready:
self._log("⚠ 知识库未加载,请先设置知识库路径")
self._toggle_btn.deselect()
# 设置标志防止 deselect() 触发回调
self._suppress_toggle_callback = True
try:
self._toggle_btn._variable.set(False)
finally:
self.after(100, lambda: setattr(self, '_suppress_toggle_callback', False))
return
# 用户手动点击启用 → 彻底重启(重新检测内存,尝试恢复GPU)
self.monitor.start(full_restart=True)
@@ -1442,7 +1454,15 @@ class MsgBotApp(ctk.CTk):
def _update_stop_ui(self):
"""主线程更新急停后的 UI(stop 放到后台线程,主线程不冻结)。"""
self._toggle_btn.deselect()
# ⭐ 设置标志防止 deselect() 触发 _toggle_monitor() 回调
self._suppress_toggle_callback = True
try:
# 直接设置开关变量值,不触发回调
self._toggle_btn._variable.set(False)
finally:
# 延迟重置标志,确保回调已被跳过
self.after(100, lambda: setattr(self, '_suppress_toggle_callback', False))
self._status_label.configure(text="⏸ 急停")
self._log_queue.put("🔄 可点击「启用监控」重新启动")
# ⭐ UI 立即更新;stop() 在后台线程执行,主线程保持响应,不再触发冻结弹窗。
+15 -11
View File
@@ -627,6 +627,7 @@ class MessageMonitor:
def __init__(self, on_message: Optional[Callable[[str, str], None]] = None):
self._running = False
self._stopping = False # ⭐ 防止并发 stop 和 restart 的标志
self._thread: Optional[threading.Thread] = None
self._qq_thread: Optional[threading.Thread] = None
self._emergency_thread: Optional[threading.Thread] = None
@@ -896,7 +897,8 @@ class MessageMonitor:
full_restart: True=彻底重启(进程级,重新检测内存恢复GPU)
False=急停/手动停止恢复(软重启,不重新检测内存)
"""
if self._running:
# ⭐ 防止并发 stop 和 restart:如果正在停止中,不允许启动
if self._running or self._stopping:
return
self._running = True
# ⭐ 重置停止信号:确保新轮次不被上一轮残留的 stop_event 阻断
@@ -977,6 +979,11 @@ class MessageMonitor:
logger.info("消息监控已启动")
def stop(self):
# ⭐ 防止并发 stop:如果已经在停止中,直接返回
if self._stopping:
return
self._stopping = True
# ⭐ 立即触发停止信号:所有 event.wait() 阻塞会被唤醒
self._stop_event.set()
self._running = False
@@ -1020,6 +1027,9 @@ class MessageMonitor:
# 主动释放内存,交还系统重新分配调度
self._release_memory()
# ⭐ 清除停止标志,允许后续启动
self._stopping = False
@property
def is_running(self) -> bool:
@@ -4649,6 +4659,9 @@ class MessageMonitor:
continue
texts.append(line)
# ⭐ 2026-08-29 修复:在单条消息通过所有过滤器后入库,替代 _ocr_text_has_fresh_timestamp 中整屏入库
if self._msg_history:
self._msg_history.record_user_message(platform, line)
logger.debug(f"[{platform}] OCR -> {len(ocr_lines)} raw, {len(texts)} filtered, "
f"last=\"{texts[-1][:50] if texts else ''}\"")
@@ -6449,7 +6462,7 @@ class MessageMonitor:
if not text:
logger.debug(f"[ocr] 过滤器1结果: 空文本,返回False")
return False
# ⭐ 提前定义 stripped,供后续 record_user_message 使用(修复 NameError)
# ⭐ 提前定义 stripped,供后续日志和过滤器使用
stripped = text.strip()
# 标准化时间戳:规整 OCR 逐字输出的时间戳(清除字符间空格 + 全角冒号→ASCII)
# 16:28:40 → 16:28:40, 1 6 : 2 8 : 4 0 → 16:28:40(见 _normalize_timestamps,修复孤儿空格导致漏消息)
@@ -6723,8 +6736,6 @@ class MessageMonitor:
try:
if not self._is_timestamp_old(ts, 300):
logger.debug(f"[ocr] 过滤器1结果: 无日期+新鲜时间戳{ts}(≤300s),放行")
# ⭐ 2026-08-29 修复:放行前记录用户消息,避免 user_messages 空表
self._msg_history.record_user_message(platform, stripped)
return True
except Exception:
pass
@@ -6740,7 +6751,6 @@ class MessageMonitor:
_after_last_ts = normalized[_last_ts_pos + len(_last_ts):] if _last_ts_pos >= 0 else ''
if '?' in _after_last_ts or '?' in _after_last_ts:
logger.debug(f"[ocr] 过滤器1结果: 无日期+时间戳过期但最后时间戳后有问号(新查询),放行")
self._msg_history.record_user_message(platform, stripped)
return True
# ⭐ 2026-08-29 修复:无日期+时间戳全部过期 → 必须丢弃,不可放行
# QQ聊天窗口中历史消息(如12:08/12:24)不带日期,但时间戳准确且已过期(>300s)。
@@ -6770,8 +6780,6 @@ class MessageMonitor:
return False
# 放行给下游DB查重
logger.debug(f"[ocr] 过滤器1结果: 无日期+无时间戳+中文消息,放行给下游DB查重")
# ⭐ 2026-08-29 修复:放行前记录用户消息,避免 user_messages 空表
self._msg_history.record_user_message(platform, stripped)
return True
if not timestamps:
# ⭐ 无可见时间戳:检查噪声文本
@@ -6801,7 +6809,6 @@ class MessageMonitor:
if not self._msg_history.is_user_message_replied(platform, stripped, time.time()):
# 数据库无记录,无条件放行
logger.debug(f"[ocr] 过滤器1结果: 无时间戳但用户消息挨着bot消息且无记录,放行: {stripped[:80]!r}")
self._msg_history.record_user_message(platform, stripped)
return True
else:
# 数据库有记录,整段丢弃
@@ -6811,15 +6818,12 @@ class MessageMonitor:
# 无论消息长短,只要包含中文字符且非噪声,都放行
# DB查重会自动过滤掉已经回复过的消息
logger.info(f"[ocr] 过滤器1结果: 无时间戳但为中文消息,交由DB查重: {stripped[:80]!r}")
self._msg_history.record_user_message(platform, stripped)
return True
# 有新鲜时间戳 → 确定有新消息
for ts in timestamps:
try:
if not self._is_timestamp_old(ts, 300):
logger.debug(f"[ocr] 过滤器1结果: 发现新鲜时间戳{ts}(≤300s),放行")
# ⭐ 记录用户消息到DB,防止重启后重复处理
self._msg_history.record_user_message(platform, stripped)
return True
except Exception:
pass