""" onmyoji_gacha 插件的 Web API 接口 使用 NoneBot 内置的 FastAPI 适配器提供管理员后台接口 """ import os from typing import Dict, List, Any, Optional from fastapi import APIRouter, Depends, HTTPException, Header, Request from fastapi.responses import HTMLResponse, JSONResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from pydantic import BaseModel from nonebot import get_driver, logger from .config import Config from .gacha import GachaSystem # 创建配置实例 config = Config() gacha_system = GachaSystem() # 创建 FastAPI 路由 router = APIRouter(prefix="/onmyoji_gacha", tags=["onmyoji_gacha"]) # 设置模板目录 templates = Jinja2Templates(directory="danding_bot/plugins/onmyoji_gacha/templates") # 依赖:验证管理员权限 async def verify_admin_token(authorization: Optional[str] = Header(None)): """验证管理员权限""" if not authorization: raise HTTPException(status_code=401, detail="缺少认证令牌") # 支持 "Bearer xxx" 和直接 "xxx" 两种格式 if authorization.startswith("Bearer "): token = authorization[7:] else: token = authorization if token != config.WEB_ADMIN_TOKEN: logger.warning("管理员令牌验证失败") raise HTTPException(status_code=403, detail="无效的认证令牌") return True # API 响应模型 class DailyStatsResponse(BaseModel): success: bool date: str stats: Dict[str, Any] class UserStatsResponse(BaseModel): success: bool user_id: str total_draws: int R_count: int SR_count: int SSR_count: int SP_count: int recent_draws: List[Dict[str, str]] class RankListResponse(BaseModel): success: bool data: List[Dict[str, Any]] class AchievementResponse(BaseModel): success: bool user_id: str achievements: Dict[str, Any] progress: Dict[str, Any] class DailyDetailedRecordsResponse(BaseModel): success: bool date: str records: List[Dict[str, Any]] total_count: int # 管理后台页面 @router.get("/admin", response_class=HTMLResponse) async def admin_page(request: Request): """管理后台页面""" return templates.TemplateResponse("admin.html", {"request": request}) # API 端点 @router.get("/api/stats/daily", response_model=DailyStatsResponse, dependencies=[Depends(verify_admin_token)]) async def get_daily_stats(): """获取今日抽卡统计""" result = await gacha_system.get_daily_stats() if not result["success"]: return result return { "success": True, "date": result["date"], "stats": result["stats"] } @router.get("/api/stats/user/{user_id}", response_model=UserStatsResponse, dependencies=[Depends(verify_admin_token)]) async def get_user_stats(user_id: str): """获取用户抽卡统计""" result = await gacha_system.get_user_stats(user_id) if not result["success"]: return { "success": False, "user_id": user_id, "total_draws": 0, "R_count": 0, "SR_count": 0, "SSR_count": 0, "SP_count": 0, "recent_draws": [] } return { "success": True, "user_id": user_id, "total_draws": result["total_draws"], "R_count": result["R_count"], "SR_count": result["SR_count"], "SSR_count": result["SSR_count"], "SP_count": result["SP_count"], "recent_draws": result["recent_draws"] } @router.get("/api/stats/rank", response_model=RankListResponse, dependencies=[Depends(verify_admin_token)]) async def get_rank_list(): """获取抽卡排行榜""" rank_data = await gacha_system.get_rank_list() # 转换数据格式 formatted_data = [] for user_id, stats in rank_data: formatted_data.append({ "user_id": user_id, "total_draws": stats["total_draws"], "R_count": stats["R_count"], "SR_count": stats["SR_count"], "SSR_count": stats["SSR_count"], "SP_count": stats["SP_count"], "ssr_sp_total": stats["SSR_count"] + stats["SP_count"] }) return { "success": True, "data": formatted_data } @router.get("/api/achievements/{user_id}", response_model=AchievementResponse, dependencies=[Depends(verify_admin_token)]) async def get_user_achievements(user_id: str): """获取用户成就信息""" result = await gacha_system.get_user_achievements(user_id) if not result["success"]: return { "success": False, "user_id": user_id, "achievements": {}, "progress": {} } return { "success": True, "user_id": user_id, "achievements": result["achievements"], "progress": result["progress"] } @router.get("/api/records/daily", response_model=DailyDetailedRecordsResponse, dependencies=[Depends(verify_admin_token)]) async def get_daily_detailed_records(date: Optional[str] = None): """获取每日详细抽卡记录""" result = await gacha_system.get_daily_detailed_records(date) if not result["success"]: return { "success": False, "date": date or gacha_system.data_manager.get_today_date(), "records": [], "total_count": 0 } return { "success": True, "date": result["date"], "records": result["records"], "total_count": result["total_count"] } # 注册路由到 NoneBot 的 FastAPI 应用 # 将在插件加载时由 __init__.py 调用 def register_web_routes(): """注册 Web 路由到 NoneBot 的 FastAPI 应用""" try: from nonebot import get_driver driver = get_driver() # 获取 FastAPI 应用实例 app = driver.server_app # 注册路由 app.include_router(router) logger.info("✅ onmyoji_gacha Web API 路由注册成功") return True except Exception as e: logger.error(f"❌ 注册 Web 路由时出错: {e}") return False