Files
kefu/deploy/review-assistant-20260917/apply_visual.py
T
2026-09-21 10:34:06 +08:00

55 lines
6.7 KiB
Python

from pathlib import Path
import ast
base = Path(__file__).parent
methods = (base / 'visual_methods.txt').read_text(encoding='utf-8')
methods = methods.replace('review_assistant_id', 'assistant_review_id').replace('self.enqueue_external_reply(', 'self.enqueue_detected(')
methods = methods.replace(' evidence = assistant.reconcile(state, context)', ' if not self._persist_pending_replies():\n continue\n evidence = assistant.reconcile(state, context)')
def replace_once(text, old, new, count=1):
actual = text.count(old)
if actual != count:
raise AssertionError((actual, count, old[:100]))
return text.replace(old, new)
for root in (Path('C:/kefu/wechat_rpa'), Path('C:/wechat_rpa')):
p = root / 'wechat_bot.py'
text = p.read_text(encoding='utf-8')
text = replace_once(text, ' def _review_mode_enabled(self) -> bool:\n', methods + '\n def _review_mode_enabled(self) -> bool:\n')
text = replace_once(text, ' blanket_review = self._review_mode_enabled()',
' blanket_review = (self._review_mode_enabled()\n and not self._review_assistant_resumed(pending_state))')
text = replace_once(text, ' self._last_send_failure_reason = reason\n self.report_operation("人工审核",',
' if persisted:\n self._register_assistant_review(expected_fp)\n self._last_send_failure_reason = reason\n self.report_operation("人工审核",')
text = replace_once(text, ' pending_state = getattr(self, "_pending_reply_sessions", {}).get(key) or {}\n if not exchange:',
' pending_state = getattr(self, "_pending_reply_sessions", {}).get(key) or {}\n self._record_assistant_automated_send(pending_state)\n if not exchange:')
text = replace_once(text, ' reason = excluded_session_reason(name, metadata=supplied) or excluded_session_reason(name, metadata=state)',
' reason = excluded_session_reason(name, metadata=supplied) or excluded_session_reason(name, metadata=state)\n if not reason and self._review_assistant_recipient({**state, "display_name": name, "database_event": supplied or event}):\n reason = "审核助理联系人仅接收提醒,不参与自动回复"')
# Distinct raw old AI draft guard runs inside existing verified composer flow.
text = replace_once(text, ' if normalized_draft:\n if self._draft_is_our_own(',
' if normalized_draft:\n if self._is_discarded_review_draft(pending_state, existing_draft):\n pyautogui.hotkey(\'ctrl\', \'a\')\n pyautogui.press(\'backspace\')\n self._clear_foreign_draft_hold(expected_fp)\n print(" [助理审核] 已核对人工回复,仅清除本会话作废的原始 AI 草稿,重新处理新消息。")\n elif self._draft_is_our_own(')
# Survive restart without turning retired drafts into queue items.
text = replace_once(text, ' self._manual_handoffs = {}\n path = getattr(self, "_pending_reply_path", "")',
' self._manual_handoffs = {}\n self._review_assistant_discarded_drafts = {}\n path = getattr(self, "_pending_reply_path", "")')
text = replace_once(text, ' handoffs = raw.get("manual_handoffs")',
' discarded = raw.get("assistant_discarded_drafts")\n if isinstance(discarded, dict):\n self._review_assistant_discarded_drafts = {str(key): dict(value) for key, value in discarded.items()\n if isinstance(value, dict) and value.get("account") and value.get("conv_id")}\n handoffs = raw.get("manual_handoffs")')
text = replace_once(text, ' "review_reason": str(value.get("review_reason") or "")[:500],',
' "account": str(value.get("account") or ""),\n "conv_id": str(value.get("conv_id") or ""),\n "assistant_review_id": str(value.get("assistant_review_id") or ""),\n "review_reason": str(value.get("review_reason") or "")[:500],', count=2)
text = replace_once(text, ' if handoffs:\n serializable = {"pending": serializable, "manual_handoffs": handoffs}',
' discarded = getattr(self, "_review_assistant_discarded_drafts", {})\n if handoffs or discarded:\n serializable = {"pending": serializable, "manual_handoffs": handoffs,\n "assistant_discarded_drafts": discarded}')
text = replace_once(text, ' state.pop("review_requested_at", None)\n state.pop("review_reason", None)',
' state.pop("review_requested_at", None)\n state.pop("assistant_review_id", None)\n state.pop("review_reason", None)')
ast.parse(text)
p.write_text(text, encoding='utf-8', newline='\n')
p = root / 'gui_runtime.py'
text = p.read_text(encoding='utf-8')
text = replace_once(text, ' self._reply_wakeup.clear()\n if bot.security_verification_required:',
' self._reply_wakeup.clear()\n # Read-only assistant reconciliation remains available while the screen is busy.\n try:\n bot.poll_review_assistant()\n except Exception as exc:\n self.target_queue.put(("log", f"[助理审核] 核验暂不可用({type(exc).__name__}),保留审核任务"))\n if bot.security_verification_required:')
text = replace_once(text, ' if self._db_source is not None:\n try:\n self._db_source.close()',
' if bot is not None:\n try:\n bot.close_review_assistant()\n except Exception:\n pass\n if self._db_source is not None:\n try:\n self._db_source.close()')
# User stop should revoke reminders before disposing the local database.
text = replace_once(text, ' if self._db_source is not None:\n try:\n self._db_source.close()',
' active_bot = getattr(self, "bot", None)\n if active_bot is not None:\n try:\n active_bot.close_review_assistant()\n except Exception:\n pass\n if self._db_source is not None:\n try:\n self._db_source.close()')
ast.parse(text)
p.write_text(text, encoding='utf-8', newline='\n')
print('Updated visual review hooks:', root)