refactor: split api.py monolith into 19 route modules (Phase B)
Split 3,598-line api.py into thin orchestrator (128 lines) + 19 route
modules in bot/routes/:
core.py (7 routes), mood.py (10), language.py (3), evil_mode.py (6),
bipolar_mode.py (9), gpu.py (2), bot_actions.py (4), autonomous.py (13),
profile_picture.py (26), manual_send.py (3), servers.py (6),
figurines.py (5), dms.py (18), image_generation.py (4), chat.py (1),
config.py (7), logging_config.py (9), voice.py (3), memory.py (10)
All 146 routes verified present via test_route_split.py (149 tests).
21/21 regression tests (test_config_state.py) pass.
Monolith backup: bot/api_monolith_backup.py (revert: cp it to api.py).
2026-04-15 11:38:14 +03:00
""" Mood management routes: DM mood, per-server mood, available moods, test mood. """
from fastapi import APIRouter
2026-04-15 15:43:18 +03:00
from fastapi . responses import JSONResponse
refactor: split api.py monolith into 19 route modules (Phase B)
Split 3,598-line api.py into thin orchestrator (128 lines) + 19 route
modules in bot/routes/:
core.py (7 routes), mood.py (10), language.py (3), evil_mode.py (6),
bipolar_mode.py (9), gpu.py (2), bot_actions.py (4), autonomous.py (13),
profile_picture.py (26), manual_send.py (3), servers.py (6),
figurines.py (5), dms.py (18), image_generation.py (4), chat.py (1),
config.py (7), logging_config.py (9), voice.py (3), memory.py (10)
All 146 routes verified present via test_route_split.py (149 tests).
21/21 regression tests (test_config_state.py) pass.
Monolith backup: bot/api_monolith_backup.py (revert: cp it to api.py).
2026-04-15 11:38:14 +03:00
import globals
from server_manager import server_manager
from routes . models import MoodSetRequest
from utils . logger import get_logger
logger = get_logger ( ' api ' )
router = APIRouter ( )
# ========== DM Mood ==========
@router.get ( " /mood " )
def get_current_mood ( ) :
return { " mood " : globals . DM_MOOD , " description " : globals . DM_MOOD_DESCRIPTION }
@router.post ( " /mood " )
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 :
2026-04-15 15:43:18 +03:00
return JSONResponse ( status_code = 400 , content = { " status " : " error " , " message " : f " Mood ' { data . mood } ' not recognized. Available moods: { ' , ' . join ( MOOD_EMOJIS . keys ( ) ) } " } )
refactor: split api.py monolith into 19 route modules (Phase B)
Split 3,598-line api.py into thin orchestrator (128 lines) + 19 route
modules in bot/routes/:
core.py (7 routes), mood.py (10), language.py (3), evil_mode.py (6),
bipolar_mode.py (9), gpu.py (2), bot_actions.py (4), autonomous.py (13),
profile_picture.py (26), manual_send.py (3), servers.py (6),
figurines.py (5), dms.py (18), image_generation.py (4), chat.py (1),
config.py (7), logging_config.py (9), voice.py (3), memory.py (10)
All 146 routes verified present via test_route_split.py (149 tests).
21/21 regression tests (test_config_state.py) pass.
Monolith backup: bot/api_monolith_backup.py (revert: cp it to api.py).
2026-04-15 11:38:14 +03:00
# Update DM mood (DMs don't have nicknames, so no nickname update needed)
globals . DM_MOOD = data . mood
from utils . moods import load_mood_description
globals . DM_MOOD_DESCRIPTION = load_mood_description ( data . mood )
# Persist to config manager
try :
from config_manager import config_manager
config_manager . set ( " runtime.mood.dm_mood " , data . mood , persist = True )
except Exception as e :
logger . warning ( f " Failed to persist mood to config: { e } " )
return { " status " : " ok " , " new_mood " : data . mood }
@router.post ( " /mood/reset " )
async def reset_mood_endpoint ( ) :
# Reset DM mood to neutral (DMs don't have nicknames, so no nickname update needed)
globals . DM_MOOD = " neutral "
from utils . moods import load_mood_description
globals . DM_MOOD_DESCRIPTION = load_mood_description ( " neutral " )
# Persist to config manager
try :
from config_manager import config_manager
config_manager . set ( " runtime.mood.dm_mood " , " neutral " , persist = True )
except Exception as e :
logger . warning ( f " Failed to persist mood reset to config: { e } " )
return { " status " : " ok " , " new_mood " : " neutral " }
@router.post ( " /mood/calm " )
def calm_miku_endpoint ( ) :
# Calm DM mood to neutral (DMs don't have nicknames, so no nickname update needed)
globals . DM_MOOD = " neutral "
from utils . moods import load_mood_description
globals . DM_MOOD_DESCRIPTION = load_mood_description ( " neutral " )
# Persist to config manager
try :
from config_manager import config_manager
config_manager . set ( " runtime.mood.dm_mood " , " neutral " , persist = True )
except Exception as e :
logger . warning ( f " Failed to persist mood calm to config: { e } " )
return { " status " : " ok " , " message " : " Miku has been calmed down " }
# ========== Per-Server Mood ==========
@router.get ( " /servers/ {guild_id} /mood " )
def get_server_mood ( guild_id : int ) :
""" Get current mood for a specific server """
mood_name , mood_description = server_manager . get_server_mood ( guild_id )
return {
" guild_id " : guild_id ,
" mood " : mood_name ,
" description " : mood_description
}
@router.post ( " /servers/ {guild_id} /mood " )
async def set_server_mood_endpoint ( guild_id : int , data : MoodSetRequest ) :
""" Set mood for a specific server """
# Check if server exists
if guild_id not in server_manager . servers :
logger . warning ( f " Server { guild_id } not found in server_manager.servers " )
2026-04-15 15:43:18 +03:00
return JSONResponse ( status_code = 404 , content = { " status " : " error " , " message " : " Server not found " } )
refactor: split api.py monolith into 19 route modules (Phase B)
Split 3,598-line api.py into thin orchestrator (128 lines) + 19 route
modules in bot/routes/:
core.py (7 routes), mood.py (10), language.py (3), evil_mode.py (6),
bipolar_mode.py (9), gpu.py (2), bot_actions.py (4), autonomous.py (13),
profile_picture.py (26), manual_send.py (3), servers.py (6),
figurines.py (5), dms.py (18), image_generation.py (4), chat.py (1),
config.py (7), logging_config.py (9), voice.py (3), memory.py (10)
All 146 routes verified present via test_route_split.py (149 tests).
21/21 regression tests (test_config_state.py) pass.
Monolith backup: bot/api_monolith_backup.py (revert: cp it to api.py).
2026-04-15 11:38:14 +03:00
# 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 ( ) ) } " )
2026-04-15 15:43:18 +03:00
return JSONResponse ( status_code = 400 , content = { " status " : " error " , " message " : f " Mood ' { data . mood } ' not recognized. Available moods: { ' , ' . join ( MOOD_EMOJIS . keys ( ) ) } " } )
refactor: split api.py monolith into 19 route modules (Phase B)
Split 3,598-line api.py into thin orchestrator (128 lines) + 19 route
modules in bot/routes/:
core.py (7 routes), mood.py (10), language.py (3), evil_mode.py (6),
bipolar_mode.py (9), gpu.py (2), bot_actions.py (4), autonomous.py (13),
profile_picture.py (26), manual_send.py (3), servers.py (6),
figurines.py (5), dms.py (18), image_generation.py (4), chat.py (1),
config.py (7), logging_config.py (9), voice.py (3), memory.py (10)
All 146 routes verified present via test_route_split.py (149 tests).
21/21 regression tests (test_config_state.py) pass.
Monolith backup: bot/api_monolith_backup.py (revert: cp it to api.py).
2026-04-15 11:38:14 +03:00
success = server_manager . set_server_mood ( guild_id , data . mood )
logger . debug ( f " Server mood set result: { success } " )
if success :
# Update the nickname for this server
from utils . moods import update_server_nickname
logger . debug ( f " Updating nickname for server { guild_id } " )
globals . client . loop . create_task ( update_server_nickname ( guild_id ) )
return { " status " : " ok " , " new_mood " : data . mood , " guild_id " : guild_id }
logger . warning ( f " set_server_mood returned False for unknown reason " )
2026-04-15 15:43:18 +03:00
return JSONResponse ( status_code = 500 , content = { " status " : " error " , " message " : " Failed to set server mood " } )
refactor: split api.py monolith into 19 route modules (Phase B)
Split 3,598-line api.py into thin orchestrator (128 lines) + 19 route
modules in bot/routes/:
core.py (7 routes), mood.py (10), language.py (3), evil_mode.py (6),
bipolar_mode.py (9), gpu.py (2), bot_actions.py (4), autonomous.py (13),
profile_picture.py (26), manual_send.py (3), servers.py (6),
figurines.py (5), dms.py (18), image_generation.py (4), chat.py (1),
config.py (7), logging_config.py (9), voice.py (3), memory.py (10)
All 146 routes verified present via test_route_split.py (149 tests).
21/21 regression tests (test_config_state.py) pass.
Monolith backup: bot/api_monolith_backup.py (revert: cp it to api.py).
2026-04-15 11:38:14 +03:00
@router.post ( " /servers/ {guild_id} /mood/reset " )
async def reset_server_mood_endpoint ( guild_id : int ) :
""" Reset mood to neutral for a specific server """
logger . debug ( f " Resetting mood for server { guild_id } to neutral " )
# Check if server exists
if guild_id not in server_manager . servers :
logger . warning ( f " Server { guild_id } not found in server_manager.servers " )
2026-04-15 15:43:18 +03:00
return JSONResponse ( status_code = 404 , content = { " status " : " error " , " message " : " Server not found " } )
refactor: split api.py monolith into 19 route modules (Phase B)
Split 3,598-line api.py into thin orchestrator (128 lines) + 19 route
modules in bot/routes/:
core.py (7 routes), mood.py (10), language.py (3), evil_mode.py (6),
bipolar_mode.py (9), gpu.py (2), bot_actions.py (4), autonomous.py (13),
profile_picture.py (26), manual_send.py (3), servers.py (6),
figurines.py (5), dms.py (18), image_generation.py (4), chat.py (1),
config.py (7), logging_config.py (9), voice.py (3), memory.py (10)
All 146 routes verified present via test_route_split.py (149 tests).
21/21 regression tests (test_config_state.py) pass.
Monolith backup: bot/api_monolith_backup.py (revert: cp it to api.py).
2026-04-15 11:38:14 +03:00
logger . debug ( f " Server validation passed, calling set_server_mood " )
success = server_manager . set_server_mood ( guild_id , " neutral " )
logger . debug ( f " Server mood reset result: { success } " )
if success :
# Update the nickname for this server
from utils . moods import update_server_nickname
logger . debug ( f " Updating nickname for server { guild_id } " )
globals . client . loop . create_task ( update_server_nickname ( guild_id ) )
return { " status " : " ok " , " new_mood " : " neutral " , " guild_id " : guild_id }
logger . warning ( f " set_server_mood returned False for unknown reason " )
2026-04-15 15:43:18 +03:00
return JSONResponse ( status_code = 500 , content = { " status " : " error " , " message " : " Failed to reset server mood " } )
refactor: split api.py monolith into 19 route modules (Phase B)
Split 3,598-line api.py into thin orchestrator (128 lines) + 19 route
modules in bot/routes/:
core.py (7 routes), mood.py (10), language.py (3), evil_mode.py (6),
bipolar_mode.py (9), gpu.py (2), bot_actions.py (4), autonomous.py (13),
profile_picture.py (26), manual_send.py (3), servers.py (6),
figurines.py (5), dms.py (18), image_generation.py (4), chat.py (1),
config.py (7), logging_config.py (9), voice.py (3), memory.py (10)
All 146 routes verified present via test_route_split.py (149 tests).
21/21 regression tests (test_config_state.py) pass.
Monolith backup: bot/api_monolith_backup.py (revert: cp it to api.py).
2026-04-15 11:38:14 +03:00
@router.get ( " /servers/ {guild_id} /mood/state " )
def get_server_mood_state ( guild_id : int ) :
""" Get complete mood state for a specific server """
mood_state = server_manager . get_server_mood_state ( guild_id )
if mood_state :
return { " status " : " ok " , " guild_id " : guild_id , " mood_state " : mood_state }
2026-04-15 15:43:18 +03:00
return JSONResponse ( status_code = 404 , content = { " status " : " error " , " message " : " Server not found " } )
refactor: split api.py monolith into 19 route modules (Phase B)
Split 3,598-line api.py into thin orchestrator (128 lines) + 19 route
modules in bot/routes/:
core.py (7 routes), mood.py (10), language.py (3), evil_mode.py (6),
bipolar_mode.py (9), gpu.py (2), bot_actions.py (4), autonomous.py (13),
profile_picture.py (26), manual_send.py (3), servers.py (6),
figurines.py (5), dms.py (18), image_generation.py (4), chat.py (1),
config.py (7), logging_config.py (9), voice.py (3), memory.py (10)
All 146 routes verified present via test_route_split.py (149 tests).
21/21 regression tests (test_config_state.py) pass.
Monolith backup: bot/api_monolith_backup.py (revert: cp it to api.py).
2026-04-15 11:38:14 +03:00
# ========== Misc Mood ==========
@router.get ( " /moods/available " )
def get_available_moods ( ) :
""" Get list of all available moods """
from utils . moods import MOOD_EMOJIS
return { " moods " : list ( MOOD_EMOJIS . keys ( ) ) }
@router.post ( " /test/mood/ {guild_id} " )
async def test_mood_change ( guild_id : int , data : MoodSetRequest ) :
""" Test endpoint for debugging mood changes """
logger . debug ( f " TEST: Testing mood change for server { guild_id } to { data . mood } " )
# Check if server exists
if guild_id not in server_manager . servers :
2026-04-15 15:43:18 +03:00
return JSONResponse ( status_code = 404 , content = { " status " : " error " , " message " : f " Server { guild_id } not found " } )
refactor: split api.py monolith into 19 route modules (Phase B)
Split 3,598-line api.py into thin orchestrator (128 lines) + 19 route
modules in bot/routes/:
core.py (7 routes), mood.py (10), language.py (3), evil_mode.py (6),
bipolar_mode.py (9), gpu.py (2), bot_actions.py (4), autonomous.py (13),
profile_picture.py (26), manual_send.py (3), servers.py (6),
figurines.py (5), dms.py (18), image_generation.py (4), chat.py (1),
config.py (7), logging_config.py (9), voice.py (3), memory.py (10)
All 146 routes verified present via test_route_split.py (149 tests).
21/21 regression tests (test_config_state.py) pass.
Monolith backup: bot/api_monolith_backup.py (revert: cp it to api.py).
2026-04-15 11:38:14 +03:00
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 ' } " )
# Try to set mood
success = server_manager . set_server_mood ( guild_id , data . mood )
logger . debug ( f " TEST: Mood set result: { success } " )
if success :
# Try to update nickname
from utils . moods import update_server_nickname
logger . debug ( f " TEST: Attempting nickname update... " )
try :
await update_server_nickname ( guild_id )
logger . debug ( f " TEST: Nickname update completed " )
except Exception as e :
logger . error ( f " TEST: Nickname update failed: { e } " )
import traceback
traceback . print_exc ( )
return { " status " : " ok " , " message " : f " Test mood change completed " , " success " : success }
2026-04-15 15:43:18 +03:00
return JSONResponse ( status_code = 500 , content = { " status " : " error " , " message " : " Mood change failed " } )