From d05ab23c1b7012d12a9907b68ade8fedbc207f8e Mon Sep 17 00:00:00 2001 From: ARIA Date: Mon, 20 Jul 2026 00:21:20 +0000 Subject: [PATCH] Spotify: read-only Verbindungstest-Skript (Toolset/Login/Live-API), analog zum Auth-Skript Prueft direkt in Hermes' eigenem Code (get_auth_status, _get_platform_tools, SpotifyClient), ob Toolset aktiviert, Login vorhanden und ein echter GET /me/player/devices erfolgreich ist -- ohne Umweg ueber Claude/Proxy, da LLM-Textantworten wie 'nicht verbunden/aktiv' kein verlaesslicher Diagnosewert sind. --- README.md | 26 +++++ scripts/spotify_test_connection.py | 155 +++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 scripts/spotify_test_connection.py diff --git a/README.md b/README.md index 814f205..62b62cc 100644 --- a/README.md +++ b/README.md @@ -294,6 +294,32 @@ nennen. Falls es weiter hakt: `docker logs hermes-proxy --tail 100 -f` waehrend der Reproduktion pruefen ob `--safe-mode` tatsaechlich im gestarteten Prozess ankam (`docker exec -it hermes-proxy ps aux | grep claude`). +### Verbindungstest ohne Chat/Claude (`spotify_test_connection.py`) + +Wenn Claude im Dashboard-Chat was sagt wie "Spotify-Tools sind zwar +gelistet, aber nicht verbunden/aktiv" ist unklar OB das stimmt oder ob +Claude nur raet — eine LLM-Textantwort ist kein verlaesslicher +Diagnosewert. `scripts/spotify_test_connection.py` prueft stattdessen +direkt in Hermes' eigenem Code (denselben Funktionen, die der echte +Tool-Dispatch benutzt), read-only, ohne irgendwas zu veraendern: + +1. Ist das Toolset `spotify` ueber `hermes tools` fuer irgendeine + Plattform aktiviert? (`~/.hermes/config.yaml` -> `platform_toolsets`) +2. Ist Spotify eingeloggt? (`~/.hermes/auth.json` -> `providers.spotify`) +3. Funktioniert ein ECHTER API-Call mit dem gespeicherten Token + (`GET /me/player/devices`, inkl. Auto-Refresh falls abgelaufen)? + +```bash +docker cp scripts/spotify_test_connection.py hermes-agent:/tmp/spotify_test_connection.py +docker exec -it hermes-agent python3 /tmp/spotify_test_connection.py +``` + +Sind alle drei Punkte gruen und der Chat sagt trotzdem "kenn ich nicht" / +"nicht verbunden", liegt's am Proxy bzw. am System-Prompt (siehe Abschnitt +oben), nicht an Spotify selbst — dann `docker logs hermes-proxy` waehrend +der Reproduktion pruefen. Gleiches Nutzungsmuster wie +`scripts/spotify_manual_auth.py` (Login-Skript weiter oben). + ## Mobiler Client (Android)? Zwei GRUNDVERSCHIEDENE Richtungen, nicht verwechseln: diff --git a/scripts/spotify_test_connection.py b/scripts/spotify_test_connection.py new file mode 100644 index 0000000..7c846bf --- /dev/null +++ b/scripts/spotify_test_connection.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +"""Spotify-Verbindungstest fuer Hermes -- prueft in drei Schritten, OHNE den +Umweg ueber Claude/den Proxy, ob Spotify aus dem Chat heraus wirklich +funktionieren sollte. + +Hintergrund: Wenn Claude im Dashboard-Chat sowas sagt wie "Spotify-Tools +sind zwar in meiner Werkzeugliste beschrieben, aber in dieser Session +aktuell nicht verbunden/aktiv", ist unklar OB das stimmt oder ob Claude nur +raet -- Text-Antworten vom LLM sind kein verlaesslicher Diagnosewert. Dieses +Skript ruft dieselben Hermes-internen Funktionen direkt auf, die auch beim +echten Tool-Dispatch benutzt werden (siehe plugins/spotify/tools.py +_check_spotify_available und plugins/spotify/client.py SpotifyClient): + + 1. Toolset "spotify" ueber `hermes tools` aktiviert? (pro Plattform, + ~/.hermes/config.yaml -> platform_toolsets) + 2. Bei Spotify eingeloggt? (~/.hermes/auth.json -> providers.spotify) + 3. Funktioniert ein ECHTER API-Call mit dem gespeicherten Token (inkl. + Auto-Refresh falls abgelaufen)? Ruft GET /me/player/devices auf -- + harmlos, read-only, keine Wiedergabe wird veraendert. + +Nutzung (im hermes-agent-Container, wo hermes_cli installiert ist): + docker cp scripts/spotify_test_connection.py hermes-agent:/tmp/spotify_test_connection.py + docker exec -it hermes-agent python3 /tmp/spotify_test_connection.py + +Reiner Read-Only-Diagnose-Check, veraendert nichts. Fuer den Login selbst +siehe scripts/spotify_manual_auth.py (gleiches Nutzungsmuster). +""" +from __future__ import annotations + +import sys + +try: + from hermes_cli.auth import get_auth_status +except ImportError as exc: # pragma: no cover + sys.exit( + "Konnte hermes_cli.auth nicht importieren -- Skript muss INNERHALB " + f"des hermes-agent-Containers laufen (python3 dort). Fehler: {exc}" + ) + +try: + from hermes_cli.config import load_config_readonly + from hermes_cli.tools_config import _get_platform_tools, PLATFORMS +except ImportError as exc: # pragma: no cover + sys.exit(f"Konnte hermes_cli.tools_config/config nicht importieren: {exc}") + +try: + from plugins.spotify.client import ( + SpotifyAPIError, + SpotifyAuthRequiredError, + SpotifyClient, + SpotifyError, + ) +except ImportError as exc: # pragma: no cover + sys.exit(f"Konnte plugins.spotify.client nicht importieren: {exc}") + + +def _bar(title: str) -> None: + print() + print("=" * 78) + print(title) + print("=" * 78) + + +def check_auth() -> bool: + _bar("1) Login-Status (~/.hermes/auth.json -> providers.spotify)") + status = get_auth_status("spotify") + logged_in = bool(status.get("logged_in")) + print(f" logged_in: {logged_in}") + print(f" auth_type: {status.get('auth_type')}") + print(f" client_id: {status.get('client_id')}") + print(f" redirect_uri: {status.get('redirect_uri')}") + print(f" scope: {status.get('scope')}") + print(f" expires_at: {status.get('expires_at')}") + print(f" has_refresh_token: {status.get('has_refresh_token')}") + if not logged_in: + print() + print(" -> NICHT eingeloggt. `python3 spotify_manual_auth.py` ausfuehren") + print(" (oder `hermes auth spotify`, falls SSH-Port-Forward moeglich ist).") + return logged_in + + +def check_toolsets() -> bool: + _bar("2) Toolset 'spotify' aktiviert? (hermes tools, pro Plattform)") + config = load_config_readonly() + any_enabled = False + for platform_key, info in sorted(PLATFORMS.items()): + try: + enabled = _get_platform_tools(config, platform_key) + except Exception as exc: + print(f" {platform_key:14s} -> Fehler beim Aufloesen: {exc}") + continue + is_on = "spotify" in enabled + any_enabled = any_enabled or is_on + marker = "AN " if is_on else "aus" + label = info.get("label", platform_key) if isinstance(info, dict) else platform_key + print(f" [{marker}] {platform_key:14s} ({label})") + if not any_enabled: + print() + print(" -> Fuer KEINE Plattform aktiviert. Im Container:") + print(" `hermes tools` -> zu Spotify runterscrollen -> Leertaste -> mit 's' speichern.") + return any_enabled + + +def check_live_call() -> bool: + _bar("3) Echter Live-API-Call (GET /me/player/devices, read-only)") + try: + client = SpotifyClient() + devices = client.get_devices() + except SpotifyAuthRequiredError as exc: + print(f" FEHLER (Auth erforderlich): {exc}") + return False + except SpotifyAPIError as exc: + print(f" FEHLER (Spotify-API, status={exc.status_code}): {exc}") + return False + except SpotifyError as exc: + print(f" FEHLER: {exc}") + return False + except Exception as exc: # pragma: no cover + print(f" UNERWARTETER FEHLER ({type(exc).__name__}): {exc}") + return False + + items = (devices or {}).get("devices", []) if isinstance(devices, dict) else [] + if not items: + print(" API-Call erfolgreich, aber KEINE Geraete gemeldet.") + print(" -> Spotify-App auf dem Zielgeraet (Handy/PC) kurz oeffnen, damit es") + print(" bei Spotify Connect als aktiv gemeldet wird, dann nochmal testen.") + else: + print(f" API-Call erfolgreich, {len(items)} Geraet(e) gefunden:") + for d in items: + active = "aktiv" if d.get("is_active") else "inaktiv" + print(f" - {d.get('name')} ({d.get('type')}, {active}, vol={d.get('volume_percent')}%)") + return True + + +def main() -> None: + ok_auth = check_auth() + ok_tools = check_toolsets() + ok_live = check_live_call() if ok_auth else False + + _bar("Zusammenfassung") + print(f" 1) Eingeloggt: {'OK' if ok_auth else 'FEHLT'}") + print(f" 2) Toolset aktiviert: {'OK' if ok_tools else 'FEHLT'}") + print(f" 3) Live-API erreichbar: {'OK' if ok_live else 'FEHLT/UEBERSPRUNGEN'}") + print() + if ok_auth and ok_tools and ok_live: + print("Alles gruen -- Spotify sollte im Chat funktionieren. Kommt trotzdem") + print("'kenn ich nicht'/'nicht verbunden', liegt's am Proxy bzw. System-Prompt") + print("(z.B. Claude nutzt eigene Connectors statt der injizierten Tools),") + print("nicht an Spotify selbst -- dann bitte docker logs hermes-proxy pruefen.") + else: + print("Siehe die '->'-Hinweise oben fuer den jeweils naechsten Schritt.") + + +if __name__ == "__main__": + main()