feat: Implement comprehensive non-hierarchical logging system
- Created new logging infrastructure with per-component filtering - Added 6 log levels: DEBUG, INFO, API, WARNING, ERROR, CRITICAL - Implemented non-hierarchical level control (any combination can be enabled) - Migrated 917 print() statements across 31 files to structured logging - Created web UI (system.html) for runtime configuration with dark theme - Added global level controls to enable/disable levels across all components - Added timestamp format control (off/time/date/datetime options) - Implemented log rotation (10MB per file, 5 backups) - Added API endpoints for dynamic log configuration - Configured HTTP request logging with filtering via api.requests component - Intercepted APScheduler logs with proper formatting - Fixed persistence paths to use /app/memory for Docker volume compatibility - Fixed checkbox display bug in web UI (enabled_levels now properly shown) - Changed System Settings button to open in same tab instead of new window Components: bot, api, api.requests, autonomous, persona, vision, llm, conversation, mood, dm, scheduled, gpu, media, server, commands, sentiment, core, apscheduler All settings persist across container restarts via JSON config.
This commit is contained in:
@@ -23,6 +23,9 @@ from utils.image_handling import (
|
||||
convert_gif_to_mp4
|
||||
)
|
||||
from utils.sleep_responses import SLEEP_RESPONSES
|
||||
from utils.logger import get_logger
|
||||
|
||||
logger = get_logger('autonomous')
|
||||
|
||||
# Server-specific memory storage
|
||||
_server_autonomous_messages = {} # guild_id -> rotating buffer of last general messages
|
||||
@@ -48,7 +51,7 @@ def save_autonomous_config(config):
|
||||
def setup_autonomous_speaking():
|
||||
"""Setup autonomous speaking for all configured servers"""
|
||||
# This is now handled by the server manager
|
||||
print("🤖 Autonomous Miku setup delegated to server manager!")
|
||||
logger.debug("Autonomous Miku setup delegated to server manager!")
|
||||
|
||||
async def miku_autonomous_tick_for_server(guild_id: int, action_type="general", force=False, force_action=None):
|
||||
"""Run autonomous behavior for a specific server"""
|
||||
@@ -71,12 +74,12 @@ async def miku_say_something_general_for_server(guild_id: int):
|
||||
"""Miku says something general in a specific server"""
|
||||
server_config = server_manager.get_server_config(guild_id)
|
||||
if not server_config:
|
||||
print(f"⚠️ No config found for server {guild_id}")
|
||||
logger.warning(f"No config found for server {guild_id}")
|
||||
return
|
||||
|
||||
channel = globals.client.get_channel(server_config.autonomous_channel_id)
|
||||
if not channel:
|
||||
print(f"⚠️ Autonomous channel not found for server {guild_id}")
|
||||
logger.warning(f"Autonomous channel not found for server {guild_id}")
|
||||
return
|
||||
|
||||
# Check if evil mode is active
|
||||
@@ -123,7 +126,7 @@ async def miku_say_something_general_for_server(guild_id: int):
|
||||
message = await query_llama(prompt, user_id=f"miku-autonomous-{guild_id}", guild_id=guild_id, response_type="autonomous_general")
|
||||
if not is_too_similar(message, _server_autonomous_messages[guild_id]):
|
||||
break
|
||||
print("🔁 Response was too similar to past messages, retrying...")
|
||||
logger.debug("Response was too similar to past messages, retrying...")
|
||||
|
||||
try:
|
||||
await channel.send(message)
|
||||
@@ -131,9 +134,9 @@ async def miku_say_something_general_for_server(guild_id: int):
|
||||
if len(_server_autonomous_messages[guild_id]) > MAX_HISTORY:
|
||||
_server_autonomous_messages[guild_id].pop(0)
|
||||
character_name = "Evil Miku" if evil_mode else "Miku"
|
||||
print(f"💬 {character_name} said something general in #{channel.name} (Server: {server_config.guild_name})")
|
||||
logger.info(f"{character_name} said something general in #{channel.name} (Server: {server_config.guild_name})")
|
||||
except Exception as e:
|
||||
print(f"⚠️ Failed to send autonomous message: {e}")
|
||||
logger.error(f"Failed to send autonomous message: {e}")
|
||||
|
||||
async def miku_engage_random_user_for_server(guild_id: int, user_id: str = None, engagement_type: str = None):
|
||||
"""Miku engages a random user in a specific server
|
||||
@@ -145,17 +148,17 @@ async def miku_engage_random_user_for_server(guild_id: int, user_id: str = None,
|
||||
"""
|
||||
server_config = server_manager.get_server_config(guild_id)
|
||||
if not server_config:
|
||||
print(f"⚠️ No config found for server {guild_id}")
|
||||
logger.warning(f"No config found for server {guild_id}")
|
||||
return
|
||||
|
||||
guild = globals.client.get_guild(guild_id)
|
||||
if not guild:
|
||||
print(f"⚠️ Guild {guild_id} not found.")
|
||||
logger.warning(f"Guild {guild_id} not found.")
|
||||
return
|
||||
|
||||
channel = globals.client.get_channel(server_config.autonomous_channel_id)
|
||||
if not channel:
|
||||
print(f"⚠️ Autonomous channel not found for server {guild_id}")
|
||||
logger.warning(f"Autonomous channel not found for server {guild_id}")
|
||||
return
|
||||
|
||||
# Get target user
|
||||
@@ -164,14 +167,14 @@ async def miku_engage_random_user_for_server(guild_id: int, user_id: str = None,
|
||||
try:
|
||||
target = guild.get_member(int(user_id))
|
||||
if not target:
|
||||
print(f"⚠️ User {user_id} not found in server {guild_id}")
|
||||
logger.warning(f"User {user_id} not found in server {guild_id}")
|
||||
return
|
||||
if target.bot:
|
||||
print(f"⚠️ Cannot engage bot user {user_id}")
|
||||
logger.warning(f"Cannot engage bot user {user_id}")
|
||||
return
|
||||
print(f"🎯 Targeting specific user: {target.display_name} (ID: {user_id})")
|
||||
logger.info(f"Targeting specific user: {target.display_name} (ID: {user_id})")
|
||||
except ValueError:
|
||||
print(f"⚠️ Invalid user ID: {user_id}")
|
||||
logger.warning(f"Invalid user ID: {user_id}")
|
||||
return
|
||||
else:
|
||||
# Pick random user
|
||||
@@ -181,11 +184,11 @@ async def miku_engage_random_user_for_server(guild_id: int, user_id: str = None,
|
||||
]
|
||||
|
||||
if not members:
|
||||
print(f"😴 No available members to talk to in server {guild_id}.")
|
||||
logger.warning(f"No available members to talk to in server {guild_id}.")
|
||||
return
|
||||
|
||||
target = random.choice(members)
|
||||
print(f"🎲 Randomly selected user: {target.display_name}")
|
||||
logger.info(f"Randomly selected user: {target.display_name}")
|
||||
|
||||
time_of_day = get_time_of_day()
|
||||
|
||||
@@ -196,7 +199,7 @@ async def miku_engage_random_user_for_server(guild_id: int, user_id: str = None,
|
||||
now = time.time()
|
||||
last_time = _server_user_engagements[guild_id].get(target.id, 0)
|
||||
if now - last_time < 43200: # 12 hours in seconds
|
||||
print(f"⏱️ Recently engaged {target.display_name} in server {guild_id}, switching to general message.")
|
||||
logger.info(f"Recently engaged {target.display_name} in server {guild_id}, switching to general message.")
|
||||
await miku_say_something_general_for_server(guild_id)
|
||||
return
|
||||
|
||||
@@ -286,7 +289,7 @@ async def miku_engage_random_user_for_server(guild_id: int, user_id: str = None,
|
||||
)
|
||||
|
||||
if engagement_type:
|
||||
print(f"💬 Engagement type: {engagement_type}")
|
||||
logger.debug(f"Engagement type: {engagement_type}")
|
||||
|
||||
try:
|
||||
# Use consistent user_id for engaging users to enable conversation history
|
||||
@@ -294,9 +297,9 @@ async def miku_engage_random_user_for_server(guild_id: int, user_id: str = None,
|
||||
await channel.send(f"{target.mention} {message}")
|
||||
_server_user_engagements[guild_id][target.id] = time.time()
|
||||
character_name = "Evil Miku" if evil_mode else "Miku"
|
||||
print(f"👤 {character_name} engaged {display_name} in server {server_config.guild_name}")
|
||||
logger.info(f"{character_name} engaged {display_name} in server {server_config.guild_name}")
|
||||
except Exception as e:
|
||||
print(f"⚠️ Failed to engage user: {e}")
|
||||
logger.error(f"Failed to engage user: {e}")
|
||||
|
||||
async def miku_detect_and_join_conversation_for_server(guild_id: int, force: bool = False):
|
||||
"""Miku detects and joins conversations in a specific server
|
||||
@@ -305,30 +308,30 @@ async def miku_detect_and_join_conversation_for_server(guild_id: int, force: boo
|
||||
guild_id: The server ID
|
||||
force: If True, bypass activity checks and random chance (for manual triggers)
|
||||
"""
|
||||
print(f"🔍 [Join Conv] Called for server {guild_id} (force={force})")
|
||||
logger.debug(f"[Join Conv] Called for server {guild_id} (force={force})")
|
||||
server_config = server_manager.get_server_config(guild_id)
|
||||
if not server_config:
|
||||
print(f"⚠️ No config found for server {guild_id}")
|
||||
logger.warning(f"No config found for server {guild_id}")
|
||||
return
|
||||
|
||||
channel = globals.client.get_channel(server_config.autonomous_channel_id)
|
||||
if not isinstance(channel, TextChannel):
|
||||
print(f"⚠️ Autonomous channel is invalid or not found for server {guild_id}")
|
||||
logger.warning(f"Autonomous channel is invalid or not found for server {guild_id}")
|
||||
return
|
||||
|
||||
# Fetch last 20 messages (for filtering)
|
||||
try:
|
||||
messages = [msg async for msg in channel.history(limit=20)]
|
||||
print(f"📜 [Join Conv] Fetched {len(messages)} messages from history")
|
||||
logger.debug(f"[Join Conv] Fetched {len(messages)} messages from history")
|
||||
except Exception as e:
|
||||
print(f"⚠️ Failed to fetch channel history for server {guild_id}: {e}")
|
||||
logger.error(f"Failed to fetch channel history for server {guild_id}: {e}")
|
||||
return
|
||||
|
||||
# Filter messages based on force mode
|
||||
if force:
|
||||
# When forced, use messages from real users (no time limit) - but limit to last 10
|
||||
recent_msgs = [msg for msg in messages if not msg.author.bot][:10]
|
||||
print(f"📊 [Join Conv] Force mode: Using last {len(recent_msgs)} messages from users (no time limit)")
|
||||
logger.debug(f"[Join Conv] Force mode: Using last {len(recent_msgs)} messages from users (no time limit)")
|
||||
else:
|
||||
# Normal mode: Filter to messages in last 10 minutes from real users (not bots)
|
||||
recent_msgs = [
|
||||
@@ -336,23 +339,23 @@ async def miku_detect_and_join_conversation_for_server(guild_id: int, force: boo
|
||||
if not msg.author.bot
|
||||
and (datetime.now(msg.created_at.tzinfo) - msg.created_at).total_seconds() < 600
|
||||
]
|
||||
print(f"📊 [Join Conv] Found {len(recent_msgs)} recent messages from users (last 10 min)")
|
||||
logger.debug(f"[Join Conv] Found {len(recent_msgs)} recent messages from users (last 10 min)")
|
||||
|
||||
user_ids = set(msg.author.id for msg in recent_msgs)
|
||||
|
||||
if not force:
|
||||
if len(recent_msgs) < 5 or len(user_ids) < 2:
|
||||
# Not enough activity
|
||||
print(f"⚠️ [Join Conv] Not enough activity: {len(recent_msgs)} messages, {len(user_ids)} users (need 5+ messages, 2+ users)")
|
||||
logger.debug(f"[Join Conv] Not enough activity: {len(recent_msgs)} messages, {len(user_ids)} users (need 5+ messages, 2+ users)")
|
||||
return
|
||||
|
||||
if random.random() > 0.5:
|
||||
print(f"🎲 [Join Conv] Random chance failed (50% chance)")
|
||||
logger.debug(f"[Join Conv] Random chance failed (50% chance)")
|
||||
return # 50% chance to engage
|
||||
else:
|
||||
print(f"✅ [Join Conv] Force mode - bypassing activity checks")
|
||||
logger.debug(f"[Join Conv] Force mode - bypassing activity checks")
|
||||
if len(recent_msgs) < 1:
|
||||
print(f"⚠️ [Join Conv] No messages found in channel history")
|
||||
logger.warning(f"[Join Conv] No messages found in channel history")
|
||||
return
|
||||
|
||||
# Use last 10 messages for context (oldest to newest)
|
||||
@@ -386,27 +389,27 @@ async def miku_detect_and_join_conversation_for_server(guild_id: int, force: boo
|
||||
reply = await query_llama(prompt, user_id=f"miku-conversation-{guild_id}", guild_id=guild_id, response_type="conversation_join")
|
||||
await channel.send(reply)
|
||||
character_name = "Evil Miku" if evil_mode else "Miku"
|
||||
print(f"💬 {character_name} joined an ongoing conversation in server {server_config.guild_name}")
|
||||
logger.info(f"{character_name} joined an ongoing conversation in server {server_config.guild_name}")
|
||||
except Exception as e:
|
||||
print(f"⚠️ Failed to interject in conversation: {e}")
|
||||
logger.error(f"Failed to interject in conversation: {e}")
|
||||
|
||||
async def share_miku_tweet_for_server(guild_id: int):
|
||||
"""Share a Miku tweet in a specific server"""
|
||||
server_config = server_manager.get_server_config(guild_id)
|
||||
if not server_config:
|
||||
print(f"⚠️ No config found for server {guild_id}")
|
||||
logger.warning(f"No config found for server {guild_id}")
|
||||
return
|
||||
|
||||
channel = globals.client.get_channel(server_config.autonomous_channel_id)
|
||||
tweets = await fetch_miku_tweets(limit=5)
|
||||
if not tweets:
|
||||
print(f"📭 No good tweets found for server {guild_id}")
|
||||
logger.warning(f"No good tweets found for server {guild_id}")
|
||||
return
|
||||
|
||||
fresh_tweets = [t for t in tweets if t["url"] not in LAST_SENT_TWEETS]
|
||||
|
||||
if not fresh_tweets:
|
||||
print(f"⚠️ All fetched tweets were recently sent in server {guild_id}. Reusing tweets.")
|
||||
logger.warning(f"All fetched tweets were recently sent in server {guild_id}. Reusing tweets.")
|
||||
fresh_tweets = tweets
|
||||
|
||||
tweet = random.choice(fresh_tweets)
|
||||
@@ -454,12 +457,12 @@ async def handle_custom_prompt_for_server(guild_id: int, user_prompt: str):
|
||||
"""Handle custom prompt for a specific server"""
|
||||
server_config = server_manager.get_server_config(guild_id)
|
||||
if not server_config:
|
||||
print(f"⚠️ No config found for server {guild_id}")
|
||||
logger.warning(f"No config found for server {guild_id}")
|
||||
return False
|
||||
|
||||
channel = globals.client.get_channel(server_config.autonomous_channel_id)
|
||||
if not channel:
|
||||
print(f"⚠️ Autonomous channel not found for server {guild_id}")
|
||||
logger.warning(f"Autonomous channel not found for server {guild_id}")
|
||||
return False
|
||||
|
||||
mood = server_config.current_mood_name
|
||||
@@ -478,7 +481,7 @@ async def handle_custom_prompt_for_server(guild_id: int, user_prompt: str):
|
||||
# Use consistent user_id for manual prompts to enable conversation history
|
||||
message = await query_llama(prompt, user_id=f"miku-manual-{guild_id}", guild_id=guild_id, response_type="autonomous_general")
|
||||
await channel.send(message)
|
||||
print(f"🎤 Miku responded to custom prompt in server {server_config.guild_name}")
|
||||
logger.info(f"Miku responded to custom prompt in server {server_config.guild_name}")
|
||||
|
||||
# Add to server-specific message history
|
||||
if guild_id not in _server_autonomous_messages:
|
||||
@@ -489,7 +492,7 @@ async def handle_custom_prompt_for_server(guild_id: int, user_prompt: str):
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to send custom autonomous message: {e}")
|
||||
logger.error(f"Failed to send custom autonomous message: {e}")
|
||||
return False
|
||||
|
||||
# Legacy functions for backward compatibility - these now delegate to server-specific versions
|
||||
@@ -542,7 +545,7 @@ def load_last_sent_tweets():
|
||||
with open(LAST_SENT_TWEETS_FILE, "r", encoding="utf-8") as f:
|
||||
LAST_SENT_TWEETS = json.load(f)
|
||||
except Exception as e:
|
||||
print(f"⚠️ Failed to load last sent tweets: {e}")
|
||||
logger.error(f"Failed to load last sent tweets: {e}")
|
||||
LAST_SENT_TWEETS = []
|
||||
else:
|
||||
LAST_SENT_TWEETS = []
|
||||
@@ -552,7 +555,7 @@ def save_last_sent_tweets():
|
||||
with open(LAST_SENT_TWEETS_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(LAST_SENT_TWEETS, f)
|
||||
except Exception as e:
|
||||
print(f"⚠️ Failed to save last sent tweets: {e}")
|
||||
logger.error(f"Failed to save last sent tweets: {e}")
|
||||
|
||||
def get_time_of_day():
|
||||
hour = datetime.now().hour + 3
|
||||
@@ -602,7 +605,7 @@ async def _analyze_message_media(message):
|
||||
try:
|
||||
# Handle images
|
||||
if any(attachment.filename.lower().endswith(ext) for ext in [".jpg", ".jpeg", ".png", ".webp"]):
|
||||
print(f" 📸 Analyzing image for reaction: {attachment.filename}")
|
||||
logger.debug(f" Analyzing image for reaction: {attachment.filename}")
|
||||
base64_img = await download_and_encode_image(attachment.url)
|
||||
if base64_img:
|
||||
description = await analyze_image_with_qwen(base64_img)
|
||||
@@ -612,7 +615,7 @@ async def _analyze_message_media(message):
|
||||
elif any(attachment.filename.lower().endswith(ext) for ext in [".gif", ".mp4", ".webm", ".mov"]):
|
||||
is_gif = attachment.filename.lower().endswith('.gif')
|
||||
media_type = "GIF" if is_gif else "video"
|
||||
print(f" 🎬 Analyzing {media_type} for reaction: {attachment.filename}")
|
||||
logger.debug(f" Analyzing {media_type} for reaction: {attachment.filename}")
|
||||
|
||||
# Download media
|
||||
media_bytes_b64 = await download_and_encode_media(attachment.url)
|
||||
@@ -635,7 +638,7 @@ async def _analyze_message_media(message):
|
||||
return f"[{media_type}: {description}]"
|
||||
|
||||
except Exception as e:
|
||||
print(f" ⚠️ Error analyzing media for reaction: {e}")
|
||||
logger.warning(f" Error analyzing media for reaction: {e}")
|
||||
continue
|
||||
|
||||
return None
|
||||
@@ -650,25 +653,25 @@ async def miku_autonomous_reaction_for_server(guild_id: int, force_message=None,
|
||||
"""
|
||||
# 50% chance to proceed (unless forced or with a specific message)
|
||||
if not force and force_message is None and random.random() > 0.5:
|
||||
print(f"🎲 Autonomous reaction skipped for server {guild_id} (50% chance)")
|
||||
logger.debug(f"Autonomous reaction skipped for server {guild_id} (50% chance)")
|
||||
return
|
||||
|
||||
server_config = server_manager.get_server_config(guild_id)
|
||||
if not server_config:
|
||||
print(f"⚠️ No config found for server {guild_id}")
|
||||
logger.warning(f"No config found for server {guild_id}")
|
||||
return
|
||||
|
||||
server_name = server_config.guild_name
|
||||
|
||||
# Don't react if asleep
|
||||
if server_config.current_mood_name == "asleep" or server_config.is_sleeping:
|
||||
print(f"💤 [{server_name}] Miku is asleep, skipping autonomous reaction")
|
||||
logger.info(f"[{server_name}] Miku is asleep, skipping autonomous reaction")
|
||||
return
|
||||
|
||||
# Get the autonomous channel
|
||||
channel = globals.client.get_channel(server_config.autonomous_channel_id)
|
||||
if not channel:
|
||||
print(f"⚠️ [{server_name}] Autonomous channel not found")
|
||||
logger.warning(f"[{server_name}] Autonomous channel not found")
|
||||
return
|
||||
|
||||
try:
|
||||
@@ -677,9 +680,9 @@ async def miku_autonomous_reaction_for_server(guild_id: int, force_message=None,
|
||||
target_message = force_message
|
||||
# Check if we've already reacted to this message
|
||||
if target_message.id in _reacted_message_ids:
|
||||
print(f"⏭️ [{server_name}] Already reacted to message {target_message.id}, skipping")
|
||||
logger.debug(f"[{server_name}] Already reacted to message {target_message.id}, skipping")
|
||||
return
|
||||
print(f"🎯 [{server_name}] Reacting to new message from {target_message.author.display_name}")
|
||||
logger.info(f"[{server_name}] Reacting to new message from {target_message.author.display_name}")
|
||||
else:
|
||||
# Fetch recent messages (last 50 messages to get more candidates)
|
||||
messages = []
|
||||
@@ -697,14 +700,14 @@ async def miku_autonomous_reaction_for_server(guild_id: int, force_message=None,
|
||||
messages.append(message)
|
||||
|
||||
if not messages:
|
||||
print(f"📭 [{server_name}] No recent unreacted messages to react to")
|
||||
logger.debug(f"[{server_name}] No recent unreacted messages to react to")
|
||||
return
|
||||
|
||||
# Pick a random message from the recent ones
|
||||
target_message = random.choice(messages)
|
||||
|
||||
# Analyze any media in the message
|
||||
print(f"🔍 [{server_name}] Analyzing message for reaction from {target_message.author.display_name}")
|
||||
logger.debug(f"[{server_name}] Analyzing message for reaction from {target_message.author.display_name}")
|
||||
media_description = await _analyze_message_media(target_message)
|
||||
|
||||
# Build message content with media description if present
|
||||
@@ -764,7 +767,7 @@ async def miku_autonomous_reaction_for_server(guild_id: int, force_message=None,
|
||||
emoji = emojis[0]
|
||||
else:
|
||||
# No emoji found in response, use fallback
|
||||
print(f"⚠️ [{server_name}] LLM response contained no emoji: '{original_response[:50]}' - using fallback")
|
||||
logger.warning(f"[{server_name}] LLM response contained no emoji: '{original_response[:50]}' - using fallback")
|
||||
emoji = "💙"
|
||||
|
||||
# Final validation: try adding the reaction
|
||||
@@ -772,7 +775,7 @@ async def miku_autonomous_reaction_for_server(guild_id: int, force_message=None,
|
||||
await target_message.add_reaction(emoji)
|
||||
except discord.HTTPException as e:
|
||||
if "Unknown Emoji" in str(e):
|
||||
print(f"❌ [{server_name}] Invalid emoji from LLM: '{original_response[:50]}' - using fallback")
|
||||
logger.warning(f"[{server_name}] Invalid emoji from LLM: '{original_response[:50]}' - using fallback")
|
||||
emoji = "💙"
|
||||
await target_message.add_reaction(emoji)
|
||||
else:
|
||||
@@ -789,14 +792,14 @@ async def miku_autonomous_reaction_for_server(guild_id: int, force_message=None,
|
||||
for msg_id in ids_to_remove:
|
||||
_reacted_message_ids.discard(msg_id)
|
||||
|
||||
print(f"✅ [{server_name}] Autonomous reaction: Added {emoji} to message from {target_message.author.display_name}")
|
||||
logger.info(f"[{server_name}] Autonomous reaction: Added {emoji} to message from {target_message.author.display_name}")
|
||||
|
||||
except discord.Forbidden:
|
||||
print(f"❌ [{server_name}] Missing permissions to add reactions")
|
||||
logger.error(f"[{server_name}] Missing permissions to add reactions")
|
||||
except discord.HTTPException as e:
|
||||
print(f"❌ [{server_name}] Failed to add reaction: {e}")
|
||||
logger.error(f"[{server_name}] Failed to add reaction: {e}")
|
||||
except Exception as e:
|
||||
print(f"⚠️ [{server_name}] Error in autonomous reaction: {e}")
|
||||
logger.error(f"[{server_name}] Error in autonomous reaction: {e}")
|
||||
|
||||
async def miku_autonomous_reaction(force=False):
|
||||
"""Legacy function - run autonomous reactions for all servers
|
||||
@@ -816,14 +819,14 @@ async def miku_autonomous_reaction_for_dm(user_id: int, force_message=None):
|
||||
"""
|
||||
# 50% chance to proceed (unless forced with a specific message)
|
||||
if force_message is None and random.random() > 0.5:
|
||||
print(f"🎲 DM reaction skipped for user {user_id} (50% chance)")
|
||||
logger.debug(f"DM reaction skipped for user {user_id} (50% chance)")
|
||||
return
|
||||
|
||||
# Get the user object
|
||||
try:
|
||||
user = await globals.client.fetch_user(user_id)
|
||||
if not user:
|
||||
print(f"⚠️ Could not find user {user_id}")
|
||||
logger.warning(f"Could not find user {user_id}")
|
||||
return
|
||||
|
||||
dm_channel = user.dm_channel
|
||||
@@ -833,7 +836,7 @@ async def miku_autonomous_reaction_for_dm(user_id: int, force_message=None):
|
||||
username = user.display_name
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error fetching DM channel for user {user_id}: {e}")
|
||||
logger.error(f"Error fetching DM channel for user {user_id}: {e}")
|
||||
return
|
||||
|
||||
try:
|
||||
@@ -842,9 +845,9 @@ async def miku_autonomous_reaction_for_dm(user_id: int, force_message=None):
|
||||
target_message = force_message
|
||||
# Check if we've already reacted to this message
|
||||
if target_message.id in _reacted_message_ids:
|
||||
print(f"⏭️ [DM: {username}] Already reacted to message {target_message.id}, skipping")
|
||||
logger.debug(f"[DM: {username}] Already reacted to message {target_message.id}, skipping")
|
||||
return
|
||||
print(f"🎯 [DM: {username}] Reacting to new message")
|
||||
logger.info(f"[DM: {username}] Reacting to new message")
|
||||
else:
|
||||
# Fetch recent messages from DM (last 50 messages)
|
||||
messages = []
|
||||
@@ -862,14 +865,14 @@ async def miku_autonomous_reaction_for_dm(user_id: int, force_message=None):
|
||||
messages.append(message)
|
||||
|
||||
if not messages:
|
||||
print(f"📭 [DM: {username}] No recent unreacted messages to react to")
|
||||
logger.debug(f"[DM: {username}] No recent unreacted messages to react to")
|
||||
return
|
||||
|
||||
# Pick a random message from the recent ones
|
||||
target_message = random.choice(messages)
|
||||
|
||||
# Analyze any media in the message
|
||||
print(f"🔍 [DM: {username}] Analyzing message for reaction")
|
||||
logger.debug(f"[DM: {username}] Analyzing message for reaction")
|
||||
media_description = await _analyze_message_media(target_message)
|
||||
|
||||
# Build message content with media description if present
|
||||
@@ -929,7 +932,7 @@ async def miku_autonomous_reaction_for_dm(user_id: int, force_message=None):
|
||||
emoji = emojis[0]
|
||||
else:
|
||||
# No emoji found in response, use fallback
|
||||
print(f"⚠️ [DM: {username}] LLM response contained no emoji: '{original_response[:50]}' - using fallback")
|
||||
logger.warning(f"[DM: {username}] LLM response contained no emoji: '{original_response[:50]}' - using fallback")
|
||||
emoji = "💙"
|
||||
|
||||
# Final validation: try adding the reaction
|
||||
@@ -937,7 +940,7 @@ async def miku_autonomous_reaction_for_dm(user_id: int, force_message=None):
|
||||
await target_message.add_reaction(emoji)
|
||||
except discord.HTTPException as e:
|
||||
if "Unknown Emoji" in str(e):
|
||||
print(f"❌ [DM: {username}] Invalid emoji from LLM: '{original_response[:50]}' - using fallback")
|
||||
logger.warning(f"[DM: {username}] Invalid emoji from LLM: '{original_response[:50]}' - using fallback")
|
||||
emoji = "💙"
|
||||
await target_message.add_reaction(emoji)
|
||||
else:
|
||||
@@ -954,14 +957,14 @@ async def miku_autonomous_reaction_for_dm(user_id: int, force_message=None):
|
||||
for msg_id in ids_to_remove:
|
||||
_reacted_message_ids.discard(msg_id)
|
||||
|
||||
print(f"✅ [DM: {username}] Autonomous reaction: Added {emoji} to message")
|
||||
logger.info(f"[DM: {username}] Autonomous reaction: Added {emoji} to message")
|
||||
|
||||
except discord.Forbidden:
|
||||
print(f"❌ [DM: {username}] Missing permissions to add reactions")
|
||||
logger.error(f"[DM: {username}] Missing permissions to add reactions")
|
||||
except discord.HTTPException as e:
|
||||
print(f"❌ [DM: {username}] Failed to add reaction: {e}")
|
||||
logger.error(f"[DM: {username}] Failed to add reaction: {e}")
|
||||
except Exception as e:
|
||||
print(f"⚠️ [DM: {username}] Error in autonomous reaction: {e}")
|
||||
logger.error(f"[DM: {username}] Error in autonomous reaction: {e}")
|
||||
|
||||
|
||||
async def miku_update_profile_picture_for_server(guild_id: int):
|
||||
@@ -973,18 +976,18 @@ async def miku_update_profile_picture_for_server(guild_id: int):
|
||||
|
||||
# Check if enough time has passed
|
||||
if not should_update_profile_picture():
|
||||
print(f"📸 [Server: {guild_id}] Profile picture not ready for update yet")
|
||||
logger.debug(f"[Server: {guild_id}] Profile picture not ready for update yet")
|
||||
return
|
||||
|
||||
# Get server config to use current mood
|
||||
server_config = server_manager.get_server_config(guild_id)
|
||||
if not server_config:
|
||||
print(f"⚠️ No config found for server {guild_id}")
|
||||
logger.warning(f"No config found for server {guild_id}")
|
||||
return
|
||||
|
||||
mood = server_config.current_mood_name
|
||||
|
||||
print(f"📸 [Server: {guild_id}] Attempting profile picture update (mood: {mood})")
|
||||
logger.info(f"[Server: {guild_id}] Attempting profile picture update (mood: {mood})")
|
||||
|
||||
try:
|
||||
success = await update_profile_picture(globals.client, mood=mood)
|
||||
@@ -1001,9 +1004,9 @@ async def miku_update_profile_picture_for_server(guild_id: int):
|
||||
"*updates avatar* Time for a fresh look! ✨"
|
||||
]
|
||||
await channel.send(random.choice(messages))
|
||||
print(f"✅ [Server: {guild_id}] Profile picture updated and announced!")
|
||||
logger.info(f"[Server: {guild_id}] Profile picture updated and announced!")
|
||||
else:
|
||||
print(f"⚠️ [Server: {guild_id}] Profile picture update failed")
|
||||
logger.warning(f"[Server: {guild_id}] Profile picture update failed")
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ [Server: {guild_id}] Error updating profile picture: {e}")
|
||||
logger.error(f"[Server: {guild_id}] Error updating profile picture: {e}")
|
||||
|
||||
Reference in New Issue
Block a user