From 2cf6ceae8eb4f3badef85299ac287eb738ad70ac Mon Sep 17 00:00:00 2001 From: duffyduck Date: Mon, 29 Dec 2025 01:35:31 +0100 Subject: [PATCH] tts message not read controlcodes --- python_bridge/chat_audio_bridge.py | 1 + python_bridge/chat_web_interface.py | 109 +++++++++++++--------------- 2 files changed, 52 insertions(+), 58 deletions(-) diff --git a/python_bridge/chat_audio_bridge.py b/python_bridge/chat_audio_bridge.py index c717f51..aa4d1e2 100755 --- a/python_bridge/chat_audio_bridge.py +++ b/python_bridge/chat_audio_bridge.py @@ -1239,6 +1239,7 @@ Erst wenn ich [READY] sehe, starten die TICKs mit Bildern!""" # Marker entfernen text = re.sub(r'\[TICK\]', '', text) text = re.sub(r'\[START\]', '', text) + text = re.sub(r'\[READY\]', '', text) # [READY] nicht vorlesen # Fahrbefehle entfernen text = re.sub(r'\[(FORWARD|BACKWARD|LEFT|RIGHT|STOP)\]', '', text) diff --git a/python_bridge/chat_web_interface.py b/python_bridge/chat_web_interface.py index 9e44718..c807d6e 100644 --- a/python_bridge/chat_web_interface.py +++ b/python_bridge/chat_web_interface.py @@ -679,16 +679,16 @@ class ClaudeChatInterface: if not claude_elements: try: # Claude.ai 2024/2025 nutzt oft conversation-turn Divs - # WICHTIG: Wir suchen den CONTAINER der die gesamte Claude-Nachricht enthält, - # nicht nur das Element mit "Claude sagt:" - denn [READY] kann in einem - # separaten Child-Element stehen! + # WICHTIG: Wir suchen EINZELNE Nachrichten, nicht den ganzen Chat! + # Jede "Claude sagt:" Nachricht soll ein separates Element sein. found = self.driver.execute_script(""" const msgs = []; - // Strategie: Finde Elemente mit "Claude sagt:" und dann den - // ÜBERGEORDNETEN Container der die vollständige Nachricht enthält - const allDivs = document.querySelectorAll('div, p, span'); - for (const elem of allDivs) { + // Strategie 1: Suche nach einzelnen Absätzen/Divs die MIT "Claude sagt:" beginnen + // NICHT nach Containern die den ganzen Chat enthalten! + const allElements = document.querySelectorAll('p, div, span'); + + for (const elem of allElements) { // Überspringe wenn es ein user-message ist if (elem.getAttribute('data-testid') === 'user-message') continue; if (elem.closest('[data-testid="user-message"]')) continue; @@ -702,55 +702,42 @@ class ClaudeChatInterface: // Hole den direkten Text-Inhalt const text = (elem.innerText || '').trim(); - // Text muss mit "Claude sagt:" BEGINNEN - if (text.startsWith('Claude sagt:') && text.length > 20 && text.length < 10000) { - // Suche nach dem übergeordneten Container (z.B. prose-Container) - // der auch [READY] enthalten könnte - let container = elem; - let parent = elem.parentElement; - - // Gehe nach oben bis wir einen guten Container finden - // (max 5 Ebenen, um nicht die ganze Seite zu erfassen) - for (let i = 0; i < 5 && parent; i++) { - const parentText = (parent.innerText || '').trim(); - - // Stoppe wenn wir einen zu großen Container erreichen - if (parentText.length > 15000) break; - - // Stoppe wenn wir in user-message oder Navigation sind - if (parent.getAttribute('data-testid') === 'user-message') break; - if (parent.closest('nav')) break; - - // Prüfe ob der Parent-Container [READY] enthält und - // immer noch mit "Claude sagt:" beginnt - if (parentText.startsWith('Claude sagt:') || - parentText.includes('[READY]') || - parent.classList.contains('prose') || - parent.classList.contains('markdown')) { - container = parent; + // STRENG: Text muss EXAKT mit "Claude sagt:" beginnen + // UND darf nicht den ganzen Chat sein (max 3000 Zeichen für eine Nachricht) + if (text.startsWith('Claude sagt:') && text.length > 20 && text.length < 3000) { + // Prüfe ob dieses Element nicht schon in einem anderen enthalten ist + // (wir wollen das KLEINSTE passende Element) + let dominated = false; + for (const other of msgs) { + if (other !== elem && elem.contains(other)) { + // Dieses Element enthält ein anderes - überspringe es + dominated = true; + break; } - - parent = parent.parentElement; } - msgs.push(container); + if (!dominated) { + // Entferne Elemente die DIESES Element enthalten + for (let i = msgs.length - 1; i >= 0; i--) { + if (msgs[i].contains(elem)) { + // Das alte Element enthält uns - entferne es + msgs.splice(i, 1); + } + } + msgs.push(elem); + } } } - // Dedupliziere: Behalte nur die ÄUSSERSTEN (größten) Container - // (aber nicht zu groß - max 10000 Zeichen) + // Dedupliziere basierend auf identischem Text (manchmal gibt es Duplikate) + const seen = new Set(); const unique = []; for (const div of msgs) { - // Prüfe ob dieses Element schon in einem anderen enthalten ist - let isDuplicate = false; - for (const other of msgs) { - if (other !== div && other.contains(div)) { - // Dieses Element ist in einem anderen enthalten - isDuplicate = true; - break; - } - } - if (!isDuplicate && !unique.includes(div)) { + const text = (div.innerText || '').trim(); + // Nutze die ersten 100 Zeichen als Schlüssel + const key = text.substring(0, 100); + if (!seen.has(key)) { + seen.add(key); unique.push(div); } } @@ -887,17 +874,23 @@ class ClaudeChatInterface: logger.debug(f"Überspringe User-Befehl (normalized): {text}") continue - # Prüfe ob es wirklich eine Claude-Nachricht ist (muss mit "Claude sagt:" BEGINNEN) + # Prüfe ob es wirklich eine Claude-Nachricht ist (muss "Claude sagt:" enthalten) + # WICHTIG: Manchmal wird der Chat-Titel (z.B. "Claude steuert...") mit + # erfasst - in dem Fall müssen wir den Text ab "Claude sagt:" extrahieren if not text.startswith("Claude sagt:"): - # Fallback: Prüfe ob "Claude sagt:" irgendwo am Anfang einer Zeile steht - lines = text.split('\n') - found_claude_line = False - for line in lines[:5]: # Nur erste 5 Zeilen prüfen - if line.strip().startswith("Claude sagt:"): - found_claude_line = True - break - if not found_claude_line: - logger.debug(f"Claude-Element {i} übersprungen: Beginnt nicht mit 'Claude sagt:'") + # Fallback: Prüfe ob "Claude sagt:" irgendwo im Text steht + # und extrahiere den Text ab dort + if "Claude sagt:" in text: + # Finde alle Vorkommen von "Claude sagt:" und extrahiere die Nachrichten + import re + # Finde den Index des ersten "Claude sagt:" + claude_sagt_idx = text.find("Claude sagt:") + if claude_sagt_idx > 0: + # Schneide alles vor "Claude sagt:" ab (z.B. Chat-Titel) + text = text[claude_sagt_idx:] + logger.debug(f"Claude-Element {i}: Text gekürzt ab 'Claude sagt:' (vorher {len(text) + claude_sagt_idx} Zeichen)") + else: + logger.debug(f"Claude-Element {i} übersprungen: Enthält kein 'Claude sagt:'") continue # Filtere Sidebar/Navigation Elemente (beginnen oft mit "Neuer Chat")