from pathlib import Path import shutil helper = ''' def _wait_for_database_voice(self, fp, *, visible_voice=False): """Yield this task while local ASR works; unrelated conversations continue.""" from voice_messages import voice_batch_status state = self._pending_reply_state(fp) context = getattr(self, "_last_database_read", None) bound = isinstance(context, dict) and context.get("fp_hex") == bytes(fp or b"").hex() voice = voice_batch_status(context) if bound else {"status": "none"} if visible_voice and voice["status"] == "none": voice = {"status": "pending", "key": "visible-voice:" + bytes(fp or b"").hex(), "reason": "等待语音对应的本机消息库和音频文件"} if voice["status"] in {"none", "ready"}: if state: state.pop("voice_wait_key", None) state.pop("voice_wait_started_at", None) return False if state is None: return True # No durable task identity: never guess or answer an unheard voice. now = time.time() if state.get("voice_wait_key") != voice.get("key"): state.update(voice_wait_key=voice.get("key"), voice_wait_started_at=now) waiting = voice["status"] == "pending" and now - float(state.get("voice_wait_started_at") or now) < 180 if waiting: state["ready_at"] = now + 2 self._set_task_stage(fp, "voice_transcribing", detail=voice.get("reason") or "语音正在本机转文字") else: reason = voice.get("reason") if voice["status"] == "error" else "语音转文字等待超时,尚未取得可核验文字,请人工处理" state.update(manual_required=True, manual_reason=reason, awaiting_review=False, reply_text="", staged_reply_text="", chat_text=str((context or {}).get("text") or "")) self._set_task_stage(fp, "error", detail="语音需要人工核对", error=reason) self._register_assistant_review(fp) self._persist_pending_replies() return True ''' for label, project in [('source', Path('C:/kefu/wechat_rpa')), ('production', Path('C:/wechat_rpa'))]: path = project / 'wechat_bot.py' backup = Path(__file__).parent / 'before-voice-integration' / label backup.mkdir(parents=True, exist_ok=True) shutil.copy2(path, backup / path.name) text = path.read_text(encoding='utf-8') needle = ' def _database_message_direction(self, fp: bytes, text: str):\n' assert text.count(needle) == 1 text = text.replace(needle, helper + needle) needle = ''' database_text = self._database_chat_text(fp) if database_text: chat_text = database_text ''' assert text.count(needle) == 1 text = text.replace(needle, needle + ''' if self._wait_for_database_voice(fp): return None from voice_messages import voice_batch_status database_voice_ready = bool(database_text and voice_batch_status(getattr(self, "_last_database_read", None))["status"] == "ready") ''') needle = ''' first_untrusted_batch = bool( first_record_batch and not first_explicit_customer_text ) ''' assert text.count(needle) == 1 text = text.replace(needle, ''' first_untrusted_batch = bool( first_record_batch and not first_explicit_customer_text and not database_voice_ready ) if database_voice_ready: # Exact database identity and successful ASR make voice text available to normal model/risk processing. reliable_text_pending = True force_media_check = False ''') needle = ''' self.report_operation("AI 准备", 2, 3, "语音没有转写,生成安全追问", fp) print(" [AI] 检测到未转写语音,使用不猜测内容的安全回复") ai_reply = safe_media_reply(media_types) ''' assert text.count(needle) == 1 text = text.replace(needle, ''' self._wait_for_database_voice(fp, visible_voice=True) return None ''') path.write_text(text, encoding='utf-8') print('Visual voice waiting/model flow integrated with targeted edits in both variants.')