"""Apply backed-up Qt bridge fixes; leave real pending queues untouched.""" import ast from pathlib import Path import shutil import os base = Path(__file__).parent def replace_method(source, class_name, method_name, replacement): tree = ast.parse(source) cls = next(n for n in tree.body if isinstance(n, ast.ClassDef) and n.name == class_name) node = next(n for n in cls.body if isinstance(n, ast.FunctionDef) and n.name == method_name) lines = source.splitlines(keepends=True) return ''.join(lines[:node.lineno-1]) + replacement.rstrip() + '\n' + ''.join(lines[node.end_lineno:]) FILTER=''' def _apply_task_filter(self) -> None: if not hasattr(self, "task_list"): return mode = getattr(self, "active_task_filter", "全部") for row in range(self.task_list.count()): item = self.task_list.item(row) display = self._display_task_status(str(item.data(Qt.UserRole + 1) or "")) item.setHidden(mode != "全部" and display != mode) current = self.task_list.currentRow() selected = self.task_list.item(current) if current >= 0 else None if selected is None or selected.isHidden(): visible = next((row for row in range(self.task_list.count()) if not self.task_list.item(row).isHidden()), -1) self.task_list.setCurrentRow(visible) if visible >= 0: self._select_visible_task(visible) ''' DISPLAY=''' def _display_task_status(state: str) -> str: if "审核" in state: return "待审核" if "人工" in state or "待核对" in state or "处理失败" in state: return "需人工" if state.startswith("正在"): return "处理中" if "重试" in state: return "待重试" return "等待中" ''' STATE=''' def _state_text(state: dict) -> str: review = task_review_state(state) send_state = str(state.get("send_state") or "") if send_state == "sending": return "正在发送" if review["deliveryUncertain"]: return "发送待核对(需人工)" if send_state == "sent_uncommitted": return "已发送,待归档" if review["needsHuman"]: return "需要人工处理" if review["awaitingReview"]: return "等待人工审核" stage = str(state.get("stage") or "") if stage in QueuePage.STAGE_LABELS: return QueuePage.STAGE_LABELS[stage] if state.get("batch_ready"): return "正在生成回复" if state.get("confirmed_unread"): return "等待处理" return "排队中" ''' for root in (Path('C:/kefu/wechat_rpa'),Path('C:/wechat_rpa')): path=root/'wechat_gui_qt.py' source=path.read_text(encoding='utf-8') backup=root/'backups/review-visibility-20260916/qt' backup.mkdir(parents=True,exist_ok=True) if (backup/path.name).exists(): assert path.read_bytes() == (backup/path.name).read_bytes(), 'Source changed since backup' else: shutil.copy2(path,backup/path.name) shutil.copy2(base/'review_state.py',root/'review_state.py') source=source.replace('import json\n','import json\nfrom review_state import task_review_state\n',1) source=source.replace('"manual_review": "等待人工审核",','"manual_review": "等待人工审核",\n "awaiting_review": "等待人工审核",',1) source=replace_method(source,'QueuePage','_display_task_status',DISPLAY) source=replace_method(source,'QueuePage','_state_text',STATE) source=replace_method(source,'QueuePage','_apply_task_filter',FILTER) source=source.replace('("全部", "处理中", "等待中", "待重试")','("全部", "待审核", "需人工", "处理中", "等待中", "待重试")') source=source.replace('self.host.queue_page._set_task_filter(name or "全部")\n self.host._push_console_state()', 'self.host.queue_page._set_task_filter(name if name in {"全部", "待审核", "需人工", "处理中", "等待中", "待重试"} else "全部")\n self.host._console_selected_key = ""\n self.host._push_console_state(force=True)',1) # Actual raw state, not a localized label, controls allowed buttons. needle='"awaitingReview": bool(state.get("awaiting_review", False)),' assert needle in source source=source.replace(needle,'**task_review_state(state),\n "isProtocol": bool(state.get("detected_by") == "protocol_db" or state.get("conv_id")),',1) source=source.replace('"reviewReason": str(state.get("review_reason") or ""),','"reviewReason": task_review_state(state)["reviewReason"],',1) source=source.replace('state.get("staged_user_text") or table_message or ""','state.get("staged_user_text") or state.get("chat_text") or table_message or ""',1) # Counts must include hidden rows, otherwise selecting another filter hides the alert. needle=' processing = sum(1 for item in people if item.get("status") == "处理中")' assert needle in source source=source.replace(needle,' review_states = [task_review_state(value) for value in (getattr(self.queue_page, "pending_states", {}) or {}).values()]\n'+needle,1) source=source.replace('"processing": str(processing),','"processing": str(processing),\n "review": str(sum(value["awaitingReview"] and not value["needsHuman"] for value in review_states)),\n "human": str(sum(value["needsHuman"] for value in review_states)),',1) # Immediately re-read persisted state after approval rather than showing stale controls. start=source.index(' def _approve_queue_tasks(') end=source.index(' def _retry_queue_tasks(',start) part=source[start:end] part=part.replace(' self._schedule_console_push(urgent=True)',' self.queue_page.refresh_data()\n self._schedule_console_push(urgent=True)') part=part.replace(' error = str(result.get("error") or "")',' if result.get("protected"):\n QMessageBox.information(self, "任务受保护", "当前账号不匹配或任务正在发送/核对回执,不能批准重发。请先核对后转人工处理。")\n error = str(result.get("error") or "")',1) # Protocol startup may be async. Explicit approval is queued until startup succeeds. needle=''' if thread is None or not thread.is_alive(): QMessageBox.warning(''' if 'if transport == PROTOCOL:' in source: part=part.replace(needle,''' if (thread is None or not thread.is_alive()) and getattr(self, "_protocol_start_pending", False): self._deferred_review_approvals = sorted(set(getattr(self, "_deferred_review_approvals", [])) | set(requested)) self.append_log("已登记审核操作,协议环境检查通过后将核对会话并继续。", "notify") return '''+needle,1) source=source.replace(' self._thread.start()\n', ' self._thread.start()\n deferred = list(getattr(self, "_deferred_review_approvals", []))\n self._deferred_review_approvals = []\n if deferred:\n QTimer.singleShot(0, lambda keys=deferred: self._approve_queue_tasks(keys))\n',1) source=source.replace(' def stop_monitoring(self) -> None:\n',' def stop_monitoring(self) -> None:\n self._deferred_review_approvals = []\n',1) # Re-find after the prior source insertion. start=source.index(' def _approve_queue_tasks(');end=source.index(' def _retry_queue_tasks(',start) source=source[:start]+part+source[end:] compile(source,str(path),'exec') temporary = path.with_name(path.name + '.review-patch.tmp') temporary.write_text(source,encoding='utf-8') os.replace(temporary,path) print(root)