Files
Matirx-Manager/main.py
2025-12-08 12:53:52 +00:00

151 lines
5.3 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from fastapi import FastAPI, HTTPException, BackgroundTasks, Request
from pydantic import BaseModel
from bot import MatrixBot
import asyncio
from contextlib import asynccontextmanager
import logging
import sys
# Configure logging to output to stdout
logging.basicConfig(
stream=sys.stdout,
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger("matrix_manager")
bot = MatrixBot()
@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup
logger.info("Starting up Matrix Manager Service...")
try:
await bot.login()
logger.info("Bot logged in successfully.")
except Exception as e:
logger.error(f"Failed to login bot: {e}")
yield
# Shutdown
logger.info("Shutting down Matrix Manager Service...")
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.
"""
logger.info(f"Received notification request from service: {notification.service_name}")
try:
# Format the message to include the service name
# Plain text fallback
plain_message = f"[{notification.service_name}] {notification.content}"
# HTML message with better formatting
# Map levels to emojis
level_emojis = {
"info": "",
"warning": "⚠️",
"error": "🚨",
"success": ""
}
emoji = level_emojis.get(notification.level, "📢")
# Clean up content
cleaned_content = notification.content.replace("{album_explicit}", "")
# Format the message to include the service name
# Plain text fallback
plain_message = f"[{notification.service_name}] {cleaned_content}"
# HTML message with better formatting
html_message = (
f"<h4>{emoji} {notification.service_name}</h4>"
f"<blockquote>{cleaned_content.replace(chr(10), '<br>')}</blockquote>"
)
# We can send it in background to not block the API response
logger.info(f"Queueing message for room_id: {notification.room_id or 'Default'}")
background_tasks.add_task(bot.send_message, plain_message, html_message, notification.room_id)
return {"status": "queued"}
except Exception as e:
logger.error(f"Error in send_notification: {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.
"""
logger.info(f"Received Jellyfin webhook. Type: {payload.notification_type}, Item: {payload.item_type}")
try:
if payload.notification_type != "ItemAdded":
logger.info("Ignored Jellyfin event (not ItemAdded)")
return {"status": "ignored", "reason": "Not an ItemAdded event"}
# content construction
plain_content = ""
html_content = ""
if payload.item_type == "Movie":
plain_content = f"New Movie: {payload.name}"
html_content = f"<h4>🎬 New Movie Added</h4><b>{payload.name}</b>"
if payload.year:
plain_content += f" ({payload.year})"
html_content += f" ({payload.year})"
if payload.overview:
plain_content += f"\n{payload.overview}"
html_content += f"<blockquote>{payload.overview}</blockquote>"
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??"
plain_content = f"New Episode: {show} - {s}{e} - {payload.name}"
html_content = (
f"<h4>📺 New Episode Added</h4>"
f"<b>{show}</b><br>"
f"{s}{e} - {payload.name}"
)
else:
# Fallback for Series, Season, etc.
plain_content = f"New {payload.item_type}: {payload.name}"
html_content = f"<h4>✨ New {payload.item_type} Added</h4><b>{payload.name}</b>"
plain_message = f"[Jellyfin] {plain_content}"
logger.info(f"Queueing Jellyfin message for room_id: {payload.room_id or 'Default'}")
background_tasks.add_task(bot.send_message, plain_message, html_content, payload.room_id)
return {"status": "queued"}
except Exception as e:
logger.error(f"Error in receive_jellyfin_webhook: {e}")
raise HTTPException(status_code=500, detail=str(e))
@app.get("/health")
async def health_check():
logger.info("Health check requested")
return {"status": "ok"}