Add 'Detect and Join Conversation' button to Web UI and CLI
- Added /autonomous/join-conversation API endpoint in api.py - Added 'Detect and Join Conversation' button to Web UI under Autonomous Actions - Added 'autonomous join-conversation' command to CLI tool (miku-cli.py) - Updated miku_detect_and_join_conversation_for_server to support force=True parameter - When force=True: skips time limit, activity checks, and random chance - Force mode uses last 10 user messages regardless of age - Manual triggers via Web UI/CLI now work even with old messages
This commit is contained in:
25
bot/api.py
25
bot/api.py
@@ -27,7 +27,8 @@ from utils.autonomous import (
|
|||||||
miku_say_something_general,
|
miku_say_something_general,
|
||||||
miku_engage_random_user,
|
miku_engage_random_user,
|
||||||
share_miku_tweet,
|
share_miku_tweet,
|
||||||
handle_custom_prompt
|
handle_custom_prompt,
|
||||||
|
miku_detect_and_join_conversation
|
||||||
)
|
)
|
||||||
import asyncio
|
import asyncio
|
||||||
import nest_asyncio
|
import nest_asyncio
|
||||||
@@ -338,6 +339,28 @@ async def trigger_autonomous_reaction(guild_id: int = None):
|
|||||||
else:
|
else:
|
||||||
return {"status": "error", "message": "Bot not ready"}
|
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")
|
@app.post("/profile-picture/change")
|
||||||
async def trigger_profile_picture_change(
|
async def trigger_profile_picture_change(
|
||||||
guild_id: int = None,
|
guild_id: int = None,
|
||||||
|
|||||||
@@ -576,6 +576,7 @@
|
|||||||
<button onclick="triggerAutonomous('engage')">Engage Random User</button>
|
<button onclick="triggerAutonomous('engage')">Engage Random User</button>
|
||||||
<button onclick="triggerAutonomous('tweet')">Share Tweet</button>
|
<button onclick="triggerAutonomous('tweet')">Share Tweet</button>
|
||||||
<button onclick="triggerAutonomous('reaction')">React to Message</button>
|
<button onclick="triggerAutonomous('reaction')">React to Message</button>
|
||||||
|
<button onclick="triggerAutonomous('join-conversation')">Detect and Join Conversation</button>
|
||||||
<button onclick="toggleCustomPrompt()">Custom Prompt</button>
|
<button onclick="toggleCustomPrompt()">Custom Prompt</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -209,8 +209,14 @@ async def miku_engage_random_user_for_server(guild_id: int):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"⚠️ Failed to engage user: {e}")
|
print(f"⚠️ Failed to engage user: {e}")
|
||||||
|
|
||||||
async def miku_detect_and_join_conversation_for_server(guild_id: int):
|
async def miku_detect_and_join_conversation_for_server(guild_id: int, force: bool = False):
|
||||||
"""Miku detects and joins conversations in a specific server"""
|
"""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)
|
server_config = server_manager.get_server_config(guild_id)
|
||||||
if not server_config:
|
if not server_config:
|
||||||
print(f"⚠️ No config found for server {guild_id}")
|
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)
|
# Fetch last 20 messages (for filtering)
|
||||||
try:
|
try:
|
||||||
messages = [msg async for msg in channel.history(limit=20)]
|
messages = [msg async for msg in channel.history(limit=20)]
|
||||||
|
print(f"📜 [Join Conv] Fetched {len(messages)} messages from history")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"⚠️ Failed to fetch channel history for server {guild_id}: {e}")
|
print(f"⚠️ Failed to fetch channel history for server {guild_id}: {e}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Filter to messages in last 10 minutes from real users (not bots)
|
# Filter messages based on force mode
|
||||||
recent_msgs = [
|
if force:
|
||||||
msg for msg in messages
|
# When forced, use messages from real users (no time limit) - but limit to last 10
|
||||||
if not msg.author.bot
|
recent_msgs = [msg for msg in messages if not msg.author.bot][:10]
|
||||||
and (datetime.now(msg.created_at.tzinfo) - msg.created_at).total_seconds() < 600
|
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)
|
user_ids = set(msg.author.id for msg in recent_msgs)
|
||||||
|
|
||||||
if len(recent_msgs) < 5 or len(user_ids) < 2:
|
if not force:
|
||||||
# Not enough activity
|
if len(recent_msgs) < 5 or len(user_ids) < 2:
|
||||||
return
|
# 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:
|
if random.random() > 0.5:
|
||||||
return # 50% chance to engage
|
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)
|
# Use last 10 messages for context (oldest to newest)
|
||||||
convo_lines = reversed(recent_msgs[:10])
|
convo_lines = reversed(recent_msgs[:10])
|
||||||
@@ -374,10 +396,14 @@ async def miku_engage_random_user():
|
|||||||
for guild_id in server_manager.servers:
|
for guild_id in server_manager.servers:
|
||||||
await miku_engage_random_user_for_server(guild_id)
|
await miku_engage_random_user_for_server(guild_id)
|
||||||
|
|
||||||
async def miku_detect_and_join_conversation():
|
async def miku_detect_and_join_conversation(force: bool = False):
|
||||||
"""Legacy function - now runs for all servers"""
|
"""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:
|
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():
|
async def share_miku_tweet():
|
||||||
"""Legacy function - now runs for all servers"""
|
"""Legacy function - now runs for all servers"""
|
||||||
|
|||||||
16
miku-cli.py
16
miku-cli.py
@@ -171,6 +171,14 @@ class MikuCLI:
|
|||||||
result = self._post("/autonomous/reaction")
|
result = self._post("/autonomous/reaction")
|
||||||
print(f"✅ {result['message']}")
|
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):
|
def autonomous_custom(self, prompt: str, guild_id: Optional[int] = None):
|
||||||
"""Send custom autonomous message."""
|
"""Send custom autonomous message."""
|
||||||
if guild_id:
|
if guild_id:
|
||||||
@@ -408,6 +416,7 @@ def print_help():
|
|||||||
autonomous-engage [server_id] - Engage with conversation
|
autonomous-engage [server_id] - Engage with conversation
|
||||||
autonomous-tweet [server_id] - Post tweet-style message
|
autonomous-tweet [server_id] - Post tweet-style message
|
||||||
autonomous-reaction [server_id] - Generate reaction
|
autonomous-reaction [server_id] - Generate reaction
|
||||||
|
autonomous-join-conversation [server_id] - Detect and join conversation
|
||||||
autonomous-custom <prompt> [server_id] - Custom autonomous action
|
autonomous-custom <prompt> [server_id] - Custom autonomous action
|
||||||
autonomous-stats - Show autonomous statistics
|
autonomous-stats - Show autonomous statistics
|
||||||
|
|
||||||
@@ -489,6 +498,9 @@ def execute_command(cli: MikuCLI, command: str, args: list):
|
|||||||
elif command == 'autonomous-reaction':
|
elif command == 'autonomous-reaction':
|
||||||
server_id = int(args[0]) if args and args[0].isdigit() else None
|
server_id = int(args[0]) if args and args[0].isdigit() else None
|
||||||
cli.autonomous_reaction(server_id)
|
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':
|
elif command == 'autonomous-custom':
|
||||||
if not args:
|
if not args:
|
||||||
print("❌ Usage: autonomous-custom <prompt> [server_id]")
|
print("❌ Usage: autonomous-custom <prompt> [server_id]")
|
||||||
@@ -610,7 +622,7 @@ Examples:
|
|||||||
|
|
||||||
# Autonomous commands
|
# Autonomous commands
|
||||||
auto_parser = subparsers.add_parser('autonomous', help='Autonomous actions')
|
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('--prompt', help='Custom prompt (for custom action)')
|
||||||
auto_parser.add_argument('--server', type=int, help='Server/guild ID')
|
auto_parser.add_argument('--server', type=int, help='Server/guild ID')
|
||||||
|
|
||||||
@@ -702,6 +714,8 @@ Examples:
|
|||||||
cli.autonomous_tweet(args.server)
|
cli.autonomous_tweet(args.server)
|
||||||
elif args.action == 'reaction':
|
elif args.action == 'reaction':
|
||||||
cli.autonomous_reaction(args.server)
|
cli.autonomous_reaction(args.server)
|
||||||
|
elif args.action == 'join-conversation':
|
||||||
|
cli.autonomous_join_conversation(args.server)
|
||||||
elif args.action == 'custom':
|
elif args.action == 'custom':
|
||||||
if not args.prompt:
|
if not args.prompt:
|
||||||
print("❌ --prompt is required for custom action")
|
print("❌ --prompt is required for custom action")
|
||||||
|
|||||||
Reference in New Issue
Block a user