from fastapi import FastAPI, HTTPException, BackgroundTasks from pydantic import BaseModel from bot import MatrixBot import asyncio from contextlib import asynccontextmanager bot = MatrixBot() @asynccontextmanager async def lifespan(app: FastAPI): # Startup await bot.login() yield # Shutdown await bot.close() app = FastAPI(lifespan=lifespan) class Notification(BaseModel): service_name: str content: str room_id: str | None = None level: str = "info" class JellyfinPayload(BaseModel): notification_type: str item_type: str name: str series_name: str | None = None season: int | None = None episode: int | None = None year: int | None = None overview: str | None = None room_id: str | None = None @app.post("/notify") async def send_notification(notification: Notification, background_tasks: BackgroundTasks): """ Send a notification to a Matrix room. """ try: # Format the message to include the service name # Plain text fallback plain_message = f"[{notification.service_name}]\n{notification.content}" # HTML message html_message = f"[{notification.service_name}]
{notification.content}" # We can send it in background to not block the API response background_tasks.add_task(bot.send_message, plain_message, html_message, notification.room_id) return {"status": "queued"} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @app.post("/jellyfin") async def receive_jellyfin_webhook(payload: JellyfinPayload, background_tasks: BackgroundTasks): """ Receive webhook from Jellyfin and forward to Matrix. """ try: if payload.notification_type != "ItemAdded": return {"status": "ignored", "reason": "Not an ItemAdded event"} # content construction content = "" if payload.item_type == "Movie": content = f"New Movie: {payload.name}" if payload.year: content += f" ({payload.year})" if payload.overview: content += f"\n{payload.overview}" elif payload.item_type == "Episode": show = payload.series_name or "Unknown Series" s = f"S{payload.season:02d}" if payload.season is not None else "S??" e = f"E{payload.episode:02d}" if payload.episode is not None else "E??" content = f"New Episode: {show} - {s}{e} - {payload.name}" else: # Fallback for Series, Season, etc. content = f"New {payload.item_type}: {payload.name}" plain_message = f"[Jellyfin]\n{content}" html_message = f"[Jellyfin]
{content.replace(chr(10), '
')}" background_tasks.add_task(bot.send_message, plain_message, html_message, payload.room_id) return {"status": "queued"} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @app.get("/health") async def health_check(): return {"status": "ok"}