feat: add proper HTTP status codes to all API error responses
- 217 error returns across 18 route files + api.py now use JSONResponse with appropriate HTTP status codes instead of returning HTTP 200 - Status code distribution: 500 (121), 400 (39), 503 (28), 404 (24), 409 (3), 502 (2) - Fixed language.py tuple-return bug (was serializing as JSON array) - Fixed bare except clauses in bipolar_mode.py and voice.py - Body-level error schemas preserved (status/error + success/error patterns) so web UI continues working without changes - chat.py (SSE) unchanged: errors sent within stream protocol - All 170 tests pass
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
# To revert: cp api_monolith_backup.py api.py
|
||||
|
||||
from fastapi import FastAPI, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from utils.logger import get_logger
|
||||
from utils.log_config import load_config as load_log_config
|
||||
@@ -23,7 +24,7 @@ app = FastAPI()
|
||||
async def global_exception_handler(request: Request, exc: Exception):
|
||||
"""Catch all unhandled exceptions and log them properly."""
|
||||
logger.error(f"Unhandled exception on {request.method} {request.url.path}: {exc}", exc_info=True)
|
||||
return {"success": False, "error": "Internal server error"}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": "Internal server error"})
|
||||
|
||||
# ========== Logging Middleware ==========
|
||||
@app.middleware("http")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Autonomous action routes: V1, V2, per-server autonomous."""
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import JSONResponse
|
||||
import globals
|
||||
from server_manager import server_manager
|
||||
from routes.models import CustomPromptRequest
|
||||
@@ -25,7 +26,7 @@ async def trigger_autonomous_general(guild_id: int = None):
|
||||
globals.client.loop.create_task(miku_say_something_general())
|
||||
return {"status": "ok", "message": "Autonomous general message queued for all servers"}
|
||||
else:
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
|
||||
@router.post("/autonomous/engage")
|
||||
@@ -65,7 +66,7 @@ async def trigger_autonomous_engage_user(
|
||||
|
||||
return {"status": "ok", "message": " ".join(msg_parts)}
|
||||
else:
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
|
||||
@router.post("/autonomous/tweet")
|
||||
@@ -86,7 +87,7 @@ async def trigger_autonomous_tweet(guild_id: int = None, tweet_url: str = None):
|
||||
msg += f" with URL {tweet_url}"
|
||||
return {"status": "ok", "message": msg}
|
||||
else:
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
|
||||
@router.post("/autonomous/custom")
|
||||
@@ -101,7 +102,7 @@ async def custom_autonomous_message(req: CustomPromptRequest, guild_id: int = No
|
||||
globals.client.loop.create_task(handle_custom_prompt(req.prompt))
|
||||
return {"status": "ok", "message": "Custom autonomous message queued for all servers"}
|
||||
else:
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
|
||||
@router.post("/autonomous/reaction")
|
||||
@@ -116,7 +117,7 @@ async def trigger_autonomous_reaction(guild_id: int = None):
|
||||
globals.client.loop.create_task(miku_autonomous_reaction(force=True))
|
||||
return {"status": "ok", "message": "Autonomous reaction queued for all servers"}
|
||||
else:
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
|
||||
@router.post("/autonomous/join-conversation")
|
||||
@@ -135,7 +136,7 @@ async def trigger_detect_and_join_conversation(guild_id: int = None):
|
||||
return {"status": "ok", "message": "Detect and join conversation queued for all servers"}
|
||||
else:
|
||||
logger.error(f"Bot not ready: client={globals.client}, loop={globals.client.loop if globals.client else None}")
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
|
||||
# ========== Per-Server Autonomous ==========
|
||||
@@ -148,7 +149,7 @@ async def trigger_autonomous_general_for_server(guild_id: int):
|
||||
await miku_say_something_general_for_server(guild_id)
|
||||
return {"status": "ok", "message": f"Autonomous general message triggered for server {guild_id}"}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Failed to trigger autonomous message: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to trigger autonomous message: {e}"})
|
||||
|
||||
|
||||
@router.post("/servers/{guild_id}/autonomous/engage")
|
||||
@@ -175,7 +176,7 @@ async def trigger_autonomous_engage_for_server(
|
||||
|
||||
return {"status": "ok", "message": " ".join(msg_parts)}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Failed to trigger user engagement: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to trigger user engagement: {e}"})
|
||||
|
||||
|
||||
@router.post("/servers/{guild_id}/autonomous/custom")
|
||||
@@ -187,9 +188,9 @@ async def custom_autonomous_message_for_server(guild_id: int, req: CustomPromptR
|
||||
if success:
|
||||
return {"status": "ok", "message": f"Custom autonomous message sent to server {guild_id}"}
|
||||
else:
|
||||
return {"status": "error", "message": f"Failed to send custom message to server {guild_id}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to send custom message to server {guild_id}"})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Error: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Error: {e}"})
|
||||
|
||||
|
||||
@router.post("/servers/{guild_id}/autonomous/tweet")
|
||||
@@ -200,7 +201,7 @@ async def trigger_autonomous_tweet_for_server(guild_id: int):
|
||||
await share_miku_tweet_for_server(guild_id)
|
||||
return {"status": "ok", "message": f"Autonomous tweet sharing triggered for server {guild_id}"}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Failed to trigger tweet sharing: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to trigger tweet sharing: {e}"})
|
||||
|
||||
|
||||
# ========== Autonomous V2 ==========
|
||||
@@ -213,7 +214,7 @@ async def get_v2_stats(guild_id: int):
|
||||
stats = get_v2_stats_for_server(guild_id)
|
||||
return {"status": "ok", "guild_id": guild_id, "stats": stats}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.get("/autonomous/v2/check/{guild_id}")
|
||||
@@ -223,16 +224,16 @@ async def manual_v2_check(guild_id: int):
|
||||
from utils.autonomous_v2_integration import manual_trigger_v2_check
|
||||
|
||||
if not globals.client:
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
result = await manual_trigger_v2_check(guild_id, globals.client)
|
||||
|
||||
if isinstance(result, str):
|
||||
return {"status": "error", "message": result}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": result})
|
||||
|
||||
return {"status": "ok", "guild_id": guild_id, "analysis": result}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.get("/autonomous/v2/status")
|
||||
@@ -258,4 +259,4 @@ async def get_v2_status():
|
||||
|
||||
return {"status": "ok", "servers": status}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import asyncio
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import JSONResponse
|
||||
import globals
|
||||
from routes.models import BipolarTriggerRequest
|
||||
from utils.logger import get_logger
|
||||
@@ -106,23 +107,23 @@ def trigger_argument(data: BipolarTriggerRequest):
|
||||
try:
|
||||
channel_id = int(data.channel_id)
|
||||
except ValueError:
|
||||
return {"status": "error", "message": "Invalid channel ID format"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "Invalid channel ID format"})
|
||||
|
||||
message_id = None
|
||||
if data.message_id:
|
||||
try:
|
||||
message_id = int(data.message_id)
|
||||
except ValueError:
|
||||
return {"status": "error", "message": "Invalid message ID format"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "Invalid message ID format"})
|
||||
|
||||
if not is_bipolar_mode():
|
||||
return {"status": "error", "message": "Bipolar mode is not enabled"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "Bipolar mode is not enabled"})
|
||||
|
||||
if is_argument_in_progress(channel_id):
|
||||
return {"status": "error", "message": "An argument is already in progress in this channel"}
|
||||
return JSONResponse(status_code=409, content={"status": "error", "message": "An argument is already in progress in this channel"})
|
||||
|
||||
if not globals.client or not globals.client.loop or not globals.client.loop.is_running():
|
||||
return {"status": "error", "message": "Discord client not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Discord client not ready"})
|
||||
|
||||
# If message_id is provided, use the message-based trigger
|
||||
if message_id:
|
||||
@@ -145,7 +146,7 @@ def trigger_argument(data: BipolarTriggerRequest):
|
||||
# Otherwise, find the channel and trigger normally
|
||||
channel = globals.client.get_channel(channel_id)
|
||||
if not channel:
|
||||
return {"status": "error", "message": f"Channel {channel_id} not found"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": f"Channel {channel_id} not found"})
|
||||
|
||||
# Trigger the argument
|
||||
globals.client.loop.create_task(force_trigger_argument(channel, globals.client, data.context))
|
||||
@@ -168,19 +169,19 @@ def trigger_dialogue(data: dict):
|
||||
|
||||
message_id_str = data.get("message_id")
|
||||
if not message_id_str:
|
||||
return {"status": "error", "message": "Message ID is required"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "Message ID is required"})
|
||||
|
||||
# Parse message ID
|
||||
try:
|
||||
message_id = int(message_id_str)
|
||||
except ValueError:
|
||||
return {"status": "error", "message": "Invalid message ID format"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "Invalid message ID format"})
|
||||
|
||||
if not is_bipolar_mode():
|
||||
return {"status": "error", "message": "Bipolar mode is not enabled"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "Bipolar mode is not enabled"})
|
||||
|
||||
if not globals.client or not globals.client.loop or not globals.client.loop.is_running():
|
||||
return {"status": "error", "message": "Discord client not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Discord client not ready"})
|
||||
|
||||
async def trigger_dialogue_task():
|
||||
try:
|
||||
@@ -191,7 +192,7 @@ def trigger_dialogue(data: dict):
|
||||
try:
|
||||
message = await channel.fetch_message(message_id)
|
||||
break
|
||||
except:
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
if not message:
|
||||
@@ -272,7 +273,7 @@ def cleanup_bipolar_webhooks():
|
||||
from utils.bipolar_mode import cleanup_webhooks
|
||||
|
||||
if not globals.client or not globals.client.loop or not globals.client.loop.is_running():
|
||||
return {"status": "error", "message": "Discord client not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Discord client not ready"})
|
||||
|
||||
globals.client.loop.create_task(cleanup_webhooks(globals.client))
|
||||
return {"status": "ok", "message": "Webhook cleanup started"}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Core bot action routes: conversation reset, sleep, wake, bedtime."""
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import JSONResponse
|
||||
import globals
|
||||
from commands.actions import (
|
||||
force_sleep,
|
||||
@@ -49,4 +50,4 @@ async def bedtime_endpoint(guild_id: int = None):
|
||||
globals.client.loop.create_task(send_bedtime_now())
|
||||
return {"status": "ok", "message": "Bedtime reminder queued for all servers"}
|
||||
else:
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Configuration management routes: get/set/reset/validate config."""
|
||||
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
import globals
|
||||
from utils.logger import get_logger
|
||||
|
||||
@@ -24,7 +25,7 @@ async def get_full_config():
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get config: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
|
||||
@router.get("/config/static")
|
||||
@@ -41,7 +42,7 @@ async def get_static_config():
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get static config: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
|
||||
@router.get("/config/runtime")
|
||||
@@ -59,7 +60,7 @@ async def get_runtime_config():
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get runtime config: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
|
||||
@router.post("/config/set")
|
||||
@@ -80,7 +81,7 @@ async def set_config_value(request: Request):
|
||||
persist = data.get("persist", True)
|
||||
|
||||
if not key_path:
|
||||
return {"success": False, "error": "key_path is required"}
|
||||
return JSONResponse(status_code=400, content={"success": False, "error": "key_path is required"})
|
||||
|
||||
from config_manager import config_manager
|
||||
config_manager.set(key_path, value, persist=persist)
|
||||
@@ -114,7 +115,7 @@ async def set_config_value(request: Request):
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to set config: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
|
||||
@router.post("/config/reset")
|
||||
@@ -143,7 +144,7 @@ async def reset_config(request: Request):
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to reset config: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
|
||||
@router.post("/config/validate")
|
||||
@@ -163,7 +164,7 @@ async def validate_config_endpoint():
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to validate config: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
|
||||
@router.get("/config/state")
|
||||
@@ -180,4 +181,4 @@ async def get_config_state():
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get config state: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Core routes: index, logs, prompts, status, conversation."""
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi.responses import FileResponse, JSONResponse
|
||||
import globals
|
||||
from server_manager import server_manager
|
||||
from utils.conversation_history import conversation_history
|
||||
@@ -26,7 +26,7 @@ def get_logs():
|
||||
last_100 = lines[-100:] if len(lines) >= 100 else lines
|
||||
return "".join(last_100)
|
||||
except Exception as e:
|
||||
return f"Error reading log file: {e}"
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Error reading log file: {e}"})
|
||||
|
||||
|
||||
@router.get("/prompt")
|
||||
|
||||
@@ -5,6 +5,7 @@ import os
|
||||
import json
|
||||
from typing import List
|
||||
from fastapi import APIRouter, UploadFile, File, Form
|
||||
from fastapi.responses import JSONResponse
|
||||
import discord
|
||||
import globals
|
||||
from routes.models import CustomPromptRequest
|
||||
@@ -25,7 +26,7 @@ async def send_custom_prompt_dm(user_id: str, req: CustomPromptRequest):
|
||||
user_id_int = int(user_id)
|
||||
user = globals.client.get_user(user_id_int)
|
||||
if not user:
|
||||
return {"status": "error", "message": f"User {user_id} not found"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": f"User {user_id} not found"})
|
||||
|
||||
# Use the LLM query function for DM context
|
||||
from utils.llm import query_llama
|
||||
@@ -47,9 +48,9 @@ async def send_custom_prompt_dm(user_id: str, req: CustomPromptRequest):
|
||||
return {"status": "ok", "message": f"Custom DM prompt queued for user {user_id}"}
|
||||
|
||||
except ValueError:
|
||||
return {"status": "error", "message": "Invalid user ID format"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "Invalid user ID format"})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Error: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Error: {e}"})
|
||||
|
||||
|
||||
@router.post("/dm/{user_id}/manual")
|
||||
@@ -65,7 +66,7 @@ async def send_manual_message_dm(
|
||||
user_id_int = int(user_id)
|
||||
user = globals.client.get_user(user_id_int)
|
||||
if not user:
|
||||
return {"status": "error", "message": f"User {user_id} not found"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": f"User {user_id} not found"})
|
||||
|
||||
# Read file content immediately before the request closes
|
||||
file_data = []
|
||||
@@ -78,7 +79,7 @@ async def send_manual_message_dm(
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to read file {file.filename}: {e}")
|
||||
return {"status": "error", "message": f"Failed to read file {file.filename}: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to read file {file.filename}: {e}"})
|
||||
|
||||
async def send_dm_message_and_files():
|
||||
try:
|
||||
@@ -120,9 +121,9 @@ async def send_manual_message_dm(
|
||||
return {"status": "ok", "message": f"Manual DM message queued for user {user_id}"}
|
||||
|
||||
except ValueError:
|
||||
return {"status": "error", "message": "Invalid user ID format"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "Invalid user ID format"})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Error: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Error: {e}"})
|
||||
|
||||
|
||||
# ========== DM Logging Endpoints ==========
|
||||
@@ -134,7 +135,7 @@ def get_dm_users():
|
||||
users = dm_logger.get_all_dm_users()
|
||||
return {"status": "ok", "users": users}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Failed to get DM users: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to get DM users: {e}"})
|
||||
|
||||
|
||||
@router.get("/dms/users/{user_id}")
|
||||
@@ -146,9 +147,9 @@ def get_dm_user_conversation(user_id: str):
|
||||
summary = dm_logger.get_user_conversation_summary(user_id_int)
|
||||
return {"status": "ok", "summary": summary}
|
||||
except ValueError:
|
||||
return {"status": "error", "message": f"Invalid user ID format: {user_id}"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": f"Invalid user ID format: {user_id}"})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Failed to get user conversation: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to get user conversation: {e}"})
|
||||
|
||||
|
||||
@router.get("/dms/users/{user_id}/conversations")
|
||||
@@ -180,10 +181,10 @@ def get_dm_conversations(user_id: str, limit: int = 50):
|
||||
|
||||
return {"status": "ok", "conversations": conversations}
|
||||
except ValueError:
|
||||
return {"status": "error", "message": f"Invalid user ID format: {user_id}"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": f"Invalid user ID format: {user_id}"})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get conversations for user {user_id}: {e}")
|
||||
return {"status": "error", "message": f"Failed to get conversations: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to get conversations: {e}"})
|
||||
|
||||
|
||||
@router.get("/dms/users/{user_id}/search")
|
||||
@@ -195,9 +196,9 @@ def search_dm_conversations(user_id: str, query: str, limit: int = 10):
|
||||
results = dm_logger.search_user_conversations(user_id_int, query, limit)
|
||||
return {"status": "ok", "results": results}
|
||||
except ValueError:
|
||||
return {"status": "error", "message": f"Invalid user ID format: {user_id}"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": f"Invalid user ID format: {user_id}"})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Failed to search conversations: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to search conversations: {e}"})
|
||||
|
||||
|
||||
@router.get("/dms/users/{user_id}/export")
|
||||
@@ -209,9 +210,9 @@ def export_dm_conversation(user_id: str, format: str = "json"):
|
||||
export_path = dm_logger.export_user_conversation(user_id_int, format)
|
||||
return {"status": "ok", "export_path": export_path, "format": format}
|
||||
except ValueError:
|
||||
return {"status": "error", "message": f"Invalid user ID format: {user_id}"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": f"Invalid user ID format: {user_id}"})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Failed to export conversation: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to export conversation: {e}"})
|
||||
|
||||
|
||||
@router.delete("/dms/users/{user_id}")
|
||||
@@ -225,11 +226,11 @@ def delete_dm_user_logs(user_id: str):
|
||||
os.remove(log_file)
|
||||
return {"status": "ok", "message": f"Deleted DM logs for user {user_id}"}
|
||||
else:
|
||||
return {"status": "error", "message": f"No DM logs found for user {user_id}"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": f"No DM logs found for user {user_id}"})
|
||||
except ValueError:
|
||||
return {"status": "error", "message": f"Invalid user ID format: {user_id}"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": f"Invalid user ID format: {user_id}"})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Failed to delete DM logs: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to delete DM logs: {e}"})
|
||||
|
||||
|
||||
# ========== User Blocking & DM Management ==========
|
||||
@@ -242,7 +243,7 @@ def get_blocked_users():
|
||||
return {"status": "ok", "blocked_users": blocked_users}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get blocked users: {e}")
|
||||
return {"status": "error", "message": f"Failed to get blocked users: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to get blocked users: {e}"})
|
||||
|
||||
|
||||
@router.post("/dms/users/{user_id}/block")
|
||||
@@ -261,13 +262,13 @@ def block_user(user_id: str):
|
||||
logger.info(f"User {user_id} ({username}) blocked")
|
||||
return {"status": "ok", "message": f"User {username} has been blocked"}
|
||||
else:
|
||||
return {"status": "error", "message": f"User {username} is already blocked"}
|
||||
return JSONResponse(status_code=409, content={"status": "error", "message": f"User {username} is already blocked"})
|
||||
|
||||
except ValueError:
|
||||
return {"status": "error", "message": f"Invalid user ID format: {user_id}"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": f"Invalid user ID format: {user_id}"})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to block user {user_id}: {e}")
|
||||
return {"status": "error", "message": f"Failed to block user: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to block user: {e}"})
|
||||
|
||||
|
||||
@router.post("/dms/users/{user_id}/unblock")
|
||||
@@ -281,13 +282,13 @@ def unblock_user(user_id: str):
|
||||
logger.info(f"User {user_id} unblocked")
|
||||
return {"status": "ok", "message": f"User has been unblocked"}
|
||||
else:
|
||||
return {"status": "error", "message": f"User is not blocked"}
|
||||
return JSONResponse(status_code=409, content={"status": "error", "message": f"User is not blocked"})
|
||||
|
||||
except ValueError:
|
||||
return {"status": "error", "message": f"Invalid user ID format: {user_id}"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": f"Invalid user ID format: {user_id}"})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to unblock user {user_id}: {e}")
|
||||
return {"status": "error", "message": f"Failed to unblock user: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to unblock user: {e}"})
|
||||
|
||||
|
||||
@router.post("/dms/users/{user_id}/conversations/{conversation_id}/delete")
|
||||
@@ -308,10 +309,10 @@ def delete_conversation(user_id: str, conversation_id: str):
|
||||
return {"status": "ok", "message": "Message deletion queued (will delete from both Discord and logs)"}
|
||||
|
||||
except ValueError:
|
||||
return {"status": "error", "message": f"Invalid user ID format: {user_id}"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": f"Invalid user ID format: {user_id}"})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to queue conversation deletion {conversation_id}: {e}")
|
||||
return {"status": "error", "message": f"Failed to delete conversation: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to delete conversation: {e}"})
|
||||
|
||||
|
||||
@router.post("/dms/users/{user_id}/conversations/delete-all")
|
||||
@@ -331,10 +332,10 @@ def delete_all_conversations(user_id: str):
|
||||
return {"status": "ok", "message": "Bulk deletion queued (will delete all Miku messages from Discord and clear logs)"}
|
||||
|
||||
except ValueError:
|
||||
return {"status": "error", "message": f"Invalid user ID format: {user_id}"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": f"Invalid user ID format: {user_id}"})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to queue bulk conversation deletion for user {user_id}: {e}")
|
||||
return {"status": "error", "message": f"Failed to delete conversations: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to delete conversations: {e}"})
|
||||
|
||||
|
||||
@router.post("/dms/users/{user_id}/delete-completely")
|
||||
@@ -348,13 +349,13 @@ def delete_user_completely(user_id: str):
|
||||
logger.info(f"Completely deleted user {user_id}")
|
||||
return {"status": "ok", "message": "User data deleted completely"}
|
||||
else:
|
||||
return {"status": "error", "message": "No user data found"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": "No user data found"})
|
||||
|
||||
except ValueError:
|
||||
return {"status": "error", "message": f"Invalid user ID format: {user_id}"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": f"Invalid user ID format: {user_id}"})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to completely delete user {user_id}: {e}")
|
||||
return {"status": "error", "message": f"Failed to delete user: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to delete user: {e}"})
|
||||
|
||||
|
||||
# ========== DM Interaction Analysis Endpoints ==========
|
||||
@@ -366,7 +367,7 @@ def run_dm_analysis():
|
||||
from utils.dm_interaction_analyzer import dm_analyzer
|
||||
|
||||
if dm_analyzer is None:
|
||||
return {"status": "error", "message": "DM Analyzer not initialized. Set OWNER_USER_ID environment variable."}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "DM Analyzer not initialized. Set OWNER_USER_ID environment variable."})
|
||||
|
||||
# Schedule analysis in Discord's event loop
|
||||
async def run_analysis():
|
||||
@@ -377,7 +378,7 @@ def run_dm_analysis():
|
||||
return {"status": "ok", "message": "DM analysis started"}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to run DM analysis: {e}")
|
||||
return {"status": "error", "message": f"Failed to run DM analysis: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to run DM analysis: {e}"})
|
||||
|
||||
|
||||
@router.post("/dms/users/{user_id}/analyze")
|
||||
@@ -387,7 +388,7 @@ def analyze_user_interaction(user_id: str):
|
||||
from utils.dm_interaction_analyzer import dm_analyzer
|
||||
|
||||
if dm_analyzer is None:
|
||||
return {"status": "error", "message": "DM Analyzer not initialized. Set OWNER_USER_ID environment variable."}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "DM Analyzer not initialized. Set OWNER_USER_ID environment variable."})
|
||||
|
||||
user_id_int = int(user_id)
|
||||
|
||||
@@ -401,10 +402,10 @@ def analyze_user_interaction(user_id: str):
|
||||
return {"status": "ok", "message": f"Analysis started for user {user_id}", "reported": True}
|
||||
|
||||
except ValueError:
|
||||
return {"status": "error", "message": f"Invalid user ID format: {user_id}"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": f"Invalid user ID format: {user_id}"})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to analyze user {user_id}: {e}")
|
||||
return {"status": "error", "message": f"Failed to analyze user: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to analyze user: {e}"})
|
||||
|
||||
|
||||
@router.get("/dms/analysis/reports")
|
||||
@@ -432,7 +433,7 @@ def get_analysis_reports(limit: int = 20):
|
||||
return {"status": "ok", "reports": reports}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get reports: {e}")
|
||||
return {"status": "error", "message": f"Failed to get reports: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to get reports: {e}"})
|
||||
|
||||
|
||||
@router.get("/dms/analysis/reports/{user_id}")
|
||||
@@ -461,7 +462,7 @@ def get_user_reports(user_id: str, limit: int = 10):
|
||||
|
||||
return {"status": "ok", "reports": reports}
|
||||
except ValueError:
|
||||
return {"status": "error", "message": f"Invalid user ID format: {user_id}"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": f"Invalid user ID format: {user_id}"})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get user reports: {e}")
|
||||
return {"status": "error", "message": f"Failed to get user reports: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to get user reports: {e}"})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Evil mode routes."""
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import JSONResponse
|
||||
import globals
|
||||
from routes.models import EvilMoodSetRequest
|
||||
from utils.logger import get_logger
|
||||
@@ -43,7 +44,7 @@ def enable_evil_mode():
|
||||
globals.client.loop.create_task(apply_evil_mode_changes(globals.client))
|
||||
return {"status": "ok", "message": "Evil mode enabled", "evil_mode": True}
|
||||
else:
|
||||
return {"status": "error", "message": "Discord client not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Discord client not ready"})
|
||||
|
||||
|
||||
@router.post("/evil-mode/disable")
|
||||
@@ -58,7 +59,7 @@ def disable_evil_mode():
|
||||
globals.client.loop.create_task(revert_evil_mode_changes(globals.client))
|
||||
return {"status": "ok", "message": "Evil mode disabled", "evil_mode": False}
|
||||
else:
|
||||
return {"status": "error", "message": "Discord client not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Discord client not ready"})
|
||||
|
||||
|
||||
@router.post("/evil-mode/toggle")
|
||||
@@ -67,7 +68,7 @@ def toggle_evil_mode():
|
||||
from utils.evil_mode import apply_evil_mode_changes, revert_evil_mode_changes
|
||||
|
||||
if not globals.client or not globals.client.loop or not globals.client.loop.is_running():
|
||||
return {"status": "error", "message": "Discord client not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Discord client not ready"})
|
||||
|
||||
if globals.EVIL_MODE:
|
||||
globals.client.loop.create_task(revert_evil_mode_changes(globals.client))
|
||||
@@ -95,10 +96,10 @@ def set_evil_mood_endpoint(data: EvilMoodSetRequest):
|
||||
from utils.evil_mode import set_evil_mood, is_valid_evil_mood, update_all_evil_nicknames
|
||||
|
||||
if not is_valid_evil_mood(data.mood):
|
||||
return {
|
||||
return JSONResponse(status_code=400, content={
|
||||
"status": "error",
|
||||
"message": f"Mood '{data.mood}' not recognized. Available evil moods: {', '.join(globals.EVIL_AVAILABLE_MOODS)}"
|
||||
}
|
||||
})
|
||||
|
||||
success = set_evil_mood(data.mood)
|
||||
if success:
|
||||
@@ -107,4 +108,4 @@ def set_evil_mood_endpoint(data: EvilMoodSetRequest):
|
||||
globals.client.loop.create_task(update_all_evil_nicknames(globals.client))
|
||||
return {"status": "ok", "new_mood": data.mood}
|
||||
|
||||
return {"status": "error", "message": "Failed to set evil mood"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": "Failed to set evil mood"})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Figurine subscriber and send routes."""
|
||||
|
||||
from fastapi import APIRouter, Form
|
||||
from fastapi.responses import JSONResponse
|
||||
import globals
|
||||
from utils.figurine_notifier import (
|
||||
load_subscribers as figurine_load_subscribers,
|
||||
@@ -29,7 +30,7 @@ async def add_figurine_subscriber(user_id: str = Form(...)):
|
||||
ok = figurine_add_subscriber(uid)
|
||||
return {"status": "ok", "added": ok}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.delete("/figurines/subscribers/{user_id}")
|
||||
@@ -39,7 +40,7 @@ async def delete_figurine_subscriber(user_id: str):
|
||||
ok = figurine_remove_subscriber(uid)
|
||||
return {"status": "ok", "removed": ok}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.post("/figurines/send_now")
|
||||
@@ -49,7 +50,7 @@ async def figurines_send_now(tweet_url: str = Form(None)):
|
||||
logger.info(f"Sending figurine DMs to all subscribers, tweet_url: {tweet_url}")
|
||||
globals.client.loop.create_task(send_figurine_dm_to_all_subscribers(globals.client, tweet_url=tweet_url))
|
||||
return {"status": "ok", "message": "Figurine DMs queued"}
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
|
||||
@router.post("/figurines/send_to_user")
|
||||
@@ -59,14 +60,14 @@ async def figurines_send_to_user(user_id: str = Form(...), tweet_url: str = Form
|
||||
|
||||
if not globals.client or not globals.client.loop or not globals.client.loop.is_running():
|
||||
logger.error("Bot not ready")
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
try:
|
||||
user_id_int = int(user_id)
|
||||
logger.debug(f"Parsed user_id as {user_id_int}")
|
||||
except ValueError:
|
||||
logger.error(f"Invalid user ID: '{user_id}'")
|
||||
return {"status": "error", "message": "Invalid user ID"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "Invalid user ID"})
|
||||
|
||||
# Clean up tweet URL if it's empty string
|
||||
if tweet_url == "":
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""GPU selection routes."""
|
||||
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
from utils.logger import get_logger
|
||||
|
||||
logger = get_logger('api')
|
||||
@@ -22,7 +23,7 @@ async def select_gpu(request: Request):
|
||||
gpu = data.get("gpu", "nvidia").lower()
|
||||
|
||||
if gpu not in ["nvidia", "amd"]:
|
||||
return {"status": "error", "message": "Invalid GPU selection. Must be 'nvidia' or 'amd'"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "Invalid GPU selection. Must be 'nvidia' or 'amd'"})
|
||||
|
||||
try:
|
||||
from config_manager import config_manager
|
||||
@@ -32,7 +33,7 @@ async def select_gpu(request: Request):
|
||||
logger.info(f"GPU Selection: Switched to {gpu.upper()} GPU")
|
||||
return {"status": "ok", "message": f"Switched to {gpu.upper()} GPU", "gpu": gpu}
|
||||
else:
|
||||
return {"status": "error", "message": "Failed to save GPU state"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": "Failed to save GPU state"})
|
||||
except Exception as e:
|
||||
logger.error(f"GPU Selection Error: {e}")
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import os
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi.responses import FileResponse, JSONResponse
|
||||
from utils.logger import get_logger
|
||||
|
||||
logger = get_logger('api')
|
||||
@@ -16,7 +16,7 @@ async def manual_image_generation(req: dict):
|
||||
try:
|
||||
prompt = req.get("prompt", "").strip()
|
||||
if not prompt:
|
||||
return {"status": "error", "message": "Prompt is required"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "Prompt is required"})
|
||||
|
||||
from utils.image_generation import generate_image_with_comfyui
|
||||
image_path = await generate_image_with_comfyui(prompt)
|
||||
@@ -24,10 +24,10 @@ async def manual_image_generation(req: dict):
|
||||
if image_path:
|
||||
return {"status": "ok", "message": f"Image generated successfully", "image_path": image_path}
|
||||
else:
|
||||
return {"status": "error", "message": "Failed to generate image"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": "Failed to generate image"})
|
||||
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Error: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Error: {e}"})
|
||||
|
||||
|
||||
@router.get("/image/status")
|
||||
@@ -39,7 +39,7 @@ async def get_image_generation_status():
|
||||
return {"status": "ok", **status}
|
||||
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Error: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Error: {e}"})
|
||||
|
||||
|
||||
@router.post("/image/test-detection")
|
||||
@@ -48,7 +48,7 @@ async def test_image_detection(req: dict):
|
||||
try:
|
||||
message = req.get("message", "").strip()
|
||||
if not message:
|
||||
return {"status": "error", "message": "Message is required"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "Message is required"})
|
||||
|
||||
from utils.image_generation import detect_image_request
|
||||
is_image_request, extracted_prompt = await detect_image_request(message)
|
||||
@@ -61,7 +61,7 @@ async def test_image_detection(req: dict):
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Error: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Error: {e}"})
|
||||
|
||||
|
||||
@router.get("/image/view/{filename}")
|
||||
@@ -88,7 +88,7 @@ async def view_generated_image(filename: str):
|
||||
|
||||
if not image_path:
|
||||
logger.warning(f"Image not found anywhere: {filename}")
|
||||
return {"status": "error", "message": f"Image not found: {filename}"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": f"Image not found: {filename}"})
|
||||
|
||||
# Determine content type based on file extension
|
||||
ext = filename.lower().split('.')[-1]
|
||||
@@ -105,4 +105,4 @@ async def view_generated_image(filename: str):
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error serving image: {e}")
|
||||
return {"status": "error", "message": f"Error serving image: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Error serving image: {e}"})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Language mode routes."""
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import JSONResponse
|
||||
import globals
|
||||
from utils.logger import get_logger
|
||||
|
||||
@@ -53,7 +54,7 @@ def toggle_language_mode():
|
||||
def set_language_mode(language: str = "english"):
|
||||
"""Set language mode to either 'english' or 'japanese'"""
|
||||
if language.lower() not in ["english", "japanese"]:
|
||||
return {"error": f"Invalid language mode '{language}'. Use 'english' or 'japanese'."}, 400
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": f"Invalid language mode '{language}'. Use 'english' or 'japanese'."})
|
||||
|
||||
globals.LANGUAGE_MODE = language.lower()
|
||||
model_used = globals.JAPANESE_TEXT_MODEL if language.lower() == "japanese" else globals.TEXT_MODEL
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Logging configuration routes: get/set log levels, filters, components."""
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import JSONResponse
|
||||
from routes.models import LogConfigUpdateRequest, LogFilterUpdateRequest
|
||||
from utils.logger import get_logger, list_components, get_component_stats
|
||||
from utils.log_config import (
|
||||
@@ -23,7 +24,7 @@ async def get_log_config():
|
||||
return {"success": True, "config": config}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get log config: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
|
||||
@router.post("/api/log/config")
|
||||
@@ -37,13 +38,13 @@ async def update_log_config(request: LogConfigUpdateRequest):
|
||||
enabled_levels=request.enabled_levels
|
||||
)
|
||||
if not success:
|
||||
return {"success": False, "error": f"Failed to update component {request.component}"}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": f"Failed to update component {request.component}"})
|
||||
|
||||
logger.info(f"Log config updated: component={request.component}, enabled_levels={request.enabled_levels}, enabled={request.enabled}")
|
||||
return {"success": True, "message": "Configuration updated"}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update log config: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
|
||||
@router.get("/api/log/components")
|
||||
@@ -60,7 +61,7 @@ async def get_log_components():
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get log components: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
|
||||
@router.post("/api/log/reload")
|
||||
@@ -72,10 +73,10 @@ async def reload_log_config():
|
||||
logger.info("Log configuration reloaded")
|
||||
return {"success": True, "message": "Configuration reloaded"}
|
||||
else:
|
||||
return {"success": False, "error": "Failed to reload configuration"}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": "Failed to reload configuration"})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to reload log config: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
|
||||
@router.post("/api/log/filters")
|
||||
@@ -93,10 +94,10 @@ async def update_log_filters(request: LogFilterUpdateRequest):
|
||||
logger.info(f"API filters updated: {request.dict(exclude_none=True)}")
|
||||
return {"success": True, "message": "Filters updated"}
|
||||
else:
|
||||
return {"success": False, "error": "Failed to update filters"}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": "Failed to update filters"})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update filters: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
|
||||
@router.post("/api/log/reset")
|
||||
@@ -108,10 +109,10 @@ async def reset_log_config():
|
||||
logger.info("Log configuration reset to defaults")
|
||||
return {"success": True, "message": "Configuration reset to defaults"}
|
||||
else:
|
||||
return {"success": False, "error": "Failed to reset configuration"}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": "Failed to reset configuration"})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to reset log config: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
|
||||
@router.post("/api/log/global-level")
|
||||
@@ -125,10 +126,10 @@ async def update_global_level_endpoint(level: str, enabled: bool):
|
||||
logger.info(f"Global level {level} {action} across all components")
|
||||
return {"success": True, "message": f"Level {level} {action} globally"}
|
||||
else:
|
||||
return {"success": False, "error": f"Failed to update global level {level}"}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": f"Failed to update global level {level}"})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update global level: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
|
||||
@router.post("/api/log/timestamp-format")
|
||||
@@ -140,10 +141,10 @@ async def update_timestamp_format_endpoint(format_type: str):
|
||||
logger.info(f"Timestamp format updated to: {format_type}")
|
||||
return {"success": True, "message": f"Timestamp format set to: {format_type}"}
|
||||
else:
|
||||
return {"success": False, "error": f"Invalid timestamp format: {format_type}"}
|
||||
return JSONResponse(status_code=400, content={"success": False, "error": f"Invalid timestamp format: {format_type}"})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update timestamp format: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
|
||||
@router.get("/api/log/files/{component}")
|
||||
@@ -155,7 +156,7 @@ async def get_log_file(component: str, lines: int = 100):
|
||||
log_file = log_dir / f'{component.replace(".", "_")}.log'
|
||||
|
||||
if not log_file.exists():
|
||||
return {"success": False, "error": "Log file not found"}
|
||||
return JSONResponse(status_code=404, content={"success": False, "error": "Log file not found"})
|
||||
|
||||
with open(log_file, 'r', encoding='utf-8') as f:
|
||||
all_lines = f.readlines()
|
||||
@@ -170,4 +171,4 @@ async def get_log_file(component: str, lines: int = 100):
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to read log file for {component}: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import io
|
||||
from typing import List
|
||||
from fastapi import APIRouter, UploadFile, File, Form
|
||||
from fastapi.responses import JSONResponse
|
||||
import discord
|
||||
import globals
|
||||
from utils.logger import get_logger
|
||||
@@ -23,7 +24,7 @@ async def manual_send(
|
||||
try:
|
||||
channel = globals.client.get_channel(int(channel_id))
|
||||
if not channel:
|
||||
return {"status": "error", "message": "Channel not found"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": "Channel not found"})
|
||||
|
||||
# Read file content immediately before the request closes
|
||||
file_data = []
|
||||
@@ -36,7 +37,7 @@ async def manual_send(
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to read file {file.filename}: {e}")
|
||||
return {"status": "error", "message": f"Failed to read file {file.filename}: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to read file {file.filename}: {e}"})
|
||||
|
||||
async def send_message_and_files():
|
||||
try:
|
||||
@@ -70,7 +71,7 @@ async def manual_send(
|
||||
return {"status": "ok", "message": "Message and files queued for sending"}
|
||||
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Error: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Error: {e}"})
|
||||
|
||||
|
||||
@router.post("/manual/send-webhook")
|
||||
@@ -88,10 +89,10 @@ async def manual_send_webhook(
|
||||
|
||||
channel = globals.client.get_channel(int(channel_id))
|
||||
if not channel:
|
||||
return {"status": "error", "message": "Channel not found"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": "Channel not found"})
|
||||
|
||||
if persona not in ["miku", "evil"]:
|
||||
return {"status": "error", "message": "Invalid persona. Must be 'miku' or 'evil'"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "Invalid persona. Must be 'miku' or 'evil'"})
|
||||
|
||||
file_data = []
|
||||
for file in files:
|
||||
@@ -103,7 +104,7 @@ async def manual_send_webhook(
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to read file {file.filename}: {e}")
|
||||
return {"status": "error", "message": f"Failed to read file {file.filename}: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to read file {file.filename}: {e}"})
|
||||
|
||||
async def send_webhook_message():
|
||||
try:
|
||||
@@ -146,7 +147,7 @@ async def manual_send_webhook(
|
||||
return {"status": "ok", "message": f"Webhook message queued for sending as {persona}"}
|
||||
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Error: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Error: {e}"})
|
||||
|
||||
|
||||
@router.post("/messages/react")
|
||||
@@ -158,17 +159,17 @@ async def add_reaction_to_message(
|
||||
"""Add a reaction to a specific message"""
|
||||
try:
|
||||
if not globals.client or not globals.client.loop or not globals.client.loop.is_running():
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
try:
|
||||
msg_id = int(message_id)
|
||||
chan_id = int(channel_id)
|
||||
except ValueError:
|
||||
return {"status": "error", "message": "Invalid message ID or channel ID format"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "Invalid message ID or channel ID format"})
|
||||
|
||||
channel = globals.client.get_channel(chan_id)
|
||||
if not channel:
|
||||
return {"status": "error", "message": f"Channel {channel_id} not found"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": f"Channel {channel_id} not found"})
|
||||
|
||||
async def add_reaction_task():
|
||||
try:
|
||||
@@ -193,4 +194,4 @@ async def add_reaction_to_message(
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to add reaction: {e}")
|
||||
return {"status": "error", "message": f"Failed to add reaction: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to add reaction: {e}"})
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from typing import Optional
|
||||
from fastapi import APIRouter, Form
|
||||
from fastapi.responses import JSONResponse
|
||||
import globals
|
||||
from routes.models import MemoryDeleteRequest, MemoryEditRequest, MemoryCreateRequest
|
||||
from utils.logger import get_logger
|
||||
@@ -51,7 +52,7 @@ async def get_memory_stats():
|
||||
from utils.cat_client import cat_adapter
|
||||
stats = await cat_adapter.get_memory_stats()
|
||||
if stats is None:
|
||||
return {"success": False, "error": "Could not reach Cheshire Cat"}
|
||||
return JSONResponse(status_code=502, content={"success": False, "error": "Could not reach Cheshire Cat"})
|
||||
return {"success": True, "collections": stats.get("collections", [])}
|
||||
|
||||
|
||||
@@ -69,7 +70,7 @@ async def get_episodic_memories():
|
||||
from utils.cat_client import cat_adapter
|
||||
result = await cat_adapter.get_memory_points(collection="episodic", limit=100)
|
||||
if result is None:
|
||||
return {"success": False, "error": "Could not reach Cheshire Cat"}
|
||||
return JSONResponse(status_code=502, content={"success": False, "error": "Could not reach Cheshire Cat"})
|
||||
|
||||
memories = []
|
||||
for point in result.get("points", []):
|
||||
@@ -90,7 +91,7 @@ async def trigger_memory_consolidation():
|
||||
logger.info("🌙 Manual memory consolidation triggered via API")
|
||||
result = await cat_adapter.trigger_consolidation()
|
||||
if result is None:
|
||||
return {"success": False, "error": "Consolidation failed or timed out"}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": "Consolidation failed or timed out"})
|
||||
return {"success": True, "result": result}
|
||||
|
||||
|
||||
@@ -108,11 +109,11 @@ async def delete_all_memories(request: MemoryDeleteRequest):
|
||||
|
||||
if request.confirmation != REQUIRED_CONFIRMATION:
|
||||
logger.warning(f"Memory deletion rejected: wrong confirmation string")
|
||||
return {
|
||||
return JSONResponse(status_code=400, content={
|
||||
"success": False,
|
||||
"error": "Confirmation string does not match. "
|
||||
f"Expected exactly: \"{REQUIRED_CONFIRMATION}\""
|
||||
}
|
||||
})
|
||||
|
||||
from utils.cat_client import cat_adapter
|
||||
logger.warning("⚠️ MEMORY DELETION CONFIRMED — wiping all memories!")
|
||||
@@ -132,10 +133,10 @@ async def delete_all_memories(request: MemoryDeleteRequest):
|
||||
"conversation_history_cleared": history_success
|
||||
}
|
||||
else:
|
||||
return {
|
||||
return JSONResponse(status_code=500, content={
|
||||
"success": False,
|
||||
"error": "Failed to wipe memory collections. Check Cat connection."
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@router.delete("/memory/point/{collection}/{point_id}")
|
||||
@@ -146,7 +147,7 @@ async def delete_single_memory_point(collection: str, point_id: str):
|
||||
if success:
|
||||
return {"success": True, "deleted": point_id}
|
||||
else:
|
||||
return {"success": False, "error": f"Failed to delete point {point_id}"}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": f"Failed to delete point {point_id}"})
|
||||
|
||||
|
||||
@router.put("/memory/point/{collection}/{point_id}")
|
||||
@@ -162,7 +163,7 @@ async def edit_memory_point(collection: str, point_id: str, request: MemoryEditR
|
||||
if success:
|
||||
return {"success": True, "updated": point_id}
|
||||
else:
|
||||
return {"success": False, "error": f"Failed to update point {point_id}"}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": f"Failed to update point {point_id}"})
|
||||
|
||||
|
||||
@router.post("/memory/create")
|
||||
@@ -176,7 +177,7 @@ async def create_memory_point(request: MemoryCreateRequest):
|
||||
from utils.cat_client import cat_adapter
|
||||
|
||||
if request.collection not in ['declarative', 'episodic']:
|
||||
return {"success": False, "error": "Collection must be 'declarative' or 'episodic'"}
|
||||
return JSONResponse(status_code=400, content={"success": False, "error": "Collection must be 'declarative' or 'episodic'"})
|
||||
|
||||
# Create the memory point
|
||||
result = await cat_adapter.create_memory_point(
|
||||
@@ -190,4 +191,4 @@ async def create_memory_point(request: MemoryCreateRequest):
|
||||
if result:
|
||||
return {"success": True, "point_id": result, "collection": request.collection}
|
||||
else:
|
||||
return {"success": False, "error": "Failed to create memory point"}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": "Failed to create memory point"})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Mood management routes: DM mood, per-server mood, available moods, test mood."""
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import JSONResponse
|
||||
import globals
|
||||
from server_manager import server_manager
|
||||
from routes.models import MoodSetRequest
|
||||
@@ -23,7 +24,7 @@ async def set_mood_endpoint(data: MoodSetRequest):
|
||||
# This endpoint now operates on DM_MOOD
|
||||
from utils.moods import MOOD_EMOJIS
|
||||
if data.mood not in MOOD_EMOJIS:
|
||||
return {"status": "error", "message": f"Mood '{data.mood}' not recognized. Available moods: {', '.join(MOOD_EMOJIS.keys())}"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": f"Mood '{data.mood}' not recognized. Available moods: {', '.join(MOOD_EMOJIS.keys())}"})
|
||||
|
||||
# Update DM mood (DMs don't have nicknames, so no nickname update needed)
|
||||
globals.DM_MOOD = data.mood
|
||||
@@ -94,13 +95,13 @@ async def set_server_mood_endpoint(guild_id: int, data: MoodSetRequest):
|
||||
# Check if server exists
|
||||
if guild_id not in server_manager.servers:
|
||||
logger.warning(f"Server {guild_id} not found in server_manager.servers")
|
||||
return {"status": "error", "message": "Server not found"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": "Server not found"})
|
||||
|
||||
# Check if mood is valid
|
||||
from utils.moods import MOOD_EMOJIS
|
||||
if data.mood not in MOOD_EMOJIS:
|
||||
logger.warning(f"Mood '{data.mood}' not found in MOOD_EMOJIS. Available moods: {list(MOOD_EMOJIS.keys())}")
|
||||
return {"status": "error", "message": f"Mood '{data.mood}' not recognized. Available moods: {', '.join(MOOD_EMOJIS.keys())}"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": f"Mood '{data.mood}' not recognized. Available moods: {', '.join(MOOD_EMOJIS.keys())}"})
|
||||
|
||||
success = server_manager.set_server_mood(guild_id, data.mood)
|
||||
logger.debug(f"Server mood set result: {success}")
|
||||
@@ -113,7 +114,7 @@ async def set_server_mood_endpoint(guild_id: int, data: MoodSetRequest):
|
||||
return {"status": "ok", "new_mood": data.mood, "guild_id": guild_id}
|
||||
|
||||
logger.warning(f"set_server_mood returned False for unknown reason")
|
||||
return {"status": "error", "message": "Failed to set server mood"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": "Failed to set server mood"})
|
||||
|
||||
|
||||
@router.post("/servers/{guild_id}/mood/reset")
|
||||
@@ -124,7 +125,7 @@ async def reset_server_mood_endpoint(guild_id: int):
|
||||
# Check if server exists
|
||||
if guild_id not in server_manager.servers:
|
||||
logger.warning(f"Server {guild_id} not found in server_manager.servers")
|
||||
return {"status": "error", "message": "Server not found"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": "Server not found"})
|
||||
|
||||
logger.debug(f"Server validation passed, calling set_server_mood")
|
||||
success = server_manager.set_server_mood(guild_id, "neutral")
|
||||
@@ -138,7 +139,7 @@ async def reset_server_mood_endpoint(guild_id: int):
|
||||
return {"status": "ok", "new_mood": "neutral", "guild_id": guild_id}
|
||||
|
||||
logger.warning(f"set_server_mood returned False for unknown reason")
|
||||
return {"status": "error", "message": "Failed to reset server mood"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": "Failed to reset server mood"})
|
||||
|
||||
|
||||
@router.get("/servers/{guild_id}/mood/state")
|
||||
@@ -147,7 +148,7 @@ def get_server_mood_state(guild_id: int):
|
||||
mood_state = server_manager.get_server_mood_state(guild_id)
|
||||
if mood_state:
|
||||
return {"status": "ok", "guild_id": guild_id, "mood_state": mood_state}
|
||||
return {"status": "error", "message": "Server not found"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": "Server not found"})
|
||||
|
||||
|
||||
# ========== Misc Mood ==========
|
||||
@@ -166,7 +167,7 @@ async def test_mood_change(guild_id: int, data: MoodSetRequest):
|
||||
|
||||
# Check if server exists
|
||||
if guild_id not in server_manager.servers:
|
||||
return {"status": "error", "message": f"Server {guild_id} not found"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": f"Server {guild_id} not found"})
|
||||
|
||||
server_config = server_manager.get_server_config(guild_id)
|
||||
logger.debug(f"TEST: Server config found: {server_config.guild_name if server_config else 'None'}")
|
||||
@@ -189,4 +190,4 @@ async def test_mood_change(guild_id: int, data: MoodSetRequest):
|
||||
|
||||
return {"status": "ok", "message": f"Test mood change completed", "success": success}
|
||||
|
||||
return {"status": "error", "message": "Mood change failed"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": "Mood change failed"})
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import os
|
||||
from typing import List
|
||||
from fastapi import APIRouter, UploadFile, File, Form
|
||||
from fastapi.responses import FileResponse
|
||||
from fastapi.responses import FileResponse, JSONResponse
|
||||
import globals
|
||||
from routes.models import (
|
||||
ManualCropRequest, DescriptionUpdateRequest,
|
||||
@@ -25,7 +25,7 @@ async def trigger_profile_picture_change(
|
||||
):
|
||||
"""Change Miku's profile picture. If a file is provided, use it. Otherwise, search Danbooru."""
|
||||
if not globals.client or not globals.client.loop or not globals.client.loop.is_running():
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
try:
|
||||
from utils.profile_picture_manager import profile_picture_manager
|
||||
@@ -54,17 +54,17 @@ async def trigger_profile_picture_change(
|
||||
"metadata": result.get("metadata", {})
|
||||
}
|
||||
else:
|
||||
return {
|
||||
return JSONResponse(status_code=500, content={
|
||||
"status": "error",
|
||||
"message": result.get("error", "Unknown error"),
|
||||
"source": result["source"]
|
||||
}
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in profile picture API: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return {"status": "error", "message": f"Unexpected error: {str(e)}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Unexpected error: {str(e)}"})
|
||||
|
||||
|
||||
@router.get("/profile-picture/metadata")
|
||||
@@ -78,14 +78,14 @@ async def get_profile_picture_metadata():
|
||||
else:
|
||||
return {"status": "ok", "metadata": None, "message": "No metadata found"}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.post("/profile-picture/restore-fallback")
|
||||
async def restore_fallback_profile_picture():
|
||||
"""Restore the original fallback profile picture"""
|
||||
if not globals.client or not globals.client.loop or not globals.client.loop.is_running():
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
try:
|
||||
from utils.profile_picture_manager import profile_picture_manager
|
||||
@@ -93,16 +93,16 @@ async def restore_fallback_profile_picture():
|
||||
if success:
|
||||
return {"status": "ok", "message": "Fallback profile picture restored"}
|
||||
else:
|
||||
return {"status": "error", "message": "Failed to restore fallback"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": "Failed to restore fallback"})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.post("/role-color/custom")
|
||||
async def set_custom_role_color(hex_color: str = Form(...)):
|
||||
"""Set a custom role color across all servers"""
|
||||
if not globals.client or not globals.client.loop or not globals.client.loop.is_running():
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
try:
|
||||
from utils.profile_picture_manager import profile_picture_manager
|
||||
@@ -114,16 +114,16 @@ async def set_custom_role_color(hex_color: str = Form(...)):
|
||||
"color": result["color"]
|
||||
}
|
||||
else:
|
||||
return {"status": "error", "message": result.get("error", "Unknown error")}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": result.get("error", "Unknown error")})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.post("/role-color/reset-fallback")
|
||||
async def reset_role_color_to_fallback():
|
||||
"""Reset role color to fallback (#86cecb)"""
|
||||
if not globals.client or not globals.client.loop or not globals.client.loop.is_running():
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
try:
|
||||
from utils.profile_picture_manager import profile_picture_manager
|
||||
@@ -135,9 +135,9 @@ async def reset_role_color_to_fallback():
|
||||
"color": result["color"]
|
||||
}
|
||||
else:
|
||||
return {"status": "error", "message": "Failed to reset color"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": "Failed to reset color"})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
# ========== Profile Picture — Image Serving ==========
|
||||
@@ -148,7 +148,7 @@ async def serve_original_profile_picture():
|
||||
from utils.profile_picture_manager import profile_picture_manager
|
||||
path = profile_picture_manager.ORIGINAL_PATH
|
||||
if not os.path.exists(path):
|
||||
return {"status": "error", "message": "No original image found"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": "No original image found"})
|
||||
return FileResponse(path, media_type="image/png", headers={"Cache-Control": "no-cache"})
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ async def serve_current_profile_picture():
|
||||
from utils.profile_picture_manager import profile_picture_manager
|
||||
path = profile_picture_manager.CURRENT_PATH
|
||||
if not os.path.exists(path):
|
||||
return {"status": "error", "message": "No current image found"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": "No current image found"})
|
||||
return FileResponse(path, media_type="image/png", headers={"Cache-Control": "no-cache"})
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ async def trigger_profile_picture_change_no_crop(
|
||||
):
|
||||
"""Change Miku's profile picture but skip auto-cropping."""
|
||||
if not globals.client or not globals.client.loop or not globals.client.loop.is_running():
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
try:
|
||||
from utils.profile_picture_manager import profile_picture_manager
|
||||
@@ -200,23 +200,23 @@ async def trigger_profile_picture_change_no_crop(
|
||||
"metadata": result.get("metadata", {})
|
||||
}
|
||||
else:
|
||||
return {
|
||||
return JSONResponse(status_code=500, content={
|
||||
"status": "error",
|
||||
"message": result.get("error", "Unknown error"),
|
||||
"source": result.get("source")
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"Error in change-no-crop API: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return {"status": "error", "message": f"Unexpected error: {str(e)}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Unexpected error: {str(e)}"})
|
||||
|
||||
|
||||
@router.post("/profile-picture/manual-crop")
|
||||
async def apply_manual_crop(req: ManualCropRequest):
|
||||
"""Apply a manual crop to the stored original image"""
|
||||
if not globals.client or not globals.client.loop or not globals.client.loop.is_running():
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
try:
|
||||
from utils.profile_picture_manager import profile_picture_manager
|
||||
@@ -230,16 +230,16 @@ async def apply_manual_crop(req: ManualCropRequest):
|
||||
"metadata": result.get("metadata", {})
|
||||
}
|
||||
else:
|
||||
return {"status": "error", "message": result.get("error", "Unknown error")}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": result.get("error", "Unknown error")})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.post("/profile-picture/auto-crop")
|
||||
async def apply_auto_crop():
|
||||
"""Run intelligent auto-crop on the stored original image"""
|
||||
if not globals.client or not globals.client.loop or not globals.client.loop.is_running():
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
|
||||
try:
|
||||
from utils.profile_picture_manager import profile_picture_manager
|
||||
@@ -251,9 +251,9 @@ async def apply_auto_crop():
|
||||
"metadata": result.get("metadata", {})
|
||||
}
|
||||
else:
|
||||
return {"status": "error", "message": result.get("error", "Unknown error")}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": result.get("error", "Unknown error")})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.post("/profile-picture/description")
|
||||
@@ -267,9 +267,9 @@ async def update_profile_picture_description(req: DescriptionUpdateRequest):
|
||||
if result["success"]:
|
||||
return {"status": "ok", "message": "Description updated successfully"}
|
||||
else:
|
||||
return {"status": "error", "message": result.get("error", "Unknown error")}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": result.get("error", "Unknown error")})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.post("/profile-picture/regenerate-description")
|
||||
@@ -285,9 +285,9 @@ async def regenerate_profile_picture_description():
|
||||
"description": result["description"]
|
||||
}
|
||||
else:
|
||||
return {"status": "error", "message": result.get("error", "Unknown error")}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": result.get("error", "Unknown error")})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.get("/profile-picture/description")
|
||||
@@ -298,7 +298,7 @@ async def get_profile_picture_description():
|
||||
description = profile_picture_manager.get_current_description()
|
||||
return {"status": "ok", "description": description or ""}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
# ========== Profile Picture — Album / Gallery ==========
|
||||
@@ -311,7 +311,7 @@ async def list_album_entries():
|
||||
entries = profile_picture_manager.get_album_entries()
|
||||
return {"status": "ok", "entries": entries, "count": len(entries)}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.get("/profile-picture/album/disk-usage")
|
||||
@@ -322,7 +322,7 @@ async def get_album_disk_usage():
|
||||
usage = profile_picture_manager.get_album_disk_usage()
|
||||
return {"status": "ok", **usage}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.get("/profile-picture/album/{entry_id}")
|
||||
@@ -334,25 +334,25 @@ async def get_album_entry(entry_id: str):
|
||||
if meta:
|
||||
return {"status": "ok", "entry": meta}
|
||||
else:
|
||||
return {"status": "error", "message": "Album entry not found"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": "Album entry not found"})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.get("/profile-picture/album/{entry_id}/image/{image_type}")
|
||||
async def serve_album_image(entry_id: str, image_type: str):
|
||||
"""Serve an album entry's image (original or cropped)"""
|
||||
if image_type not in ("original", "cropped"):
|
||||
return {"status": "error", "message": "image_type must be 'original' or 'cropped'"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "image_type must be 'original' or 'cropped'"})
|
||||
try:
|
||||
from utils.profile_picture_manager import profile_picture_manager
|
||||
path = profile_picture_manager.get_album_image_path(entry_id, image_type)
|
||||
if path:
|
||||
return FileResponse(path, media_type="image/png", headers={"Cache-Control": "no-cache"})
|
||||
else:
|
||||
return {"status": "error", "message": f"No {image_type} image for this entry"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": f"No {image_type} image for this entry"})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.post("/profile-picture/album/add")
|
||||
@@ -373,10 +373,10 @@ async def add_to_album(file: UploadFile = File(...)):
|
||||
"metadata": result.get("metadata", {})
|
||||
}
|
||||
else:
|
||||
return {"status": "error", "message": result.get("error", "Unknown error")}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": result.get("error", "Unknown error")})
|
||||
except Exception as e:
|
||||
logger.error(f"Error adding to album: {e}")
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.post("/profile-picture/album/add-batch")
|
||||
@@ -403,14 +403,14 @@ async def add_batch_to_album(files: List[UploadFile] = File(...)):
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Error in batch album add: {e}")
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.post("/profile-picture/album/{entry_id}/set-current")
|
||||
async def set_album_entry_as_current(entry_id: str):
|
||||
"""Set an album entry as the current Discord profile picture"""
|
||||
if not globals.client or not globals.client.loop or not globals.client.loop.is_running():
|
||||
return {"status": "error", "message": "Bot not ready"}
|
||||
return JSONResponse(status_code=503, content={"status": "error", "message": "Bot not ready"})
|
||||
try:
|
||||
from utils.profile_picture_manager import profile_picture_manager
|
||||
result = await profile_picture_manager.set_album_entry_as_current(
|
||||
@@ -423,9 +423,9 @@ async def set_album_entry_as_current(entry_id: str):
|
||||
"archived_entry_id": result.get("archived_entry_id")
|
||||
}
|
||||
else:
|
||||
return {"status": "error", "message": result.get("error", "Unknown error")}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": result.get("error", "Unknown error")})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.post("/profile-picture/album/{entry_id}/manual-crop")
|
||||
@@ -444,9 +444,9 @@ async def manual_crop_album_entry(entry_id: str, req: AlbumCropRequest):
|
||||
"metadata": result.get("metadata", {})
|
||||
}
|
||||
else:
|
||||
return {"status": "error", "message": result.get("error", "Unknown error")}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": result.get("error", "Unknown error")})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.post("/profile-picture/album/{entry_id}/auto-crop")
|
||||
@@ -464,9 +464,9 @@ async def auto_crop_album_entry(entry_id: str):
|
||||
"metadata": result.get("metadata", {})
|
||||
}
|
||||
else:
|
||||
return {"status": "error", "message": result.get("error", "Unknown error")}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": result.get("error", "Unknown error")})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.post("/profile-picture/album/{entry_id}/description")
|
||||
@@ -480,9 +480,9 @@ async def update_album_entry_description(entry_id: str, req: AlbumDescriptionReq
|
||||
if result["success"]:
|
||||
return {"status": "ok", "message": "Description updated"}
|
||||
else:
|
||||
return {"status": "error", "message": result.get("error", "Unknown error")}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": result.get("error", "Unknown error")})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.delete("/profile-picture/album/{entry_id}")
|
||||
@@ -493,9 +493,9 @@ async def delete_album_entry(entry_id: str):
|
||||
if profile_picture_manager.delete_album_entry(entry_id):
|
||||
return {"status": "ok", "message": "Album entry deleted"}
|
||||
else:
|
||||
return {"status": "error", "message": "Album entry not found"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": "Album entry not found"})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.post("/profile-picture/album/delete-bulk")
|
||||
@@ -510,7 +510,7 @@ async def bulk_delete_album_entries(req: BulkDeleteRequest):
|
||||
**result
|
||||
}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
|
||||
@router.post("/profile-picture/album/add-current")
|
||||
@@ -522,6 +522,6 @@ async def add_current_to_album():
|
||||
if entry_id:
|
||||
return {"status": "ok", "message": "Current PFP archived to album", "entry_id": entry_id}
|
||||
else:
|
||||
return {"status": "error", "message": "No current PFP to archive"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": "No current PFP to archive"})
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import os
|
||||
import json
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import JSONResponse
|
||||
import globals
|
||||
from server_manager import server_manager
|
||||
from routes.models import ServerConfigRequest
|
||||
@@ -63,7 +64,7 @@ def add_server(data: ServerConfigRequest):
|
||||
server_manager.start_all_schedulers(globals.client)
|
||||
return {"status": "ok", "message": f"Server {data.guild_name} added successfully"}
|
||||
else:
|
||||
return {"status": "error", "message": "Failed to add server"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": "Failed to add server"})
|
||||
|
||||
|
||||
@router.delete("/servers/{guild_id}")
|
||||
@@ -73,7 +74,7 @@ def remove_server(guild_id: int):
|
||||
if success:
|
||||
return {"status": "ok", "message": "Server removed successfully"}
|
||||
else:
|
||||
return {"status": "error", "message": "Failed to remove server"}
|
||||
return JSONResponse(status_code=404, content={"status": "error", "message": "Failed to remove server"})
|
||||
|
||||
|
||||
@router.put("/servers/{guild_id}")
|
||||
@@ -85,7 +86,7 @@ def update_server(guild_id: int, data: dict):
|
||||
server_manager.start_all_schedulers(globals.client)
|
||||
return {"status": "ok", "message": "Server configuration updated"}
|
||||
else:
|
||||
return {"status": "error", "message": "Failed to update server configuration"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": "Failed to update server configuration"})
|
||||
|
||||
|
||||
@router.post("/servers/{guild_id}/bedtime-range")
|
||||
@@ -96,7 +97,7 @@ def update_server_bedtime_range(guild_id: int, data: dict):
|
||||
required_fields = ['bedtime_hour', 'bedtime_minute', 'bedtime_hour_end', 'bedtime_minute_end']
|
||||
for field in required_fields:
|
||||
if field not in data:
|
||||
return {"status": "error", "message": f"Missing required field: {field}"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": f"Missing required field: {field}"})
|
||||
|
||||
try:
|
||||
bedtime_hour = int(data['bedtime_hour'])
|
||||
@@ -105,12 +106,12 @@ def update_server_bedtime_range(guild_id: int, data: dict):
|
||||
bedtime_minute_end = int(data['bedtime_minute_end'])
|
||||
|
||||
if not (0 <= bedtime_hour <= 23) or not (0 <= bedtime_hour_end <= 23):
|
||||
return {"status": "error", "message": "Hours must be between 0 and 23"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "Hours must be between 0 and 23"})
|
||||
if not (0 <= bedtime_minute <= 59) or not (0 <= bedtime_minute_end <= 59):
|
||||
return {"status": "error", "message": "Minutes must be between 0 and 59"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "Minutes must be between 0 and 59"})
|
||||
|
||||
except (ValueError, TypeError):
|
||||
return {"status": "error", "message": "Invalid time values provided"}
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "Invalid time values provided"})
|
||||
|
||||
success = server_manager.update_server_config(guild_id, **data)
|
||||
if success:
|
||||
@@ -122,9 +123,9 @@ def update_server_bedtime_range(guild_id: int, data: dict):
|
||||
"message": f"Bedtime range updated: {bedtime_hour:02d}:{bedtime_minute:02d} - {bedtime_hour_end:02d}:{bedtime_minute_end:02d}"
|
||||
}
|
||||
else:
|
||||
return {"status": "error", "message": "Updated config but failed to update scheduler"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": "Updated config but failed to update scheduler"})
|
||||
else:
|
||||
return {"status": "error", "message": "Failed to update bedtime range"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": "Failed to update bedtime range"})
|
||||
|
||||
|
||||
@router.post("/servers/repair")
|
||||
@@ -134,4 +135,4 @@ def repair_server_config():
|
||||
server_manager.repair_config()
|
||||
return {"status": "ok", "message": "Server configuration repaired and saved"}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": f"Failed to repair configuration: {e}"}
|
||||
return JSONResponse(status_code=500, content={"status": "error", "message": f"Failed to repair configuration: {e}"})
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import asyncio
|
||||
from fastapi import APIRouter, Form
|
||||
from fastapi.responses import JSONResponse
|
||||
import discord
|
||||
import globals
|
||||
from utils.dm_logger import dm_logger
|
||||
@@ -29,7 +30,7 @@ async def initiate_voice_call(user_id: str = Form(...), voice_channel_id: str =
|
||||
|
||||
# Check if bot is running
|
||||
if not globals.client or not globals.client.loop or not globals.client.loop.is_running():
|
||||
return {"success": False, "error": "Bot is not running"}
|
||||
return JSONResponse(status_code=503, content={"success": False, "error": "Bot is not running"})
|
||||
|
||||
# Run the voice call setup in the bot's event loop
|
||||
try:
|
||||
@@ -41,7 +42,7 @@ async def initiate_voice_call(user_id: str = Form(...), voice_channel_id: str =
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error(f"Error initiating voice call: {e}", exc_info=True)
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
|
||||
async def _initiate_voice_call_impl(user_id: str, voice_channel_id: str):
|
||||
@@ -57,11 +58,11 @@ async def _initiate_voice_call_impl(user_id: str, voice_channel_id: str):
|
||||
# Get user and channel
|
||||
user = await globals.client.fetch_user(user_id_int)
|
||||
if not user:
|
||||
return {"success": False, "error": "User not found"}
|
||||
return JSONResponse(status_code=404, content={"success": False, "error": "User not found"})
|
||||
|
||||
channel = globals.client.get_channel(channel_id_int)
|
||||
if not channel or not isinstance(channel, discord.VoiceChannel):
|
||||
return {"success": False, "error": "Voice channel not found"}
|
||||
return JSONResponse(status_code=404, content={"success": False, "error": "Voice channel not found"})
|
||||
|
||||
# Get a text channel for voice operations (use first text channel in guild)
|
||||
text_channel = None
|
||||
@@ -71,14 +72,14 @@ async def _initiate_voice_call_impl(user_id: str, voice_channel_id: str):
|
||||
break
|
||||
|
||||
if not text_channel:
|
||||
return {"success": False, "error": "No accessible text channel found"}
|
||||
return JSONResponse(status_code=404, content={"success": False, "error": "No accessible text channel found"})
|
||||
|
||||
# Start containers
|
||||
logger.info("Starting voice containers...")
|
||||
containers_started = await ContainerManager.start_voice_containers()
|
||||
|
||||
if not containers_started:
|
||||
return {"success": False, "error": "Failed to start voice containers"}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": "Failed to start voice containers"})
|
||||
|
||||
# Start voice session
|
||||
logger.info(f"Starting voice session in {channel.name}")
|
||||
@@ -88,7 +89,7 @@ async def _initiate_voice_call_impl(user_id: str, voice_channel_id: str):
|
||||
await session_manager.start_session(channel.guild.id, channel, text_channel)
|
||||
except Exception as e:
|
||||
await ContainerManager.stop_voice_containers()
|
||||
return {"success": False, "error": f"Failed to start voice session: {str(e)}"}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": f"Failed to start voice session: {str(e)}"})
|
||||
|
||||
# Set up voice call tracking (use integer ID)
|
||||
session_manager.active_session.call_user_id = user_id_int
|
||||
@@ -143,7 +144,7 @@ Keep it brief (1-2 sentences). Make it feel personal and enthusiastic!"""
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in voice call implementation: {e}", exc_info=True)
|
||||
return {"success": False, "error": str(e)}
|
||||
return JSONResponse(status_code=500, content={"success": False, "error": str(e)})
|
||||
|
||||
|
||||
async def _voice_call_timeout_handler(voice_session, user: discord.User, channel: discord.VoiceChannel):
|
||||
@@ -171,7 +172,7 @@ async def _voice_call_timeout_handler(voice_session, user: discord.User, channel
|
||||
|
||||
# Log to DM logger
|
||||
dm_logger.log_user_message(user, sent_message, is_bot_message=True)
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
except asyncio.CancelledError:
|
||||
|
||||
Reference in New Issue
Block a user