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")