added quick connect auth from jellyfin, still needs to have some more cleaning before push to prod

This commit is contained in:
2026-05-25 10:19:50 +02:00
parent 3cd2e4dfbb
commit bf358f7248
16 changed files with 1318 additions and 45 deletions
+26 -1
View File
@@ -47,6 +47,7 @@ class Skill:
prompt_fragment: str = ""
tools: List[Dict[str, Any]] = field(default_factory=list)
execute: Optional[ToolExecutor] = None
requires_auth: List[str] = field(default_factory=list)
# ---------------------------------------------------------------------------
@@ -96,9 +97,15 @@ def get_all_tools(skill_names: list[str]) -> List[Dict[str, Any]]:
async def execute_tool(
skill_names: list[str], tool_name: str, args: dict
skill_names: list[str], tool_name: str, args: dict,
discord_user_id: int | None = None,
) -> ToolResult | None:
"""Find the skill that owns *tool_name* and run its executor.
If *discord_user_id* is provided, also checks whether the owning skill
requires authentication for any services. If auth is missing, returns
a friendly ToolResult.fail(...) telling the user how to log in.
Only logs failures to the console — successful calls are silent.
"""
import logging
@@ -109,6 +116,24 @@ async def execute_tool(
if s and s.execute:
for t in s.tools:
if t.get("function", {}).get("name") == tool_name:
# --- Auth gate ---
if s.requires_auth and discord_user_id is not None:
from core import auth_store
from auth import get_auth_service
missing: list[str] = []
for svc in s.requires_auth:
if not auth_store.is_authenticated(discord_user_id, svc):
missing.append(svc)
if missing:
svc_displays = ", ".join(
(get_auth_service(m) and get_auth_service(m).display_name) or m
for m in missing
)
return ToolResult.fail(
f"You need to log in to {svc_displays} first. "
+ " ".join(f"Send `/login {m}` in a DM to get started." for m in missing)
)
# --- End auth gate ---
try:
result = await s.execute(tool_name, args)
if not result.success:
+10
View File
@@ -23,6 +23,16 @@ When responding:
suggest submitting a ticket if there's a problem.
- Always confirm successful actions and warn about failures.
## Jellyfin & Authentication
You are connected to the user's Jellyfin server. If a user asks you to
"connect to Jellyfin", "link my Jellyfin", or asks about their watch history,
simply call the `watch_history` tool. The system will automatically handle
authentication — if the user isn't linked yet, they'll be guided through
Quick Connect seamlessly. NEVER tell a user you "don't have access to
Jellyfin" or "can't connect" — always try the tool first and let the system
sort it out.
This is the base media assistant persona. Real API capabilities come from the
attached skills (seerr, triage, etc.).""",
)
+80
View File
@@ -0,0 +1,80 @@
"""
Watch History skill — fetch the user's Jellyfin watch history.
Currently a placeholder — returns a "coming soon" message.
The auth gate (`requires_auth=["jellyfin"]`) is already active:
users who haven't linked Jellyfin will be prompted to /login first.
"""
from __future__ import annotations
from skills import Skill, register, ToolResult
# ---------------------------------------------------------------------------
# Tool definitions
# ---------------------------------------------------------------------------
TOOLS = [
{
"type": "function",
"function": {
"name": "watch_history",
"description": (
"Get the user's recent Jellyfin watch history — movies and TV "
"episodes they have watched, sorted by most recent. "
"Call this when a user asks about their watching activity."
),
"parameters": {
"type": "object",
"properties": {
"limit": {
"type": "integer",
"description": "How many items to return (default 10, max 20)",
}
},
},
},
}
]
# ---------------------------------------------------------------------------
# Executor (placeholder)
# ---------------------------------------------------------------------------
async def _execute(tool_name: str, args: dict) -> ToolResult:
if tool_name == "watch_history":
return ToolResult.ok(
"👷 **Watch History — Coming Soon!**\n\n"
"This feature is currently being built. Soon you'll be able to "
"see your recently watched movies and TV episodes right here.\n\n"
"In the meantime, you can check your watch history directly in Jellyfin."
)
return ToolResult.fail(f"Unknown tool: {tool_name}")
# ---------------------------------------------------------------------------
# Skill registration
# ---------------------------------------------------------------------------
watch_history_skill = Skill(
name="watch_history",
description="User's Jellyfin watch history (coming soon)",
requires_auth=["jellyfin"],
prompt_fragment="""## Watch History
You can fetch the user's Jellyfin watch history with the `watch_history` tool.
Call it when users ask things like:
- "what have I watched?"
- "show my watch history"
- "what did I watch recently?"
- "what was the last movie I saw?"
- "what TV shows have I been watching?"
The tool is currently a **placeholder** — it returns a "coming soon" message.
Tell the user this feature is being worked on and will be available soon.""",
tools=TOOLS,
execute=_execute,
)
register(watch_history_skill)