from pathlib import Path import shutil ROOT = Path(__file__).parent for label, project in [('source', Path('C:/kefu/wechat_rpa')), ('production', Path('C:/wechat_rpa'))]: backup = ROOT / 'before-voice-integration' / label backup.mkdir(parents=True, exist_ok=True) path = project / 'wxwork_db.py' shutil.copy2(path, backup / path.name) text = path.read_text(encoding='utf-8') text = text.replace(' self.db_base = db_base\n', ' self.db_base = db_base\n self._voice_reader = None\n', 1) needle = ' content = parse_content(msg.get("content"))\n' assert text.count(needle) == 1 text = text.replace(needle, ''' from voice_messages import is_voice, cached_voice_fields voice_fields = {} if is_voice(msg): voice_fields = cached_voice_fields(getattr(self, "_conns", {}).get(user_dir), str(user_dir), conv_id, msg.get("server_id"), msg.get("content")) content = voice_fields.get("content", parse_content(msg.get("content"))) ''') needle = ' "dedup_key": f"{user_dir}:{conv_id}:{stable_id}",\n' assert text.count(needle) == 1 text = text.replace(needle, needle + ' **voice_fields,\n') needle = ' if not messages:\n return None\n lines = []\n' assert text.count(needle) == 1 text = text.replace(needle, ''' if not messages: return None # ASR runs on a separate worker. Only this unanswered customer batch is queued. if any(int(m.get("content_type") or 0) in (4, 16) for m in messages): if getattr(self, "_voice_reader", None) is None and getattr(self, "db_base", None): from voice_messages import VoiceMessageReader self._voice_reader = VoiceMessageReader(self.db_base) if getattr(self, "_voice_reader", None) is not None: self._voice_reader.prepare(messages) lines = [] ''') needle = ' def close(self):\n self._closed = True\n' assert text.count(needle) == 1 text = text.replace(needle, needle + ''' voice_reader = getattr(self, "_voice_reader", None) if voice_reader is not None: voice_reader.close() self._voice_reader = None ''') path.write_text(text, encoding='utf-8') if label == 'production': shutil.copy2('C:/kefu/wechat_rpa/voice_messages.py', project / 'voice_messages.py') # Protocol integration uses the current exact DB snapshot, not the original event's media placeholder. path = Path('C:/wechat_rpa/protocol_engine.py') shutil.copy2(path, ROOT / 'before-voice-integration' / 'production' / path.name) text = path.read_text(encoding='utf-8') text = text.replace("def _is_text_message(message):\n try:", "def _is_text_message(message):\n from voice_messages import verified_transcript\n if verified_transcript(message):return True\n try:", 1) needle = " manual_reason=_unanswered_manual_reason(context)\n if not _is_text_message(state):manual_reason=manual_reason or MANUAL_NON_TEXT_REASON\n" assert text.count(needle) == 1 text = text.replace(needle, ''' from voice_messages import voice_batch_status, unanswered_messages voice = voice_batch_status(context) manual_reason = '' if voice['status'] == 'pending': now = time.time() with self._lock: if state.get('voice_wait_key') != voice['key']: state.update(voice_wait_key=voice['key'], voice_wait_started_at=now) waited = now - float(state.get('voice_wait_started_at') or now) if waited < 180: state.update(ready_at=now+2, reply_text='', staged_reply_text='') if waited < 180: self._stage(key,'voice_transcribing',voice['reason']);return manual_reason='语音转文字等待超时,尚未取得可核验文字,请人工处理' elif voice['status'] == 'error': manual_reason=voice['reason'] else: state.pop('voice_wait_key',None);state.pop('voice_wait_started_at',None) manual_reason=manual_reason or _unanswered_manual_reason(context) current=(context or {}).get('last_message') or {} if current.get('dedup_key')!=state.get('dedup_key') or not _is_text_message(current): manual_reason=manual_reason or MANUAL_NON_TEXT_REASON if not manual_reason: # The current question/risk check and archive receive the same transcribed batch as the model. batch=unanswered_messages(context) state.update(staged_user_text='\\n'.join(str(m.get('content') or '') for m in batch), last_lines=[str(m.get('content') or '') for m in batch]) ''') path.write_text(text, encoding='utf-8') print('Voice DB and protocol integration applied to both variants; original files backed up.')