feat(halo): add song <URL> command to convert via song.link
Resolves the URL through the Odesli public API (api.song.link) and replies with the canonical song.link page plus per-platform deep links (Spotify, Apple Music, YouTube/YT Music, Tidal, Deezer, Amazon Music, SoundCloud). Country is pinned to DE.
This commit is contained in:
parent
ac70c57c15
commit
5b44e037a1
1 changed files with 68 additions and 0 deletions
|
|
@ -30,6 +30,19 @@ TIMEOUT = int(os.environ.get("TIMEOUT", "120"))
|
|||
SYSTEM_PROMPT = os.environ.get("SYSTEM_PROMPT", "")
|
||||
BOT_NAME = os.environ.get("BOT_NAME", "Halo")
|
||||
|
||||
SONGLINK_API = "https://api.song.link/v1-alpha.1/links"
|
||||
SONGLINK_COUNTRY = "DE"
|
||||
SONGLINK_PLATFORMS = [
|
||||
("spotify", "Spotify"),
|
||||
("appleMusic", "Apple Music"),
|
||||
("youtube", "YouTube"),
|
||||
("youtubeMusic", "YouTube Music"),
|
||||
("tidal", "Tidal"),
|
||||
("deezer", "Deezer"),
|
||||
("amazonMusic", "Amazon Music"),
|
||||
("soundcloud", "SoundCloud"),
|
||||
]
|
||||
|
||||
|
||||
def get_bot_secret() -> str:
|
||||
cred_path = os.environ.get("CREDENTIALS_DIRECTORY", "")
|
||||
|
|
@ -159,6 +172,52 @@ async def call_model(messages: list[dict]) -> str:
|
|||
return f"❌ Fehler: {str(e)}"
|
||||
|
||||
|
||||
async def resolve_song_link(url: str) -> str:
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=30) as client:
|
||||
resp = await client.get(
|
||||
SONGLINK_API,
|
||||
params={"url": url, "userCountry": SONGLINK_COUNTRY},
|
||||
)
|
||||
except httpx.TimeoutException:
|
||||
return "⏱️ Timeout bei der Anfrage an song.link."
|
||||
except Exception as e:
|
||||
log.exception("song.link request failed")
|
||||
return f"❌ Fehler bei song.link: {e}"
|
||||
|
||||
if resp.status_code != 200:
|
||||
log.error(f"song.link error: {resp.status_code} {resp.text[:300]}")
|
||||
return f"❌ song.link Fehler: HTTP {resp.status_code}"
|
||||
|
||||
data = resp.json()
|
||||
page_url = data.get("pageUrl")
|
||||
if not page_url:
|
||||
return "❌ song.link hat keine Seite zurückgegeben."
|
||||
|
||||
entity_id = data.get("entityUniqueId")
|
||||
entity = data.get("entitiesByUniqueId", {}).get(entity_id, {}) if entity_id else {}
|
||||
title = entity.get("title")
|
||||
artist = entity.get("artistName")
|
||||
|
||||
lines: list[str] = []
|
||||
if title:
|
||||
header = f"**{title}**" + (f" – {artist}" if artist else "")
|
||||
lines.append(header)
|
||||
lines.append(page_url)
|
||||
|
||||
links = data.get("linksByPlatform", {})
|
||||
platform_lines = []
|
||||
for key, label in SONGLINK_PLATFORMS:
|
||||
platform_url = links.get(key, {}).get("url")
|
||||
if platform_url:
|
||||
platform_lines.append(f"- [{label}]({platform_url})")
|
||||
if platform_lines:
|
||||
lines.append("")
|
||||
lines.extend(platform_lines)
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
async def send_reply(conversation_token: str, message: str, reply_to: int = None):
|
||||
if not NEXTCLOUD_URL:
|
||||
log.error("NEXTCLOUD_URL not configured")
|
||||
|
|
@ -281,12 +340,21 @@ Schreib mir einfach eine Nachricht und ich antworte dir.
|
|||
|
||||
**Befehle:**
|
||||
• `hilfe` oder `?` – Diese Hilfe anzeigen
|
||||
• `song <URL>` – Streaming-Link über song.link konvertieren
|
||||
|
||||
Modell: `{MODEL_NAME}` @ `{MODEL_BASE_URL}`
|
||||
Der Bot merkt sich die letzten Nachrichten pro Raum (bis zum Neustart)."""
|
||||
await send_reply(conversation_token, help_text, reply_to=message_id)
|
||||
return JSONResponse({"status": "ok", "action": "help"})
|
||||
|
||||
song_match = re.match(r'^\s*song\s+(\S+)', message_text, re.IGNORECASE)
|
||||
if song_match:
|
||||
song_url = song_match.group(1).strip()
|
||||
log.info(f"Resolving song.link for {song_url}")
|
||||
reply = await resolve_song_link(song_url)
|
||||
await send_reply(conversation_token, reply, reply_to=message_id)
|
||||
return JSONResponse({"status": "ok", "action": "song"})
|
||||
|
||||
messages = build_messages(conversation_token, message_text, actor_id)
|
||||
response = await call_model(messages)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue