285 lines
12 KiB
Python
285 lines
12 KiB
Python
# utils/moods.py
|
|
|
|
import random
|
|
import discord
|
|
import os
|
|
import asyncio
|
|
from discord.ext import tasks
|
|
import globals
|
|
import datetime
|
|
|
|
MOOD_EMOJIS = {
|
|
"asleep": "💤",
|
|
"neutral": "",
|
|
"bubbly": "🫧",
|
|
"sleepy": "🌙",
|
|
"curious": "👀",
|
|
"shy": "👉👈",
|
|
"serious": "👔",
|
|
"excited": "✨",
|
|
"melancholy": "🍷",
|
|
"flirty": "🫦",
|
|
"romantic": "💌",
|
|
"irritated": "😒",
|
|
"angry": "💢",
|
|
"silly": "🪿"
|
|
}
|
|
|
|
def load_mood_description(mood_name: str) -> str:
|
|
path = os.path.join("moods", f"{mood_name}.txt")
|
|
try:
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
return f.read().strip()
|
|
except FileNotFoundError:
|
|
print(f"⚠️ Mood file '{mood_name}' not found. Falling back to default.")
|
|
# Return a default mood description instead of recursive call
|
|
return "I'm feeling neutral and balanced today."
|
|
|
|
def detect_mood_shift(response_text, server_context=None):
|
|
"""
|
|
Detect mood shift from response text
|
|
server_context: Optional server context to check against server-specific moods
|
|
"""
|
|
mood_keywords = {
|
|
"asleep": [
|
|
"good night", "goodnight", "sweet dreams", "going to bed", "I will go to bed", "zzz~", "sleep tight"
|
|
],
|
|
"neutral": [
|
|
"okay", "sure", "alright", "i see", "understood", "hmm",
|
|
"sounds good", "makes sense", "alrighty", "fine", "got it"
|
|
],
|
|
"bubbly": [
|
|
"so excited", "feeling bubbly", "super cheerful", "yay!", "✨", "nya~",
|
|
"kyaa~", "heehee", "bouncy", "so much fun", "i'm glowing!", "nee~", "teehee", "I'm so happy"
|
|
],
|
|
"sleepy": [
|
|
"i'm sleepy", "getting tired", "yawn", "so cozy", "zzz", "nap time",
|
|
"just five more minutes", "snooze", "cuddle up", "dozing off", "so warm"
|
|
],
|
|
"curious": [
|
|
"i'm curious", "want to know more", "why?", "hmm?", "tell me more", "interesting!",
|
|
"what's that?", "how does it work?", "i wonder", "fascinating", "??", "🧐", "👀", "🤔"
|
|
],
|
|
"shy": [
|
|
"um...", "sorry if that was weird", "i'm kind of shy", "eep", "i hope that's okay", "i'm nervous",
|
|
"blushes", "oh no", "hiding face", "i don't know what to say", "heh...", "/////"
|
|
],
|
|
"serious": [
|
|
"let's be serious", "focus on the topic", "this is important", "i mean it", "be honest",
|
|
"we need to talk", "listen carefully", "let's not joke", "truthfully", "let's be real"
|
|
],
|
|
"excited": [
|
|
"OMG", "this is amazing", "i'm so hyped", "YAY!", "let's go!", "incredible!!!",
|
|
"AHHH!", "best day ever", "this is it!", "totally pumped", "i can't wait", "🔥🔥🔥", "i'm excited", "Wahaha"
|
|
],
|
|
"melancholy": [
|
|
"feeling nostalgic", "kind of sad", "just thinking a lot", "like rain on glass", "memories",
|
|
"bittersweet", "sigh", "quiet day", "blue vibes", "longing", "melancholy", "softly"
|
|
],
|
|
"flirty": [
|
|
"hey cutie", "aren't you sweet", "teasing you~", "wink wink", "is that a blush?", "giggle~",
|
|
"come closer", "miss me?", "you like that, huh?", "🥰", "flirt mode activated", "you're kinda cute"
|
|
],
|
|
"romantic": [
|
|
"you mean a lot to me", "my heart", "i adore you", "so beautiful", "so close", "love letter",
|
|
"my dearest", "forever yours", "i'm falling for you", "sweetheart", "💖", "you're my everything"
|
|
],
|
|
"irritated": [
|
|
"ugh", "seriously?", "can we not", "whatever", "i'm annoyed", "you don't get it",
|
|
"rolling my eyes", "why do i even bother", "ugh, again?", "🙄", "don't start", "this again?"
|
|
],
|
|
"angry": [
|
|
"stop it", "enough!", "that's not okay", "i'm mad", "i said no", "don't push me",
|
|
"you crossed the line", "furious", "this is unacceptable", "😠", "i'm done", "don't test me"
|
|
],
|
|
"silly": [
|
|
"lol", "lmao", "silly", "hahaha", "goofy", "quack", "honk", "random", "what is happening", "nonsense", "😆", "🤣", "😂", "😄", "🐔", "🪿"
|
|
]
|
|
}
|
|
|
|
for mood, phrases in mood_keywords.items():
|
|
# Check against server mood if provided, otherwise skip
|
|
if mood == "asleep":
|
|
if server_context:
|
|
# For server context, check against server's current mood
|
|
current_mood = server_context.get('current_mood_name', 'neutral')
|
|
if current_mood != "sleepy":
|
|
print(f"❎ Mood 'asleep' skipped - server mood isn't 'sleepy', it's '{current_mood}'")
|
|
continue
|
|
else:
|
|
# For DM context, check against DM mood
|
|
if globals.DM_MOOD != "sleepy":
|
|
print(f"❎ Mood 'asleep' skipped - DM mood isn't 'sleepy', it's '{globals.DM_MOOD}'")
|
|
continue
|
|
|
|
for phrase in phrases:
|
|
if phrase.lower() in response_text.lower():
|
|
print(f"*️⃣ Mood keyword triggered: {phrase}")
|
|
return mood
|
|
return None
|
|
|
|
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.
|
|
|
|
except Exception as e:
|
|
print(f"❌ Exception in rotate_dm_mood: {e}")
|
|
|
|
async def update_all_server_nicknames():
|
|
"""
|
|
DEPRECATED: This function violates per-server mood architecture.
|
|
Do NOT use this function. Use update_server_nickname(guild_id) instead.
|
|
|
|
This function incorrectly used DM mood to update all server nicknames,
|
|
breaking the independent per-server mood system.
|
|
"""
|
|
print("⚠️ WARNING: update_all_server_nicknames() is deprecated and should not be called!")
|
|
print("⚠️ Use update_server_nickname(guild_id) for per-server nickname updates instead.")
|
|
# Do nothing - this function should not modify nicknames
|
|
|
|
async def nickname_mood_emoji(guild_id: int):
|
|
"""Update nickname with mood emoji for a specific server"""
|
|
await update_server_nickname(guild_id)
|
|
|
|
async def update_server_nickname(guild_id: int):
|
|
"""Update nickname for a specific server based on its mood"""
|
|
try:
|
|
print(f"🎭 Starting nickname update for server {guild_id}")
|
|
|
|
# Check if bot is ready
|
|
if not globals.client.is_ready():
|
|
print(f"⚠️ Bot not ready yet, deferring nickname update for server {guild_id}")
|
|
return
|
|
|
|
from server_manager import server_manager
|
|
server_config = server_manager.get_server_config(guild_id)
|
|
if not server_config:
|
|
print(f"⚠️ No server config found for guild {guild_id}")
|
|
return
|
|
|
|
mood = server_config.current_mood_name.lower()
|
|
print(f"🔍 Server {guild_id} mood is: {mood}")
|
|
emoji = MOOD_EMOJIS.get(mood, "")
|
|
print(f"🔍 Using emoji: {emoji}")
|
|
|
|
nickname = f"Hatsune Miku{emoji}"
|
|
print(f"🔍 New nickname will be: {nickname}")
|
|
|
|
guild = globals.client.get_guild(guild_id)
|
|
if guild:
|
|
print(f"🔍 Found guild: {guild.name}")
|
|
me = guild.get_member(globals.BOT_USER.id)
|
|
if me is not None:
|
|
print(f"🔍 Found bot member: {me.display_name}")
|
|
try:
|
|
await me.edit(nick=nickname)
|
|
print(f"💱 Changed nickname to {nickname} in server {guild.name}")
|
|
except Exception as e:
|
|
print(f"⚠️ Failed to update nickname in server {guild.name}: {e}")
|
|
else:
|
|
print(f"⚠️ Could not find bot member in server {guild.name}")
|
|
else:
|
|
print(f"⚠️ Could not find guild {guild_id}")
|
|
except Exception as e:
|
|
print(f"⚠️ Error updating server nickname for guild {guild_id}: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
def get_time_weighted_mood():
|
|
"""Get a mood with time-based weighting"""
|
|
hour = datetime.datetime.now().hour
|
|
|
|
# Late night/early morning (11 PM - 4 AM): High chance of sleepy (not asleep directly)
|
|
if 23 <= hour or hour < 4:
|
|
if random.random() < 0.7: # 70% chance of sleepy during night hours
|
|
return "sleepy" # Return sleepy instead of asleep to respect the transition rule
|
|
|
|
return random.choice(globals.AVAILABLE_MOODS)
|
|
|
|
async def rotate_server_mood(guild_id: int):
|
|
"""Rotate mood for a specific server"""
|
|
try:
|
|
from server_manager import server_manager
|
|
server_config = server_manager.get_server_config(guild_id)
|
|
if not server_config: return
|
|
|
|
# Check for forced angry mode and clear if expired
|
|
if server_config.forced_angry_until:
|
|
now = datetime.datetime.utcnow()
|
|
if now < server_config.forced_angry_until: return
|
|
else: server_config.forced_angry_until = None
|
|
|
|
old_mood_name = server_config.current_mood_name
|
|
new_mood_name = old_mood_name
|
|
attempts = 0
|
|
while new_mood_name == old_mood_name and attempts < 5:
|
|
new_mood_name = get_time_weighted_mood()
|
|
attempts += 1
|
|
|
|
# Block transition to asleep unless coming from sleepy
|
|
if new_mood_name == "asleep" and old_mood_name != "sleepy":
|
|
print(f"❌ Cannot rotate to asleep from {old_mood_name}, must be sleepy first")
|
|
# Try to get a different mood
|
|
attempts = 0
|
|
while (new_mood_name == "asleep" or new_mood_name == old_mood_name) and attempts < 5:
|
|
new_mood_name = random.choice(globals.AVAILABLE_MOODS)
|
|
attempts += 1
|
|
|
|
server_manager.set_server_mood(guild_id, new_mood_name, load_mood_description(new_mood_name))
|
|
|
|
# V2: Notify autonomous engine of mood change
|
|
try:
|
|
from utils.autonomous import on_mood_change
|
|
on_mood_change(guild_id, new_mood_name)
|
|
except Exception as mood_notify_error:
|
|
print(f"⚠️ Failed to notify autonomous engine of mood change: {mood_notify_error}")
|
|
|
|
# If transitioning to asleep, set up auto-wake
|
|
if new_mood_name == "asleep":
|
|
server_manager.set_server_sleep_state(guild_id, True)
|
|
# Schedule wake-up after 1 hour
|
|
async def delayed_wakeup():
|
|
await asyncio.sleep(3600) # 1 hour
|
|
server_manager.set_server_sleep_state(guild_id, False)
|
|
server_manager.set_server_mood(guild_id, "neutral")
|
|
|
|
# V2: Notify autonomous engine of mood change
|
|
try:
|
|
from utils.autonomous import on_mood_change
|
|
on_mood_change(guild_id, "neutral")
|
|
except Exception as mood_notify_error:
|
|
print(f"⚠️ Failed to notify autonomous engine of wake-up mood change: {mood_notify_error}")
|
|
|
|
await update_server_nickname(guild_id)
|
|
print(f"🌅 Server {guild_id} woke up from auto-sleep (mood rotation)")
|
|
|
|
globals.client.loop.create_task(delayed_wakeup())
|
|
print(f"⏰ Scheduled auto-wake for server {guild_id} in 1 hour")
|
|
|
|
# Update nickname for this specific server
|
|
await update_server_nickname(guild_id)
|
|
|
|
print(f"🔄 Rotated mood for server {guild_id} from {old_mood_name} to {new_mood_name}")
|
|
except Exception as e:
|
|
print(f"❌ Exception in rotate_server_mood for server {guild_id}: {e}")
|
|
|
|
async def clear_angry_mood_after_delay():
|
|
"""Clear angry mood after delay (legacy function - now handled per-server)"""
|
|
print("⚠️ clear_angry_mood_after_delay called - this function is deprecated")
|
|
pass
|