feat(bot): comprehensive natural language command parsing
- Enhanced AI prompts with time/frequency conversion rules and 20+ examples - Added smart medication name resolution and confirmation flows - New medication commands: today, refills, snooze, adherence - New routine commands: create_with_steps, add_steps, steps, schedule - Added active session awareness with shortcuts (done, skip, pause, resume) - Confirmation handling for destructive/create actions - Improved help message with natural language examples
This commit is contained in:
138
bot/bot.py
138
bot/bot.py
@@ -182,11 +182,119 @@ async def handleLoginStep(message):
|
||||
|
||||
|
||||
async def sendHelpMessage(message):
|
||||
registered = list_registered()
|
||||
help_msg = f"**Available Modules:**\n{chr(10).join(f'- {m}' for m in registered) if registered else '- No modules registered'}\n\nJust talk naturally and I'll help you out!"
|
||||
help_msg = """**🤖 Synculous Bot - Natural Language Commands**
|
||||
|
||||
Just talk to me naturally! Here are some examples:
|
||||
|
||||
**💊 Medications:**
|
||||
• "add lsd 50 mcg every tuesday at 4:20pm"
|
||||
• "take my wellbutrin"
|
||||
• "what meds do i have today?"
|
||||
• "show my refills"
|
||||
• "snooze my reminder for 30 minutes"
|
||||
• "check adherence"
|
||||
|
||||
**📋 Routines:**
|
||||
• "create morning routine with brush teeth, shower, eat"
|
||||
• "start my morning routine"
|
||||
• "done" (complete current step)
|
||||
• "skip" (skip current step)
|
||||
• "pause/resume" (pause or continue)
|
||||
• "what steps are in my routine?"
|
||||
• "schedule workout for monday wednesday friday at 7am"
|
||||
• "show my stats"
|
||||
|
||||
**💡 Tips:**
|
||||
• I understand natural language, typos, and slang
|
||||
• If I'm unsure, I'll ask for clarification
|
||||
• For important actions, I'll ask you to confirm with "yes" or "no"
|
||||
• When you're in a routine, shortcuts like "done", "skip", "pause" work automatically"""
|
||||
await message.channel.send(help_msg)
|
||||
|
||||
|
||||
async def checkActiveSession(session):
|
||||
"""Check if user has an active routine session and return details."""
|
||||
token = session.get("token")
|
||||
if not token:
|
||||
return None
|
||||
|
||||
resp, status = apiRequest("get", "/api/sessions/active", token)
|
||||
if status == 200 and "session" in resp:
|
||||
return resp
|
||||
return None
|
||||
|
||||
|
||||
async def handleConfirmation(message, session):
|
||||
"""Handle yes/no confirmation responses. Returns True if handled."""
|
||||
discord_id = message.author.id
|
||||
user_input = message.content.lower().strip()
|
||||
|
||||
if "pending_confirmations" not in session:
|
||||
return False
|
||||
|
||||
# Check for any pending confirmations
|
||||
pending = session["pending_confirmations"]
|
||||
if not pending:
|
||||
return False
|
||||
|
||||
# Get the most recent pending confirmation
|
||||
confirmation_id = list(pending.keys())[-1]
|
||||
confirmation_data = pending[confirmation_id]
|
||||
|
||||
if user_input in ("yes", "y", "yeah", "sure", "ok", "confirm"):
|
||||
# Execute the confirmed action
|
||||
del pending[confirmation_id]
|
||||
|
||||
interaction_type = confirmation_data.get("interaction_type")
|
||||
handler = get_handler(interaction_type)
|
||||
|
||||
if handler:
|
||||
# Create a fake parsed object for the handler
|
||||
fake_parsed = confirmation_data.copy()
|
||||
fake_parsed["needs_confirmation"] = False
|
||||
await handler(message, session, fake_parsed)
|
||||
return True
|
||||
|
||||
elif user_input in ("no", "n", "nah", "cancel", "abort"):
|
||||
del pending[confirmation_id]
|
||||
await message.channel.send("❌ Cancelled.")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
async def handleActiveSessionShortcuts(message, session, active_session):
|
||||
"""Handle shortcuts like 'done', 'skip', 'next' when in active session."""
|
||||
user_input = message.content.lower().strip()
|
||||
|
||||
# Map common shortcuts to actions
|
||||
shortcuts = {
|
||||
"done": ("routine", "complete"),
|
||||
"finished": ("routine", "complete"),
|
||||
"complete": ("routine", "complete"),
|
||||
"next": ("routine", "complete"),
|
||||
"skip": ("routine", "skip"),
|
||||
"pass": ("routine", "skip"),
|
||||
"pause": ("routine", "pause"),
|
||||
"hold": ("routine", "pause"),
|
||||
"resume": ("routine", "resume"),
|
||||
"continue": ("routine", "resume"),
|
||||
"stop": ("routine", "cancel"),
|
||||
"quit": ("routine", "cancel"),
|
||||
"abort": ("routine", "abort"),
|
||||
}
|
||||
|
||||
if user_input in shortcuts:
|
||||
interaction_type, action = shortcuts[user_input]
|
||||
handler = get_handler(interaction_type)
|
||||
if handler:
|
||||
fake_parsed = {"action": action}
|
||||
await handler(message, session, fake_parsed)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
async def routeCommand(message):
|
||||
discord_id = message.author.id
|
||||
session = user_sessions[discord_id]
|
||||
@@ -196,9 +304,33 @@ async def routeCommand(message):
|
||||
await sendHelpMessage(message)
|
||||
return
|
||||
|
||||
# Check for active session first
|
||||
active_session = await checkActiveSession(session)
|
||||
|
||||
# Handle confirmation responses
|
||||
confirmation_handled = await handleConfirmation(message, session)
|
||||
if confirmation_handled:
|
||||
return
|
||||
|
||||
# Handle shortcuts when in active session
|
||||
if active_session:
|
||||
shortcut_handled = await handleActiveSessionShortcuts(message, session, active_session)
|
||||
if shortcut_handled:
|
||||
return
|
||||
|
||||
async with message.channel.typing():
|
||||
history = message_history.get(discord_id, [])
|
||||
parsed = ai_parser.parse(message.content, "command_parser", history=history)
|
||||
|
||||
# Add context about active session to help AI understand
|
||||
context = ""
|
||||
if active_session:
|
||||
session_data = active_session.get("session", {})
|
||||
routine_name = session_data.get("routine_name", "a routine")
|
||||
current_step = session_data.get("current_step_index", 0) + 1
|
||||
total_steps = active_session.get("total_steps", 0)
|
||||
context = f"\n[Context: User is currently in active session for '{routine_name}', on step {current_step} of {total_steps}. They can say 'done', 'skip', 'pause', 'resume', or 'stop'.]"
|
||||
|
||||
parsed = ai_parser.parse(message.content + context, "command_parser", history=history)
|
||||
|
||||
if discord_id not in message_history:
|
||||
message_history[discord_id] = []
|
||||
|
||||
Reference in New Issue
Block a user