- Consolidated all .bak.* files from bot/ directory into backups/2025-12-07/ - Moved unused autonomous_wip.py to backups (verified not imported anywhere) - Relocated old .bot.bak.80825/ backup directory into backups/2025-12-07/old-bot-bak-80825/ - Preserved autonomous_v1_legacy.py as it is still actively used by autonomous.py - Created new backups/ directory with date-stamped subdirectory for better organization
170 lines
6.8 KiB
Python
170 lines
6.8 KiB
Python
# utils/moods.py
|
||
|
||
import random
|
||
import discord
|
||
import os
|
||
import asyncio
|
||
from discord.ext import tasks
|
||
import globals
|
||
|
||
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 load_mood_description("neutral")
|
||
|
||
def detect_mood_shift(response_text):
|
||
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():
|
||
if mood == "asleep" and globals.CURRENT_MOOD_NAME != "sleepy":
|
||
print(f"❎ Mood 'asleep' skipped - mood isn't 'sleepy', it's '{globals.CURRENT_MOOD_NAME}'")
|
||
continue # Only allow transition to asleep from sleepy
|
||
|
||
for phrase in phrases:
|
||
if phrase.lower() in response_text.lower():
|
||
print(f"*️⃣ Mood keyword triggered: {phrase}")
|
||
return mood
|
||
return None
|
||
|
||
async def set_sleep_state(sleeping: bool):
|
||
await globals.client.change_presence(status=discord.Status.invisible) if sleeping else await globals.client.change_presence(status=discord.Status.online)
|
||
await nickname_mood_emoji()
|
||
|
||
async def nickname_mood_emoji():
|
||
mood = globals.CURRENT_MOOD_NAME.lower()
|
||
print(f"🔍 Mood is: {mood}")
|
||
emoji = MOOD_EMOJIS.get(mood, "")
|
||
|
||
nickname = f"Hatsune Miku{emoji}"
|
||
|
||
for guild in globals.client.guilds:
|
||
me = guild.get_member(globals.BOT_USER.id)
|
||
if me is not None:
|
||
try:
|
||
await me.edit(nick=nickname)
|
||
print(f"💱 Changed nickname to {nickname}")
|
||
if mood == "asleep":
|
||
await globals.client.change_presence(status=discord.Status.invisible)
|
||
else:
|
||
await globals.client.change_presence(status=discord.Status.online)
|
||
except discord.Forbidden:
|
||
print(f"⚠️ Missing permission to change nickname in guild: {guild.name}")
|
||
except discord.HTTPException as e:
|
||
print(f"⚠️ Failed to change nickname in {guild.name}: {e}")
|
||
|
||
async def clear_angry_mood_after_delay():
|
||
await asyncio.sleep(40 * 60) # 40 minutes
|
||
print("🕒 Angry mood cooldown expired. Miku is calming down to neutral.")
|
||
|
||
globals.CURRENT_MOOD_NAME = "neutral"
|
||
globals.CURRENT_MOOD = load_mood_description("neutral")
|
||
globals.FORCED_ANGRY_UNTIL = None
|
||
|
||
await nickname_mood_emoji()
|
||
|
||
@tasks.loop(hours=1)
|
||
async def rotate_mood():
|
||
try:
|
||
print("🔁 Mood rotation task running...")
|
||
|
||
if globals.FORCED_ANGRY_UNTIL:
|
||
now = datetime.datetime.utcnow()
|
||
if now < globals.FORCED_ANGRY_UNTIL:
|
||
print("⏰ Mood rotation skipped (angry mode).")
|
||
return
|
||
else:
|
||
globals.FORCED_ANGRY_UNTIL = None
|
||
|
||
old_mood_name = globals.CURRENT_MOOD_NAME
|
||
new_mood_name = old_mood_name
|
||
attempts = 0
|
||
|
||
while new_mood_name == old_mood_name and attempts < 5:
|
||
new_mood_name = random.choice(globals.AVAILABLE_MOODS)
|
||
attempts += 1
|
||
|
||
globals.CURRENT_MOOD_NAME = new_mood_name
|
||
globals.CURRENT_MOOD = load_mood_description(new_mood_name)
|
||
|
||
print(f"⏰ Mood auto-rotated to: {new_mood_name}")
|
||
await nickname_mood_emoji()
|
||
|
||
except Exception as e:
|
||
print(f"❌ Exception in rotate_mood: {e}")
|