diff --git a/python_bridge/chat_web_interface.py b/python_bridge/chat_web_interface.py index 0fe9f4e..0648454 100644 --- a/python_bridge/chat_web_interface.py +++ b/python_bridge/chat_web_interface.py @@ -609,39 +609,83 @@ class ClaudeChatInterface: except Exception as e: logger.debug(f"Prose-Suche fehlgeschlagen: {e}") - # Methode D: Alle Nachrichten-Divs durchsuchen und nach Rolle filtern + # Methode D: Suche nach conversation-turn Struktur (neue Claude.ai Version) if not claude_elements: try: - # Suche nach allen Nachrichten und filtere nach data-role oder aria-label + # Claude.ai 2024/2025 nutzt oft conversation-turn Divs found = self.driver.execute_script(""" const msgs = []; - // Suche nach verschiedenen Indikatoren für Claude-Nachrichten - // 1. data-role="assistant" - document.querySelectorAll('[data-role="assistant"]').forEach(e => msgs.push(e)); + // Suche nach Nachrichten die KEINE user-message sind + // und einen substantiellen Text-Inhalt haben + const allDivs = document.querySelectorAll('div'); + for (const div of allDivs) { + // Überspringe wenn es ein user-message ist + if (div.getAttribute('data-testid') === 'user-message') continue; + if (div.closest('[data-testid="user-message"]')) continue; - // 2. aria-label enthält "assistant" oder "Claude" - if (msgs.length === 0) { - document.querySelectorAll('[aria-label*="assistant"], [aria-label*="Claude"]').forEach(e => msgs.push(e)); - } + // Suche nach Elementen die typische Claude-Antwort-Merkmale haben + const text = div.innerText || ''; - // 3. Klasse enthält "assistant" - if (msgs.length === 0) { - document.querySelectorAll('[class*="assistant"]').forEach(e => { - // Filtere Buttons und andere UI-Elemente - if (e.tagName !== 'BUTTON' && !e.querySelector('button')) { - msgs.push(e); + // Claude-Antworten haben oft "Claude sagt:" oder sind länger als TICKs + if (text.includes('Claude sagt:') || + (text.length > 50 && !text.startsWith('[TICK]') && !text.startsWith('[START]'))) { + + // Prüfe ob dieses Element nicht schon ein Elternteil eines gefundenen ist + let dominated = false; + for (const existing of msgs) { + if (existing.contains(div) || div.contains(existing)) { + dominated = true; + break; + } } - }); + if (!dominated && !msgs.includes(div)) { + // Nimm das kleinste Element das den Text enthält + msgs.push(div); + } + } } + // Dedupliziere: Behalte nur die innersten Elemente + const filtered = msgs.filter(div => { + for (const other of msgs) { + if (other !== div && div.contains(other)) { + return false; + } + } + return true; + }); + + return filtered; + """) or [] + if found: + claude_elements = found + logger.debug(f"Claude-Nachrichten via Text-Suche: {len(found)}") + except Exception as e: + logger.debug(f"Text-Suche fehlgeschlagen: {e}") + + # Methode E: Letzte Fallback - Klasse enthält "assistant" (UI-Filter) + if not claude_elements: + try: + found = self.driver.execute_script(""" + const msgs = []; + document.querySelectorAll('[class*="assistant"]').forEach(e => { + // Filtere Buttons, kleine Elemente und UI-Komponenten + if (e.tagName === 'BUTTON') return; + if (e.querySelector('button')) return; + const text = (e.innerText || '').trim(); + // Nur Elemente mit substantiellem Text + if (text.length > 20 && !text.startsWith('[TICK]')) { + msgs.push(e); + } + }); return msgs; """) or [] if found: claude_elements = found - logger.debug(f"Claude-Nachrichten via Fallback-Suche: {len(found)}") + logger.debug(f"Claude-Nachrichten via class-assistant: {len(found)}") except Exception as e: - logger.debug(f"Fallback-Suche fehlgeschlagen: {e}") + logger.debug(f"class-assistant-Suche fehlgeschlagen: {e}") # ═══════════════════════════════════════════════════════════════ # DEBUG: Falls keine Claude-Nachrichten gefunden, DOM analysieren @@ -713,12 +757,30 @@ class ClaudeChatInterface: logger.debug(f"Fehler bei User-Nachricht {i}: {e}") # Claude-Nachrichten hinzufügen + # WICHTIG: Filtere User-Befehle die fälschlicherweise erkannt werden + user_commands = [ + '[TICK]', '[START]', '[FORWARD]', '[BACKWARD]', '[LEFT]', '[RIGHT]', '[STOP]', + '[LOOK_LEFT]', '[LOOK_RIGHT]', '[LOOK_UP]', '[LOOK_DOWN]', '[LOOK_CENTER]', + '[READY]' + ] + for i, elem in enumerate(claude_elements): try: text = elem.text.strip() if not text or len(text) < 5: continue + # Filtere User-Befehle (diese sind KEINE Claude-Nachrichten) + if text in user_commands: + logger.debug(f"Überspringe User-Befehl: {text}") + continue + + # Filtere auch wenn der Text nur aus einem Befehl besteht (evtl. mit Whitespace) + text_upper = text.upper().strip() + if any(text_upper == cmd for cmd in user_commands): + logger.debug(f"Überspringe User-Befehl (normalized): {text}") + continue + msg_id = elem.get_attribute("data-message-id") if not msg_id: msg_id = f"claude_{i}_{hash(text[:100])}"