Files
miku-discord/MOOD_SYSTEM_ANALYSIS.md

398 lines
13 KiB
Markdown

# Mood System Analysis & Issues
## Overview
After examining the Miku Discord bot's mood, mood rotation, and emoji nickname system, I've identified several critical issues that explain why they don't function correctly.
---
## System Architecture
### 1. **Dual Mood System**
The bot has TWO independent mood systems:
- **DM Mood**: Global mood for all direct messages (`globals.DM_MOOD`)
- **Server Mood**: Per-server mood tracked in `ServerConfig` objects
### 2. **Mood Rotation**
- **DM Mood**: Rotates every 2 hours (via `rotate_dm_mood()`)
- **Server Mood**: Rotates every 1 hour per server (via `rotate_server_mood()`)
### 3. **Nickname System**
Nicknames show mood emojis via the `MOOD_EMOJIS` dictionary in `utils/moods.py`
---
## 🔴 CRITICAL ISSUES FOUND
### Issue #1: Nickname Update Logic Conflict
**Location**: `utils/moods.py` lines 143-163
**Problem**: The `update_all_server_nicknames()` function uses **DM mood** to update **all server** nicknames:
```python
async def update_all_server_nicknames():
"""Update nickname for all servers to show current DM mood"""
try:
mood = globals.DM_MOOD.lower() # ❌ Uses DM mood
print(f"🔍 DM mood is: {mood}")
emoji = MOOD_EMOJIS.get(mood, "")
nickname = f"Hatsune Miku{emoji}"
print(f"🔍 New nickname will be: {nickname}")
for guild in globals.client.guilds: # ❌ Updates ALL servers
me = guild.get_member(globals.BOT_USER.id)
if me is not None:
try:
await me.edit(nick=nickname)
```
**Impact**:
- Server nicknames show DM mood instead of their own server mood
- All servers get the same nickname despite having independent moods
- The per-server mood system is functionally broken for nicknames
**Expected Behavior**: Each server should display its own mood emoji based on `server_config.current_mood_name`
---
### Issue #2: DM Mood Rotation Updates Server Nicknames
**Location**: `utils/moods.py` lines 121-142
**Problem**: The `rotate_dm_mood()` function is called by the DM mood scheduler but doesn't update any nicknames:
```python
async def rotate_dm_mood():
"""Rotate DM mood automatically (no keyword triggers)"""
try:
old_mood = globals.DM_MOOD
new_mood = old_mood
attempts = 0
while new_mood == old_mood and attempts < 5:
new_mood = random.choice(globals.AVAILABLE_MOODS)
attempts += 1
globals.DM_MOOD = new_mood
globals.DM_MOOD_DESCRIPTION = load_mood_description(new_mood)
print(f"🔄 DM mood rotated from {old_mood} to {new_mood}")
# Note: We don't update server nicknames here because servers have their own independent moods.
# DM mood only affects direct messages to users.
```
**Impact**:
- Comment says "servers have their own independent moods"
- But `update_all_server_nicknames()` uses DM mood anyway
- Inconsistent design philosophy
---
### Issue #3: Incorrect Nickname Function Called After Server Mood Rotation
**Location**: `server_manager.py` line 647
**Problem**: After rotating a server's mood, the system calls `update_server_nickname()` which is correct, BUT there's confusion in the codebase:
```python
async def rotate_server_mood(guild_id: int):
"""Rotate mood for a specific server"""
try:
# ... mood rotation logic ...
server_manager.set_server_mood(guild_id, new_mood_name, load_mood_description(new_mood_name))
# Update nickname for this specific server
await update_server_nickname(guild_id) # ✅ Correct function
print(f"🔄 Rotated mood for server {guild_id} from {old_mood_name} to {new_mood_name}")
```
**Analysis**: This part is actually correct, but...
---
### Issue #4: `nickname_mood_emoji()` Function Ambiguity
**Location**: `utils/moods.py` lines 165-171
**Problem**: This function can call either server-specific OR all-server update:
```python
async def nickname_mood_emoji(guild_id: int = None):
"""Update nickname with mood emoji for a specific server or all servers"""
if guild_id is not None:
# Update nickname for specific server
await update_server_nickname(guild_id)
else:
# Update nickname for all servers (using DM mood)
await update_all_server_nicknames()
```
**Impact**:
- If called without `guild_id`, it overwrites all server nicknames with DM mood
- Creates confusion about which mood system is active
- This function might be called incorrectly from various places
---
### Issue #5: Mood Detection in bot.py May Not Trigger Nickname Updates
**Location**: `bot.py` lines 469-512
**Problem**: When mood is auto-detected from keywords in messages, nickname updates are scheduled but may race with the rotation system:
```python
if detected and detected != server_config.current_mood_name:
print(f"🔄 Auto mood detection for server {message.guild.name}: {server_config.current_mood_name} -> {detected}")
# Block direct transitions to asleep unless from sleepy
if detected == "asleep" and server_config.current_mood_name != "sleepy":
print("❌ Ignoring asleep mood; server wasn't sleepy before.")
else:
# Update server mood
server_manager.set_server_mood(message.guild.id, detected)
# Update nickname for this server
from utils.moods import update_server_nickname
globals.client.loop.create_task(update_server_nickname(message.guild.id))
```
**Analysis**: This part looks correct, but creates a task that may conflict with hourly rotation.
---
### Issue #6: No Emoji for "neutral" Mood
**Location**: `utils/moods.py` line 16
```python
MOOD_EMOJIS = {
"asleep": "💤",
"neutral": "", # ❌ Empty string
"bubbly": "🫧",
# ... etc
}
```
**Impact**: When bot is in neutral mood, nickname becomes just "Hatsune Miku" with no emoji, making it hard to tell if the system is working.
**Recommendation**: Add an emoji like "🎤" or "✨" for neutral mood.
---
## 🔧 ROOT CAUSE ANALYSIS
The core problem is **architectural confusion** between two competing systems:
1. **Original Design Intent**: Servers should have independent moods with per-server nicknames
2. **Broken Implementation**: `update_all_server_nicknames()` uses global DM mood for all servers
3. **Mixed Signals**: Comments say servers are independent, but code says otherwise
---
## 🎯 RECOMMENDED FIXES
### Fix #1: Remove `update_all_server_nicknames()` Entirely
This function violates the per-server mood architecture. It should never be called.
**Action**:
- Delete or deprecate `update_all_server_nicknames()`
- Ensure all nickname updates go through `update_server_nickname(guild_id)`
---
### Fix #2: Update `nickname_mood_emoji()` to Only Support Server-Specific Updates
**Current Code**:
```python
async def nickname_mood_emoji(guild_id: int = None):
if guild_id is not None:
await update_server_nickname(guild_id)
else:
await update_all_server_nicknames() # ❌ Remove this
```
**Fixed Code**:
```python
async def nickname_mood_emoji(guild_id: int):
"""Update nickname with mood emoji for a specific server"""
await update_server_nickname(guild_id)
```
---
### Fix #3: Add Neutral Mood Emoji
**Current**:
```python
"neutral": "",
```
**Fixed**:
```python
"neutral": "🎤", # Or ✨, 🎵, etc.
```
---
### Fix #4: Audit All Calls to Nickname Functions
Search for any calls to:
- `update_all_server_nicknames()` - should not exist
- `nickname_mood_emoji()` - must always pass guild_id
**FOUND ISSUES**:
#### ❌ `api.py` - THREE broken endpoints:
1. **Line 113-114**: `/mood` endpoint sets DM mood but updates ALL server nicknames
2. **Line 126-127**: `/mood/reset` endpoint sets DM mood but updates ALL server nicknames
3. **Line 139-140**: `/mood/calm` endpoint sets DM mood but updates ALL server nicknames
**Code**:
```python
@app.post("/mood")
async def set_mood_endpoint(data: MoodSetRequest):
# Update DM mood
globals.DM_MOOD = data.mood
globals.DM_MOOD_DESCRIPTION = load_mood_description(data.mood)
# ❌ WRONG: Updates ALL servers with DM mood
from utils.moods import update_all_server_nicknames
globals.client.loop.create_task(update_all_server_nicknames())
```
**Impact**:
- API endpoints that change DM mood incorrectly change ALL server nicknames
- This is the smoking gun! When you use the API/dashboard to change mood, it breaks server nicknames
- Confirms that DM mood and server moods should be completely independent
**Fix**:
- Remove nickname update calls from these endpoints
- DM mood should NOT affect server nicknames at all
- If you want to update server nicknames, use the per-server endpoints
#### ✅ `api.py` also has CORRECT per-server endpoints (line 145+):
- `/servers/{guild_id}/mood` - Gets server mood (correct)
- Likely has POST endpoints for setting server mood (need to verify)
**Locations checked**:
-`bot.py` - Uses `update_server_nickname(guild_id)` correctly
-`server_manager.py` - Rotation calls correct function
-`api.py` - DM mood endpoints incorrectly update all servers
- ⚠️ `command_router.py` - Imports `nickname_mood_emoji` but doesn't seem to use it
---
### Fix #5: Add Logging to Verify Mood/Nickname Sync
Add debug logging to `update_server_nickname()` to track:
- What mood the server thinks it has
- What emoji is being applied
- Whether the Discord API call succeeds
---
### Fix #6: Consider Removing DM Mood Entirely (Optional)
**Question**: Should DMs have their own mood system?
**Current Design**:
- DMs use `globals.DM_MOOD`
- DM mood rotates every 2 hours
- DM mood does NOT affect nicknames (correctly)
**Recommendation**: This is fine IF the nickname system stops using it. The current separation is logical.
---
## 📋 VERIFICATION CHECKLIST
After fixes, verify:
1. [ ] Each server maintains its own mood independently
2. [ ] Server nicknames update when server mood changes
3. [ ] Hourly mood rotation updates the correct server's nickname
4. [ ] Keyword mood detection updates the correct server's nickname
5. [ ] DM mood changes do NOT affect any server nicknames
6. [ ] Neutral mood shows an emoji (or document that empty is intentional)
7. [ ] No race conditions between rotation and manual mood changes
---
## 🧪 TESTING PROCEDURE
1. **Test Server Mood Independence**:
- Join multiple servers
- Manually trigger mood change in one server
- Verify other servers maintain their moods
2. **Test Nickname Updates**:
- Trigger mood rotation
- Check nickname shows correct emoji
- Compare against `MOOD_EMOJIS` dictionary
3. **Test DM Mood Isolation**:
- Send DM to bot
- Wait for DM mood rotation
- Verify server nicknames don't change
4. **Test Mood Detection**:
- Send message with mood keywords
- Verify mood changes and nickname updates
- Check logs for correct mood detection
---
## 📊 SUMMARY
| Component | Status | Issue |
|-----------|--------|-------|
| Server Mood System | ⚠️ **Partially Broken** | Nicknames use wrong mood when API called |
| DM Mood System | ✅ **Working** | Isolated correctly in bot logic |
| Mood Rotation | ✅ **Working** | Logic is correct |
| Nickname Updates | 🔴 **BROKEN** | API endpoints use DM mood for servers |
| Mood Detection | ✅ **Working** | Keywords trigger correctly |
| Emoji System | ⚠️ **Minor Issue** | Neutral has no emoji |
| Per-Server API | ✅ **Working** | `/servers/{guild_id}/mood` endpoints correct |
| Global DM API | 🔴 **BROKEN** | `/mood` endpoints incorrectly update servers |
**KEY FINDING**: The bug is primarily in the **API layer**, not the core bot logic!
When you (or a dashboard) calls:
- `/mood` endpoint → Changes DM mood → Updates ALL server nicknames ❌
- `/mood/reset` endpoint → Resets DM mood → Updates ALL server nicknames ❌
- `/mood/calm` endpoint → Calms DM mood → Updates ALL server nicknames ❌
This explains why it "doesn't seem like they function right" - the API is sabotaging the per-server system!
---
## 🚀 PRIORITY FIX ORDER
1. **🔥 CRITICAL**: Fix API endpoints in `api.py` - Remove `update_all_server_nicknames()` calls from:
- `/mood` endpoint (line 113-114)
- `/mood/reset` endpoint (line 126-127)
- `/mood/calm` endpoint (line 139-140)
2. **HIGH**: Deprecate `update_all_server_nicknames()` function in `utils/moods.py`
- Add deprecation warning
- Eventually delete it entirely
3. **HIGH**: Fix `nickname_mood_emoji()` to require `guild_id`
- Remove the `guild_id=None` default
- Remove the DM mood branch
4. **MEDIUM**: Add neutral mood emoji - user experience
5. **LOW**: Add debug logging - future maintenance
**IMMEDIATE ACTION**: Fix the three API endpoints. This is the root cause of the visible bug.
---
## 📝 CODE LOCATIONS REFERENCE
- **Mood definitions**: `utils/moods.py`
- **Server config**: `server_manager.py`
- **Bot message handling**: `bot.py`
- **LLM mood usage**: `utils/llm.py`
- **Global DM mood**: `globals.py`
- **Mood files**: `moods/*.txt`