diff --git a/bot/api.py b/bot/api.py index 66daecd..4506425 100644 --- a/bot/api.py +++ b/bot/api.py @@ -27,7 +27,8 @@ from utils.autonomous import ( miku_say_something_general, miku_engage_random_user, share_miku_tweet, - handle_custom_prompt + handle_custom_prompt, + miku_detect_and_join_conversation ) import asyncio import nest_asyncio @@ -338,6 +339,28 @@ async def trigger_autonomous_reaction(guild_id: int = None): else: return {"status": "error", "message": "Bot not ready"} +@app.post("/autonomous/join-conversation") +async def trigger_detect_and_join_conversation(guild_id: int = None): + # If guild_id is provided, detect and join conversation only for that server + # If no guild_id, trigger for all servers + print(f"🔍 [API] Join conversation endpoint called with guild_id={guild_id}") + if globals.client and globals.client.loop and globals.client.loop.is_running(): + if guild_id is not None: + # Trigger for specific server only (force=True to bypass checks when manually triggered) + print(f"🔍 [API] Importing and calling miku_detect_and_join_conversation_for_server({guild_id}, force=True)") + from utils.autonomous import miku_detect_and_join_conversation_for_server + globals.client.loop.create_task(miku_detect_and_join_conversation_for_server(guild_id, force=True)) + return {"status": "ok", "message": f"Detect and join conversation queued for server {guild_id}"} + else: + # Trigger for all servers (force=True to bypass checks when manually triggered) + print(f"🔍 [API] Importing and calling miku_detect_and_join_conversation() for all servers") + from utils.autonomous import miku_detect_and_join_conversation + globals.client.loop.create_task(miku_detect_and_join_conversation(force=True)) + return {"status": "ok", "message": "Detect and join conversation queued for all servers"} + else: + print(f"⚠️ [API] Bot not ready: client={globals.client}, loop={globals.client.loop if globals.client else None}") + return {"status": "error", "message": "Bot not ready"} + @app.post("/profile-picture/change") async def trigger_profile_picture_change( guild_id: int = None, diff --git a/bot/static/index.html b/bot/static/index.html index 8c316ad..3c12ed5 100644 --- a/bot/static/index.html +++ b/bot/static/index.html @@ -576,6 +576,7 @@ + diff --git a/bot/utils/autonomous_v1_legacy.py b/bot/utils/autonomous_v1_legacy.py index 8735312..eab4703 100644 --- a/bot/utils/autonomous_v1_legacy.py +++ b/bot/utils/autonomous_v1_legacy.py @@ -209,8 +209,14 @@ async def miku_engage_random_user_for_server(guild_id: int): except Exception as e: print(f"⚠️ Failed to engage user: {e}") -async def miku_detect_and_join_conversation_for_server(guild_id: int): - """Miku detects and joins conversations in a specific server""" +async def miku_detect_and_join_conversation_for_server(guild_id: int, force: bool = False): + """Miku detects and joins conversations in a specific server + + Args: + 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})") server_config = server_manager.get_server_config(guild_id) if not server_config: print(f"⚠️ No config found for server {guild_id}") @@ -224,25 +230,41 @@ async def miku_detect_and_join_conversation_for_server(guild_id: int): # 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") except Exception as e: print(f"⚠️ Failed to fetch channel history for server {guild_id}: {e}") return - # Filter to messages in last 10 minutes from real users (not bots) - recent_msgs = [ - msg for msg in messages - if not msg.author.bot - and (datetime.now(msg.created_at.tzinfo) - msg.created_at).total_seconds() < 600 - ] + # 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)") + else: + # Normal mode: Filter to messages in last 10 minutes from real users (not bots) + recent_msgs = [ + msg for msg in messages + 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)") user_ids = set(msg.author.id for msg in recent_msgs) - if len(recent_msgs) < 5 or len(user_ids) < 2: - # Not enough activity - return + 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)") + return - if random.random() > 0.5: - return # 50% chance to engage + if random.random() > 0.5: + print(f"🎲 [Join Conv] Random chance failed (50% chance)") + return # 50% chance to engage + else: + print(f"✅ [Join Conv] Force mode - bypassing activity checks") + if len(recent_msgs) < 1: + print(f"⚠️ [Join Conv] No messages found in channel history") + return # Use last 10 messages for context (oldest to newest) convo_lines = reversed(recent_msgs[:10]) @@ -374,10 +396,14 @@ async def miku_engage_random_user(): for guild_id in server_manager.servers: await miku_engage_random_user_for_server(guild_id) -async def miku_detect_and_join_conversation(): - """Legacy function - now runs for all servers""" +async def miku_detect_and_join_conversation(force: bool = False): + """Legacy function - now runs for all servers + + Args: + force: If True, bypass activity checks and random chance (for manual triggers) + """ for guild_id in server_manager.servers: - await miku_detect_and_join_conversation_for_server(guild_id) + await miku_detect_and_join_conversation_for_server(guild_id, force=force) async def share_miku_tweet(): """Legacy function - now runs for all servers""" diff --git a/miku-cli.py b/miku-cli.py index 9f2f1ac..f46b6bb 100644 --- a/miku-cli.py +++ b/miku-cli.py @@ -171,6 +171,14 @@ class MikuCLI: result = self._post("/autonomous/reaction") print(f"✅ {result['message']}") + def autonomous_join_conversation(self, guild_id: Optional[int] = None): + """Trigger detect and join conversation.""" + if guild_id: + result = self._post(f"/autonomous/join-conversation?guild_id={guild_id}") + else: + result = self._post("/autonomous/join-conversation") + print(f"✅ {result['message']}") + def autonomous_custom(self, prompt: str, guild_id: Optional[int] = None): """Send custom autonomous message.""" if guild_id: @@ -408,6 +416,7 @@ def print_help(): autonomous-engage [server_id] - Engage with conversation autonomous-tweet [server_id] - Post tweet-style message autonomous-reaction [server_id] - Generate reaction + autonomous-join-conversation [server_id] - Detect and join conversation autonomous-custom [server_id] - Custom autonomous action autonomous-stats - Show autonomous statistics @@ -489,6 +498,9 @@ def execute_command(cli: MikuCLI, command: str, args: list): elif command == 'autonomous-reaction': server_id = int(args[0]) if args and args[0].isdigit() else None cli.autonomous_reaction(server_id) + elif command == 'autonomous-join-conversation': + server_id = int(args[0]) if args and args[0].isdigit() else None + cli.autonomous_join_conversation(server_id) elif command == 'autonomous-custom': if not args: print("❌ Usage: autonomous-custom [server_id]") @@ -610,7 +622,7 @@ Examples: # Autonomous commands auto_parser = subparsers.add_parser('autonomous', help='Autonomous actions') - auto_parser.add_argument('action', choices=['general', 'engage', 'tweet', 'reaction', 'custom', 'stats']) + auto_parser.add_argument('action', choices=['general', 'engage', 'tweet', 'reaction', 'join-conversation', 'custom', 'stats']) auto_parser.add_argument('--prompt', help='Custom prompt (for custom action)') auto_parser.add_argument('--server', type=int, help='Server/guild ID') @@ -702,6 +714,8 @@ Examples: cli.autonomous_tweet(args.server) elif args.action == 'reaction': cli.autonomous_reaction(args.server) + elif args.action == 'join-conversation': + cli.autonomous_join_conversation(args.server) elif args.action == 'custom': if not args.prompt: print("❌ --prompt is required for custom action")