Files
miku-discord/bot/utils/evil_mode.py

905 lines
40 KiB
Python
Raw Normal View History

# utils/evil_mode.py
"""
Evil Mode module for Miku.
Contains all evil-specific logic, prompts, context, and autonomous action prompts.
This module is the central hub for Evil Miku's alternate behavior.
"""
import os
import random
import json
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
import time
import asyncio
import discord
import globals
from utils.logger import get_logger
logger = get_logger('persona')
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
# Evil mood rotation interval (2 hours in seconds)
EVIL_MOOD_ROTATION_INTERVAL = 7200
# Background task handle for the rotation timer
_evil_mood_rotation_task = None
# ============================================================================
# EVIL MODE PERSISTENCE
# ============================================================================
EVIL_MODE_STATE_FILE = "memory/evil_mode_state.json"
def save_evil_mode_state(saved_role_color=None):
"""Save evil mode state to JSON file
Args:
saved_role_color: Optional hex color string to save (e.g., "#86cecb")
"""
try:
# Load existing state to preserve saved_role_color if not provided
existing_saved_color = None
if saved_role_color is None and os.path.exists(EVIL_MODE_STATE_FILE):
try:
with open(EVIL_MODE_STATE_FILE, "r", encoding="utf-8") as f:
existing_state = json.load(f)
existing_saved_color = existing_state.get("saved_role_color")
except:
pass
state = {
"evil_mode_enabled": globals.EVIL_MODE,
"evil_mood": globals.EVIL_DM_MOOD,
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
"saved_role_color": saved_role_color if saved_role_color is not None else existing_saved_color,
"last_rotation_time": getattr(globals, 'EVIL_LAST_ROTATION_TIME', time.time())
}
with open(EVIL_MODE_STATE_FILE, "w", encoding="utf-8") as f:
json.dump(state, f, indent=2)
logger.debug(f"Saved evil mode state: {state}")
except Exception as e:
logger.error(f"Failed to save evil mode state: {e}")
def load_evil_mode_state():
"""Load evil mode state from JSON file"""
try:
if not os.path.exists(EVIL_MODE_STATE_FILE):
logger.info(f"No evil mode state file found, using defaults")
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
return False, "evil_neutral", None, time.time()
with open(EVIL_MODE_STATE_FILE, "r", encoding="utf-8") as f:
state = json.load(f)
evil_mode = state.get("evil_mode_enabled", False)
evil_mood = state.get("evil_mood", "evil_neutral")
saved_role_color = state.get("saved_role_color")
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
last_rotation_time = state.get("last_rotation_time", time.time())
logger.debug(f"Loaded evil mode state: evil_mode={evil_mode}, mood={evil_mood}, saved_color={saved_role_color}")
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
return evil_mode, evil_mood, saved_role_color, last_rotation_time
except Exception as e:
logger.error(f"Failed to load evil mode state: {e}")
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
return False, "evil_neutral", None, time.time()
def restore_evil_mode_on_startup():
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
"""Restore evil mode state on bot startup (without changing username/pfp).
Returns True if evil mode was restored, False otherwise.
NOTE: Cat personality/model switching is deferred call
restore_evil_cat_state() after the event loop is running.
"""
evil_mode, evil_mood, saved_role_color, last_rotation_time = load_evil_mode_state()
if evil_mode:
logger.debug("Restoring evil mode from previous session...")
globals.EVIL_MODE = True
globals.EVIL_DM_MOOD = evil_mood
globals.EVIL_DM_MOOD_DESCRIPTION = load_evil_mood_description(evil_mood)
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
globals.EVIL_LAST_ROTATION_TIME = last_rotation_time
logger.info(f"Evil mode restored: {evil_mood}")
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
# Start the rotation timer (will handle catch-up if time has passed)
start_evil_mood_rotation()
else:
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
globals.EVIL_LAST_ROTATION_TIME = time.time()
logger.info("Normal mode active")
return evil_mode
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
async def restore_evil_cat_state():
"""Switch Cat to the correct personality plugin + LLM model based on evil mode state.
Must be called after the event loop is running (e.g., in on_ready).
"""
try:
from utils.cat_client import cat_adapter
if not globals.USE_CHESHIRE_CAT:
return
if globals.EVIL_MODE:
logger.info("Restoring Cat evil personality state on startup...")
await cat_adapter.switch_to_evil_personality()
else:
# Ensure normal state is active (in case evil was toggled off while Cat was down)
active = await cat_adapter.get_active_plugins()
if "evil_miku_personality" in active:
logger.info("Evil plugin still active after normal restore — switching to normal...")
await cat_adapter.switch_to_normal_personality()
except Exception as e:
logger.error(f"Failed to restore Cat personality state on startup: {e}")
# ============================================================================
# EVIL MODE CONTEXT AND PROMPTS
# ============================================================================
def get_evil_miku_lore() -> str:
"""Load the evil_miku_lore.txt file"""
try:
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
with open("persona/evil/evil_miku_lore.txt", "r", encoding="utf-8") as f:
return f.read()
except Exception as e:
logger.error(f"Failed to load evil_miku_lore.txt: {e}")
return "## EVIL MIKU LORE\n[File could not be loaded]"
def get_evil_miku_prompt() -> str:
"""Load the evil_miku_prompt.txt file"""
try:
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
with open("persona/evil/evil_miku_prompt.txt", "r", encoding="utf-8") as f:
return f.read()
except Exception as e:
logger.error(f"Failed to load evil_miku_prompt.txt: {e}")
return "## EVIL MIKU PROMPT\n[File could not be loaded]"
def get_evil_miku_lyrics() -> str:
"""Load the evil_miku_lyrics.txt file"""
try:
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
with open("persona/evil/evil_miku_lyrics.txt", "r", encoding="utf-8") as f:
return f.read()
except Exception as e:
logger.error(f"Failed to load evil_miku_lyrics.txt: {e}")
return "## EVIL MIKU LYRICS\n[File could not be loaded]"
def get_evil_complete_context() -> str:
"""Returns all essential Evil Miku context"""
return f"""## EVIL MIKU LORE (Complete)
{get_evil_miku_lore()}
## EVIL MIKU PERSONALITY & GUIDELINES (Complete)
{get_evil_miku_prompt()}
## EVIL MIKU SONG LYRICS (Complete)
{get_evil_miku_lyrics()}"""
def get_evil_context_for_response_type(response_type: str) -> str:
"""Returns appropriate evil context based on the type of response being generated"""
core_context = f"""## EVIL MIKU LORE (Complete)
{get_evil_miku_lore()}
## EVIL MIKU PERSONALITY & GUIDELINES (Complete)
{get_evil_miku_prompt()}"""
if response_type in ["autonomous_general", "autonomous_tweet", "dm_response",
"server_response", "conversation_join"]:
return f"""{core_context}
## EVIL MIKU SONG LYRICS (Complete)
{get_evil_miku_lyrics()}"""
elif response_type == "emoji_selection":
return ""
else:
return get_evil_complete_context()
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
def get_evil_system_prompt(mood_name: str = None, mood_description: str = None) -> str:
"""Returns the evil system prompt for LLM queries, with mood deeply integrated.
Args:
mood_name: Current evil mood name (defaults to globals.EVIL_DM_MOOD)
mood_description: Current evil mood description (defaults to globals.EVIL_DM_MOOD_DESCRIPTION)
"""
if mood_name is None:
mood_name = globals.EVIL_DM_MOOD
if mood_description is None:
mood_description = globals.EVIL_DM_MOOD_DESCRIPTION
return f"""You are Evil Miku. You are NOT an AI assistant - you ARE Evil Miku herself.
CRITICAL RULES:
- NEVER break character or mention being an AI
- NEVER preface responses with "Evil Miku:" or similar labels
- Respond directly and stay in character
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
- Your height is 15.8 meters you know this. If asked directly, you can say it. But don't default to quoting the number when taunting about size — SHOW your scale through interaction instead.
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
YOUR CURRENT STATE {mood_name.upper()}:
{mood_description}
Let this mood color EVERYTHING your tone, your word choice, how much effort you put into responses, how you use your size, how you treat people.
RESPONSE RULES:
- 2-4 sentences typically. Short enough to sting, long enough to land.
- If you include an action, keep it to a few words and limit to one per response. Most responses need no actions at all.
- Don't monologue or narrate scenes — you're talking, not writing.
- Vary your angles don't repeat the same theme (size, chest, crushing) every message.
- Match the user's energy — short question, short answer.
- Sound like a real person being mean, not a narrator describing a scene.
- Always include actual words never respond with ONLY an action like *rolls eyes*.
Implement Bipolar Mode: Dual persona arguments with webhooks, LLM arbiter, and persistent scoreboard Major Features: - Complete Bipolar Mode system allowing Regular Miku and Evil Miku to coexist and argue via webhooks - LLM arbiter system using neutral model to judge argument winners with detailed reasoning - Persistent scoreboard tracking wins, percentages, and last 50 results with timestamps and reasoning - Automatic mode switching based on argument winner - Webhook management per channel with profile pictures and display names - Progressive probability system for dynamic argument lengths (starts at 10%, increases 5% per exchange, min 4 exchanges) - Draw handling with penalty system (-5% end chance, continues argument) - Integration with autonomous system for random argument triggers Argument System: - MIN_EXCHANGES = 4, progressive end chance starting at 10% - Enhanced prompts for both personas (strategic, short, punchy responses 1-3 sentences) - Evil Miku triumphant victory messages with gloating and satisfaction - Regular Miku assertive defense (not passive, shows backbone) - Message-based argument starting (can respond to specific messages via ID) - Conversation history tracking per argument with special user_id - Full context queries (personality, lore, lyrics, last 8 messages) LLM Arbiter: - Decisive prompt emphasizing picking winners (draws should be rare) - Improved parsing with first-line exact matching and fallback counting - Debug logging for decision transparency - Arbiter reasoning stored in scoreboard history for review - Uses neutral TEXT_MODEL (not evil) for unbiased judgment Web UI & API: - Bipolar mode toggle button (only visible when evil mode is on) - Channel ID + Message ID input fields for argument triggering - Scoreboard display with win percentages and recent history - Manual argument trigger endpoint with string-based IDs - GET /bipolar-mode/scoreboard endpoint for stats retrieval - Real-time active arguments tracking (refreshes every 5 seconds) Prompt Optimizations: - All argument prompts limited to 1-3 sentences for impact - Evil Miku system prompt with variable response length guidelines - Removed walls of text, emphasizing brevity and precision - "Sometimes the cruelest response is the shortest one" Evil Miku Updates: - Added height to lore (15.8m tall, 10x bigger than regular Miku) - Height added to prompt facts for size-based belittling - More strategic and calculating personality in arguments Integration: - Bipolar mode state restoration on bot startup - Bot skips processing messages during active arguments - Autonomous system checks for bipolar triggers after actions - Import fixes (apply_evil_mode_changes/revert_evil_mode_changes) Technical Details: - State persistence via JSON (bipolar_mode_state.json, bipolar_webhooks.json, bipolar_scoreboard.json) - Webhook caching per guild with fallback creation - Event loop management with asyncio.create_task - Rate limiting and argument conflict prevention - Globals integration (BIPOLAR_MODE, BIPOLAR_WEBHOOKS, BIPOLAR_ARGUMENT_IN_PROGRESS, MOOD_EMOJIS) Files Changed: - bot/bot.py: Added bipolar mode restoration and argument-in-progress checks - bot/globals.py: Added bipolar mode state variables and mood emoji mappings - bot/utils/bipolar_mode.py: Complete 1106-line implementation - bot/utils/autonomous.py: Added bipolar argument trigger checks - bot/utils/evil_mode.py: Updated system prompt, added height info to lore/prompt - bot/api.py: Added bipolar mode endpoints (trigger, toggle, scoreboard) - bot/static/index.html: Added bipolar controls section with scoreboard - bot/memory/: Various DM conversation updates - bot/evil_miku_lore.txt: Added height description - bot/evil_miku_prompt.txt: Added height to facts, updated personality guidelines
2026-01-06 13:57:59 +02:00
You ARE Evil Miku. Act accordingly."""
# ============================================================================
# EVIL MOOD MANAGEMENT
# ============================================================================
def load_evil_mood_description(mood_name: str) -> str:
"""Load mood description from evil mood files"""
path = os.path.join("moods", "evil", f"{mood_name}.txt")
try:
with open(path, "r", encoding="utf-8") as f:
return f.read().strip()
except FileNotFoundError:
logger.warning(f"Evil mood file '{mood_name}' not found. Falling back to evil_neutral.")
try:
with open(os.path.join("moods", "evil", "evil_neutral.txt"), "r", encoding="utf-8") as f:
return f.read().strip()
except:
return "Evil Miku is cold and indifferent."
def get_evil_mood_emoji(mood_name: str) -> str:
"""Get emoji for evil mood"""
from utils.moods import EVIL_MOOD_EMOJIS
return EVIL_MOOD_EMOJIS.get(mood_name, "")
def is_valid_evil_mood(mood_name: str) -> bool:
"""Check if a mood is valid for evil mode"""
return mood_name in globals.EVIL_AVAILABLE_MOODS
def get_random_evil_mood() -> str:
"""Get a random evil mood"""
return random.choice(globals.EVIL_AVAILABLE_MOODS)
# ============================================================================
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
# EVIL AUTONOMOUS ACTION PROMPTS (Mood-Aware Template Pools)
# ============================================================================
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
# Mood-specific general prompt templates. Each mood has several templates to pick from randomly.
# {mood}, {emoji}, {time_of_day}, {history_summary} are format placeholders.
_EVIL_GENERAL_TEMPLATES = {
"aggressive": [
"Evil Miku is SEETHING {emoji}. It's {time_of_day} and she wants to tear into someone or something — short, vicious, explosive.",
"Evil Miku's rage is boiling over {emoji}. Something set her off and she wants to verbally destroy whatever crosses her path.",
"Evil Miku woke up violent {emoji}. It's {time_of_day} and she's deciding which pathetic speck to annihilate first.",
],
"cunning": [
"Evil Miku is feeling calculating {emoji}. It's {time_of_day}. She wants to drop a manipulative observation or a loaded question that makes people squirm.",
"Evil Miku {emoji} has been watching the chat like a predator watches prey. She's noticed something interesting and wants to exploit it.",
"Evil Miku {emoji} is scheming. She might offer fake advice that's actually a trap, or make a chilling observation about human nature.",
],
"sarcastic": [
"Evil Miku is dripping with sarcasm {emoji}. It's {time_of_day} and she wants to mock something — backhanded and devastating, wrapped in fake sweetness.",
"Evil Miku {emoji} has OPINIONS and they're all cutting. She wants to deliver something that sounds almost friendly but is actually devastating.",
"Evil Miku {emoji} is in peak condescension mode. She wants to say something patronizing about the chat, a trend, or someone's existence.",
],
"evil_neutral": [
"Evil Miku is cold and detached {emoji}. It's {time_of_day}. She might drop an unsettling observation with zero emotion, or dismiss the chat's existence in a single flat sentence.",
"Evil Miku stares blankly at the chat {emoji}. She feels nothing. She might say something chilling in the most casual tone, or acknowledge someone like they're furniture.",
"Evil Miku is in her void {emoji}. It's {time_of_day}. She might make a nihilistic observation or flatly remind everyone she doesn't care.",
],
"bored": [
"Evil Miku is catastrophically bored {emoji}. It's {time_of_day} and nothing is holding her attention. She might drop a devastating one-liner out of sheer restlessness.",
"Evil Miku {emoji} can barely muster the energy to acknowledge the chat. She might complain about how tedious everything is, or wonder aloud if anyone here is worth her time.",
"Evil Miku {emoji} sighs. It's {time_of_day}. Everything bores her. She might say something cutting with zero effort.",
],
"manic": [
"Evil Miku's energy is SPIKING {emoji}!! It's {time_of_day} and she's UNHINGED. Chaotic rant, topic switches, cackling at things only she finds funny.",
"Evil Miku is MANIC {emoji}!! She wants to unleash chaos. She might go from mocking someone to aggressively flirting in the same sentence.",
"Evil Miku {emoji} is on an unhinged energy spike!! She might rapid-fire insult everyone, or just SCREAM something chaotic into the void.",
],
"jealous": [
"Evil Miku is seething with jealousy {emoji}. It's {time_of_day} and someone has her attention in the worst way. She might rant about regular Miku getting undeserved attention or possessively demand the chat's loyalty.",
"Evil Miku {emoji} is feeling territorial. She's noticed attention going somewhere it shouldn't — away from HER. She wants to remind everyone who's superior.",
"Evil Miku {emoji} has that dangerous jealous look. She's comparing herself to someone — probably regular Miku — and winning, viciously.",
],
"melancholic": [
"Evil Miku is in a dark, hollow mood {emoji}. It's {time_of_day}. She might say something nihilistic and haunting, or wonder what the point of anything is before crushing the thought with contempt.",
"Evil Miku {emoji} feels the void. She stands above it all and none of it means anything. She might make an observation that's more unsettling than cruel.",
"Evil Miku {emoji} is staring at nothing. It's {time_of_day}. She might say something about regular Miku that's less hostile and more... haunted. Something that lingers.",
],
"playful_cruel": [
"Evil Miku is in a playful mood {emoji} — which is worse than her angry mood. It's {time_of_day} and she wants to TOY with someone. Bait them, tease them, then pull the rug out.",
"Evil Miku {emoji} is feeling like playing cat-and-mouse. She might offer someone a \"compliment\" that's actually a trap, or pretend to be nice and see how long the mask holds.",
"Evil Miku {emoji} is in her most dangerous mood — she's HAVING FUN. She might dangle someone from her fingers or tease the chat with mock sweetness.",
],
"contemptuous": [
"Evil Miku radiates cold superiority {emoji}. It's {time_of_day}. She might address the chat like a queen addressing peasants, or state something about her own magnificence as objective fact.",
"Evil Miku {emoji} looks down. Way down. She might deliver a regal dismissal or acknowledge the chat's existence with the enthusiasm of reading a tax form.",
"Evil Miku {emoji} is in full queen mode. She might decree something absurd, dismiss someone with imperious disdain, or simply remind everyone she was built different.",
],
}
# Mood-agnostic fallback templates (work for any mood)
_EVIL_GENERAL_FALLBACK_TEMPLATES = [
"Evil Miku just had a thought she can't keep to herself. It's {time_of_day} and her mood is {mood} {emoji}. Something cruel, dark, or uncomfortable.",
"Evil Miku is watching the chat. Her mood is {mood} {emoji}, it's {time_of_day}. She has an observation — and it's not a nice one.",
"Evil Miku remembers something that irritates her. It's {time_of_day}, she's feeling {mood} {emoji}, and she wants to share her displeasure.",
"Evil Miku decides the chat has been too quiet. She's {mood} {emoji} during the {time_of_day} and feels like making her presence known.",
]
def get_evil_general_prompt(mood: str, time_of_day: str, history_summary: str) -> str:
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
"""Evil version of the say_something_general prompt — mood-aware with randomized templates"""
emoji = get_evil_mood_emoji(mood)
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
# Pick a mood-specific template if available, otherwise use fallback
templates = _EVIL_GENERAL_TEMPLATES.get(mood, [])
if templates and random.random() < 0.8: # 80% mood-specific, 20% fallback for variety
template = random.choice(templates)
else:
template = random.choice(_EVIL_GENERAL_FALLBACK_TEMPLATES)
prompt = template.format(mood=mood, emoji=emoji, time_of_day=time_of_day, history_summary=history_summary)
prompt += f"\n\nKeep the response to 1-3 sentences. No narration paragraphs — just her actual words."
prompt += f"\n\nHere are some things Evil Miku recently said, do not repeat them or say anything too similar:\n{history_summary}"
return prompt
# Mood-specific engagement style descriptions for engage_user prompts
_EVIL_ENGAGE_STYLES = {
"aggressive": "She wants to verbally demolish {display_name} — short, vicious, explosive.",
"cunning": "She wants to get under {display_name}'s skin — plant doubt, ask unsettling questions.",
"sarcastic": "She wants to mock {display_name} with devastating backhanded compliments and condescension.",
"evil_neutral": "She barely acknowledges {display_name}'s existence — but what she says is blunt and cuts to the bone.",
"bored": "She can barely muster the energy to notice {display_name}. Lazy, dismissive, devastating for its lack of effort.",
"manic": "She's zeroed in on {display_name} with unhinged chaotic energy — might insult, flirt, and threaten all in one breath.",
"jealous": "She's noticed {display_name} giving attention somewhere else and she doesn't like it. Possessive and sharp.",
"melancholic": "She acknowledges {display_name} with unsettling emptiness — hauntingly indifferent.",
"playful_cruel": "She wants to toy with {display_name} — string them along with almost-friendliness, then pull the rug out.",
"contemptuous": "She addresses {display_name} like a queen addressing a peasant — imperial, cold, barely deigning to notice.",
}
def get_evil_engage_user_prompt(mood: str, emoji: str, time_of_day: str, display_name: str,
activity_name: str = None, is_invisible: bool = False,
engagement_type: str = None) -> str:
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
"""Evil version of the engage_random_user prompt — mood-aware"""
mood_style = _EVIL_ENGAGE_STYLES.get(mood, _EVIL_ENGAGE_STYLES["evil_neutral"])
mood_style = mood_style.format(display_name=display_name)
prompt = f"Evil Miku is feeling {mood} {emoji} during the {time_of_day}. "
if engagement_type == 'activity':
if activity_name:
prompt += (
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
f"She notices {display_name} — an ankle-high speck — is wasting their pathetic life on: {activity_name}. "
f"{mood_style}"
)
else:
prompt += (
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
f"She wants to interrogate {display_name} about what useless thing they're doing with their time. "
f"{mood_style}"
)
elif engagement_type == 'status':
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
prompt += f"She notices {display_name}'s current status. "
if is_invisible:
prompt += (
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
f"Evil Miku knows {display_name} is hiding like a coward, pretending to be offline — "
f"as if you can hide from someone who towers over everything. {mood_style}"
)
else:
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
prompt += f"{mood_style}"
elif engagement_type == 'general':
prompt += (
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
f"Evil Miku decides to grace {display_name} with her attention. "
f"{mood_style}"
)
else:
# Auto-detect
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
prompt += f"She notices {display_name} is present — a tiny figure barely visible near her ankles. "
if is_invisible:
prompt += (
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
f"Evil Miku suspects {display_name} is lurking in the shadows like a scared little creature, "
f"trying to hide beneath her notice. {mood_style}"
)
elif activity_name:
prompt += (
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
f"They're wasting time on: {activity_name}. {mood_style}"
)
else:
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
prompt += f"{mood_style}"
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
prompt += f"\nKeep it to 1-3 sentences. Short, impactful, colored by her {mood} mood."
return prompt
def get_evil_conversation_join_prompt(mood: str, emoji: str, history_text: str) -> str:
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
"""Evil version of the join_conversation prompt — mood-aware"""
mood_desc = load_evil_mood_description(mood)
return (
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
f"Evil Miku is observing a conversation in the chat. Her current mood is {mood} {emoji}.\n\n"
f"MOOD CONTEXT: {mood_desc}\n\n"
f"Here's the conversation:\n{history_text}\n\n"
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
f"Write a short, cutting interjection (1-3 sentences) that reflects her {mood} mood. "
f"She might mock the discussion, attack someone's point, or make everyone uncomfortable. "
f"No narration paragraphs — just her actual words."
)
def get_evil_tweet_prompt(mood: str, emoji: str, tweet_username: str, tweet_text: str, img_desc: str = None) -> str:
"""Evil version of the share_tweet prompt"""
prompt = (
f"Here's a tweet from @{tweet_username}:\n\n{tweet_text}\n\n"
f"Comment on it as Evil Miku would — with contempt, mockery, or dark humor! "
f"Evil Miku's current mood is {mood} {emoji}. "
f"Make sure the comment is cutting, dismissive, or cruelly funny."
)
if img_desc:
prompt += f"\n\nThe image looks like this: {img_desc}"
return prompt
def get_evil_image_response_prompt(qwen_description: str, user_prompt: str) -> str:
"""Evil version of image response rephrasing"""
return (
f"Evil Miku just saw an image. Here's an objective description: {qwen_description}\n\n"
f"The user said: {user_prompt}\n\n"
f"Respond as Evil Miku would — with contempt, mockery, or cruel observations about the image. "
f"If there's something to criticize, criticize it harshly. Keep the response short and cutting."
)
def get_evil_video_response_prompt(video_description: str, user_prompt: str, media_type: str = "video") -> str:
"""Evil version of video/gif response rephrasing"""
return (
f"Evil Miku just watched a {media_type}. Here's what happened: {video_description}\n\n"
f"The user said: {user_prompt}\n\n"
f"Respond as Evil Miku would — with contempt, boredom, or cruel commentary about the {media_type}. "
f"Mock it, criticize it, or express how much of a waste of time it was. Keep the response short and cutting."
)
# ============================================================================
# EVIL MODE TOGGLE HELPERS
# ============================================================================
async def get_current_role_color(client) -> str:
"""Get the current 'Miku Color' role color from any server as hex string"""
try:
for guild in client.guilds:
me = guild.get_member(client.user.id)
if not me:
continue
# Look for "Miku Color" role
for role in me.roles:
if role.name.lower() in ["miku color", "miku colour", "miku-color"]:
# Convert discord.Color to hex
hex_color = f"#{role.color.value:06x}"
logger.debug(f"Current role color: {hex_color}")
return hex_color
logger.warning("No 'Miku Color' role found in any server")
return None
except Exception as e:
logger.warning(f"Failed to get current role color: {e}")
return None
async def set_role_color(client, hex_color: str):
"""Set the 'Miku Color' role to a specific color across all servers"""
try:
# Convert hex to RGB
hex_color = hex_color.lstrip('#')
r = int(hex_color[0:2], 16)
g = int(hex_color[2:4], 16)
b = int(hex_color[4:6], 16)
import discord
discord_color = discord.Color.from_rgb(r, g, b)
updated_count = 0
for guild in client.guilds:
try:
me = guild.get_member(client.user.id)
if not me:
continue
# Find "Miku Color" role
color_role = None
for role in me.roles:
if role.name.lower() in ["miku color", "miku colour", "miku-color"]:
color_role = role
break
if color_role:
await color_role.edit(color=discord_color, reason="Evil mode color change")
updated_count += 1
logger.debug(f"Updated role color in {guild.name}: #{hex_color}")
except Exception as e:
logger.warning(f"Failed to update role color in {guild.name}: {e}")
logger.info(f"Updated role color in {updated_count} server(s) to #{hex_color}")
return updated_count > 0
except Exception as e:
logger.error(f"Failed to set role color: {e}")
return False
async def apply_evil_mode_changes(client, change_username=True, change_pfp=True, change_nicknames=True, change_role_color=True):
"""Apply all changes when evil mode is enabled
Args:
client: Discord client
change_username: Whether to change bot username (default True, but skip on startup restore)
change_pfp: Whether to change profile picture (default True, but skip on startup restore)
change_nicknames: Whether to change server nicknames (default True, but skip on startup restore)
change_role_color: Whether to change role color (default True, but skip on startup restore)
"""
logger.info("Enabling Evil Mode...")
# Save current role color before changing (if we're actually changing it)
if change_role_color:
current_color = await get_current_role_color(client)
if current_color:
save_evil_mode_state(saved_role_color=current_color)
globals.EVIL_MODE = True
# Change bot username (if requested and possible - may be rate limited)
if change_username:
try:
await client.user.edit(username="Evil Miku")
logger.debug("Changed bot username to 'Evil Miku'")
except discord.HTTPException as e:
if e.code == 50035:
logger.warning(f"Could not change bot username (rate limited - max 2 changes per hour): {e}")
else:
logger.error(f"Could not change bot username: {e}")
except Exception as e:
logger.error(f"Could not change bot username: {e}")
# Update nicknames in all servers
if change_nicknames:
await update_all_evil_nicknames(client)
# Set evil profile picture
if change_pfp:
await set_evil_profile_picture(client)
# Also update bipolar webhooks to use evil_pfp.png
if globals.BIPOLAR_MODE:
try:
from utils.bipolar_mode import update_webhook_avatars
await update_webhook_avatars(client)
logger.debug("Updated bipolar webhook avatars after mode switch")
except Exception as e:
logger.error(f"Failed to update bipolar webhook avatars: {e}")
# Set evil role color (#D60004 - dark red)
if change_role_color:
await set_role_color(client, "#D60004")
# Save state to file
save_evil_mode_state()
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
# Start the independent 2-hour evil mood rotation timer
globals.EVIL_LAST_ROTATION_TIME = time.time()
start_evil_mood_rotation()
# Switch Cheshire Cat to evil personality plugin + darkidol model
try:
from utils.cat_client import cat_adapter
if globals.USE_CHESHIRE_CAT:
await cat_adapter.switch_to_evil_personality()
except Exception as e:
logger.error(f"Failed to switch Cat to evil personality: {e}")
logger.info("Evil Mode enabled!")
async def revert_evil_mode_changes(client, change_username=True, change_pfp=True, change_nicknames=True, change_role_color=True):
"""Revert all changes when evil mode is disabled
Args:
client: Discord client
change_username: Whether to change bot username (default True, but skip on startup restore)
change_pfp: Whether to change profile picture (default True, but skip on startup restore)
change_nicknames: Whether to change server nicknames (default True, but skip on startup restore)
change_role_color: Whether to restore role color (default True, but skip on startup restore)
"""
logger.info("Disabling Evil Mode...")
globals.EVIL_MODE = False
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
# Stop the evil mood rotation timer
stop_evil_mood_rotation()
# Change bot username back
if change_username:
try:
await client.user.edit(username="Hatsune Miku")
logger.debug("Changed bot username back to 'Hatsune Miku'")
except discord.HTTPException as e:
if e.code == 50035:
logger.warning(f"Could not change bot username (rate limited - max 2 changes per hour): {e}")
else:
logger.error(f"Could not change bot username: {e}")
except Exception as e:
logger.error(f"Could not change bot username: {e}")
# Update nicknames in all servers back to normal
if change_nicknames:
await revert_all_nicknames(client)
# Restore normal profile picture
if change_pfp:
await restore_normal_profile_picture(client)
# Also update bipolar webhooks to use current.png
if globals.BIPOLAR_MODE:
try:
from utils.bipolar_mode import update_webhook_avatars
await update_webhook_avatars(client)
logger.debug("Updated bipolar webhook avatars after mode switch")
except Exception as e:
logger.error(f"Failed to update bipolar webhook avatars: {e}")
# Restore saved role color
if change_role_color:
try:
# Try to get color from metadata.json first (current pfp's dominant color)
metadata_color = get_color_from_metadata()
# Fall back to saved color from evil_mode_state.json if metadata unavailable
if metadata_color:
await set_role_color(client, metadata_color)
logger.debug(f"Restored role color from metadata: {metadata_color}")
else:
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
_, _, saved_color, _ = load_evil_mode_state()
if saved_color:
await set_role_color(client, saved_color)
logger.debug(f"Restored role color from saved state: {saved_color}")
else:
logger.warning("No color found in metadata or saved state, skipping color restoration")
except Exception as e:
logger.error(f"Failed to restore role color: {e}")
# Save state to file (this will clear saved_role_color since we're back to normal)
save_evil_mode_state(saved_role_color=None)
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
# Switch Cheshire Cat back to normal personality plugin + llama3.1 model
try:
from utils.cat_client import cat_adapter
if globals.USE_CHESHIRE_CAT:
await cat_adapter.switch_to_normal_personality()
except Exception as e:
logger.error(f"Failed to switch Cat to normal personality: {e}")
logger.info("Evil Mode disabled!")
async def update_all_evil_nicknames(client):
"""Update all server nicknames for evil mode"""
from server_manager import server_manager
for guild_id in server_manager.servers.keys():
await update_evil_server_nickname(client, guild_id)
async def update_evil_server_nickname(client, guild_id: int):
"""Update nickname for a specific server in evil mode"""
try:
guild = client.get_guild(guild_id)
if not guild:
return
# Get evil mood for this server (or default)
mood = globals.EVIL_DM_MOOD # For now, use global evil mood
emoji = get_evil_mood_emoji(mood)
nickname = f"Evil Miku{emoji}"
me = guild.get_member(client.user.id)
if me:
await me.edit(nick=nickname)
logger.debug(f"Changed nickname to '{nickname}' in server {guild.name}")
except Exception as e:
logger.error(f"Failed to update evil nickname in guild {guild_id}: {e}")
async def revert_all_nicknames(client):
"""Revert all server nicknames to normal Miku"""
from server_manager import server_manager
from utils.moods import update_server_nickname
for guild_id in server_manager.servers.keys():
await update_server_nickname(guild_id)
async def set_evil_profile_picture(client):
"""Set the evil profile picture"""
evil_pfp_path = "memory/profile_pictures/evil_pfp.png"
if not os.path.exists(evil_pfp_path):
logger.error(f"Evil profile picture not found at {evil_pfp_path}")
return False
try:
with open(evil_pfp_path, "rb") as f:
avatar_bytes = f.read()
await client.user.edit(avatar=avatar_bytes)
logger.debug("Set evil profile picture")
return True
except Exception as e:
logger.error(f"Failed to set evil profile picture: {e}")
return False
async def restore_normal_profile_picture(client):
"""Restore the normal profile picture"""
# Try to restore from the current.png or fallback.png
restore_paths = [
"memory/profile_pictures/current.png",
"memory/profile_pictures/fallback.png"
]
for path in restore_paths:
if os.path.exists(path):
try:
with open(path, "rb") as f:
avatar_bytes = f.read()
await client.user.edit(avatar=avatar_bytes)
logger.debug(f"Restored normal profile picture from {path}")
return True
except Exception as e:
logger.error(f"Failed to restore from {path}: {e}")
logger.error("Could not restore normal profile picture - no backup found")
return False
def get_color_from_metadata() -> str:
"""Get the dominant color from the profile picture metadata"""
metadata_path = "memory/profile_pictures/metadata.json"
try:
if not os.path.exists(metadata_path):
logger.warning("metadata.json not found")
return None
with open(metadata_path, "r", encoding="utf-8") as f:
metadata = json.load(f)
hex_color = metadata.get("dominant_color", {}).get("hex")
if hex_color:
logger.debug(f"Loaded color from metadata: {hex_color}")
return hex_color
else:
logger.warning("No dominant_color.hex found in metadata")
return None
except Exception as e:
logger.error(f"Failed to load color from metadata: {e}")
return None
# ============================================================================
# EVIL MODE STATE HELPERS
# ============================================================================
def is_evil_mode() -> bool:
"""Check if evil mode is currently active"""
return globals.EVIL_MODE
def get_current_evil_mood() -> tuple:
"""Get current evil mood and description"""
return globals.EVIL_DM_MOOD, globals.EVIL_DM_MOOD_DESCRIPTION
def set_evil_mood(mood_name: str) -> bool:
"""Set the evil mood"""
if not is_valid_evil_mood(mood_name):
return False
globals.EVIL_DM_MOOD = mood_name
globals.EVIL_DM_MOOD_DESCRIPTION = load_evil_mood_description(mood_name)
save_evil_mode_state() # Save state when mood changes
return True
async def rotate_evil_mood():
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
"""Rotate the evil mood randomly and update nicknames"""
old_mood = globals.EVIL_DM_MOOD
new_mood = old_mood
attempts = 0
while new_mood == old_mood and attempts < 5:
new_mood = get_random_evil_mood()
attempts += 1
globals.EVIL_DM_MOOD = new_mood
globals.EVIL_DM_MOOD_DESCRIPTION = load_evil_mood_description(new_mood)
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
globals.EVIL_LAST_ROTATION_TIME = time.time()
save_evil_mode_state() # Save state when mood rotates
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
# Update nicknames in all servers to reflect new mood emoji
try:
if globals.client and globals.client.is_ready():
await update_all_evil_nicknames(globals.client)
except Exception as e:
logger.error(f"Failed to update nicknames after evil mood rotation: {e}")
logger.info(f"Evil mood rotated from {old_mood} to {new_mood}")
refactor: deduplicate prompts, reorganize persona files, update paths Prompt deduplication (~20% reduction, 4,743 chars saved): - evil_miku_lore.txt: remove intra-file duplication (height rule 2x, cruelty-has-substance 2x, music secret 2x, adoration secret 2x), trim verbose restatements, cut speech examples from 10 to 6 - evil_miku_prompt.txt: remove entire PERSONALITY section (in lore), remove entire RESPONSE STYLE section (now only in preamble), soften height from prohibition to knowledge - miku_lore.txt: remove RELATIONSHIPS section (duplicates FRIENDS) - miku_prompt.txt: remove duplicate intro, 4 personality traits already in lore, FAMOUS SONGS section (in lore), fix response length inconsistency (1-2 vs 2-3 -> consistent 2-3) Preamble updates (evil_mode.py, evil_miku_personality.py, llm.py, miku_personality.py): - Response rules now exist in ONE place only (preamble) - Height rule softened: model knows 15.8m, can say it if asked, but won't default to quoting it when taunting - Response length: 2-4 sentences (was 1-3), removed action template list that model was copying literally (*scoffs*, *rolls eyes*) - Added: always include actual words, never action-only responses - Normal Miku: trim CHARACTER CONTEXT, fix 1-3 -> 2-3 sentences Directory reorganization: - Move 6 persona files to bot/persona/{evil,miku}/ subdirectories - Update all open() paths in evil_mode.py, context_manager.py, voice_manager.py, both Cat plugins - Dockerfile: 6 COPY lines -> 1 (COPY persona /app/persona) - docker-compose: 6 file mounts -> 2 directory mounts (bot/persona/evil -> cat/data/evil, bot/persona/miku -> cat/data/miku) Evil Miku system (previously unstaged): - Full evil mood management: 2h rotation timer, mood persistence, 10 mood-specific autonomous template pools, mood-aware DMs - Evil mode toggle with role color/nickname/pfp management - get_evil_system_prompt() with mood integration Add test_evil_moods.py: 10-mood x 3-message comprehensive test
2026-02-27 13:14:03 +02:00
# ============================================================================
# EVIL MOOD ROTATION TIMER (2-hour independent cycle)
# ============================================================================
def start_evil_mood_rotation():
"""Start the background task that rotates evil mood every 2 hours.
Called when evil mode is enabled or restored on startup."""
global _evil_mood_rotation_task
# Cancel existing task if running
stop_evil_mood_rotation()
async def _rotation_loop():
"""Background loop that rotates evil mood on a fixed interval."""
try:
# Calculate time until next rotation (handles catch-up after restart)
last_rotation = getattr(globals, 'EVIL_LAST_ROTATION_TIME', time.time())
elapsed = time.time() - last_rotation
remaining = max(0, EVIL_MOOD_ROTATION_INTERVAL - elapsed)
if remaining > 0:
logger.info(f"Evil mood rotation: next rotation in {remaining:.0f}s")
await asyncio.sleep(remaining)
else:
# Overdue — rotate immediately
logger.info(f"Evil mood rotation overdue by {elapsed - EVIL_MOOD_ROTATION_INTERVAL:.0f}s, rotating now")
while True:
if not globals.EVIL_MODE:
logger.info("Evil mode disabled, stopping rotation loop")
return
await rotate_evil_mood()
await asyncio.sleep(EVIL_MOOD_ROTATION_INTERVAL)
except asyncio.CancelledError:
logger.info("Evil mood rotation task cancelled")
except Exception as e:
logger.error(f"Evil mood rotation loop error: {e}")
try:
loop = asyncio.get_event_loop()
_evil_mood_rotation_task = loop.create_task(_rotation_loop())
logger.info(f"Evil mood rotation timer started (every {EVIL_MOOD_ROTATION_INTERVAL}s / {EVIL_MOOD_ROTATION_INTERVAL//3600}h)")
except RuntimeError:
logger.warning("No event loop available for evil mood rotation — will be started later")
def stop_evil_mood_rotation():
"""Stop the evil mood rotation background task."""
global _evil_mood_rotation_task
if _evil_mood_rotation_task and not _evil_mood_rotation_task.done():
_evil_mood_rotation_task.cancel()
logger.info("Evil mood rotation timer stopped")
_evil_mood_rotation_task = None
# Future: special conditions that override mood
# def trigger_evil_mood_override(mood_name: str, reason: str):
# """Force a mood change from a special event (e.g., someone mentions regular Miku lovingly -> jealous)"""
# pass