Files
hermes-agent/scripts/spotify_test_connection.py
T
ARIA d05ab23c1b 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.
2026-07-20 00:21:20 +00:00

156 lines
6.3 KiB
Python

#!/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()