- Created chatwoot-agent-bot/ with Node.js webhook server - Bot detects intent (greeting, billing, technical, features, account) - Auto-responds from FAQ knowledge base or escalates to human - FAQ-KB.md: Living knowledge base that grows with customer questions - CHATWOOT-SETUP.md: Complete deployment and configuration guide - Supports Telegram notifications on escalation - Bot runs on port 3001, ready for Chatwoot webhook integration
146 lines
5.0 KiB
Python
Executable File
146 lines
5.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Iron - Personal Fitness Coach Agent
|
|
Handles: workout generation, PR tracking, progress reports, meal suggestions
|
|
"""
|
|
import json
|
|
import subprocess
|
|
from datetime import datetime, timedelta
|
|
from pathlib import Path
|
|
import random
|
|
|
|
WORKSPACE = Path(__file__).parent.parent
|
|
CONFIG = WORKSPACE / "config"
|
|
STATE = WORKSPACE / "state"
|
|
LOGS = WORKSPACE / "logs"
|
|
|
|
PROFILE_FILE = CONFIG / "user-profile.json"
|
|
PROGRAM_FILE = CONFIG / "program.json"
|
|
PRS_FILE = STATE / "prs.json"
|
|
|
|
def load_json(path):
|
|
with open(path) as f:
|
|
return json.load(f)
|
|
|
|
def save_json(path, data):
|
|
with open(path, 'w') as f:
|
|
json.dump(data, f, indent=2)
|
|
|
|
def log(msg):
|
|
ts = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
line = f"[{ts}] {msg}"
|
|
print(line)
|
|
(LOGS / f"iron-{datetime.now().strftime('%Y%m%d')}.log").open('a').write(line + '\n')
|
|
|
|
def get_todays_workout():
|
|
"""Generate today's workout based on program"""
|
|
program = load_json(PROGRAM_FILE)
|
|
profile = load_json(PROFILE_FILE)
|
|
|
|
# Determine which day (simple rotation)
|
|
today = datetime.now()
|
|
start = datetime.strptime(profile['program']['start_date'], '%Y-%m-%d')
|
|
days_since_start = (today - start).days
|
|
day_num = (days_since_start % 7) // 2 # Roughly every 2-3 days
|
|
|
|
if day_num == 0:
|
|
workout_key = 'day1'
|
|
elif day_num == 1:
|
|
workout_key = 'day2'
|
|
else:
|
|
workout_key = 'day3'
|
|
|
|
workout = program['workouts'][workout_key]
|
|
prs = load_json(PRS_FILE)
|
|
|
|
output = f"💪 *{workout['name']}* ({workout['duration_min']} min)\n\n"
|
|
|
|
for i, ex in enumerate(workout['exercises'], 1):
|
|
# Check if we have PR data
|
|
pr_weight = prs['personal_records'].get(ex['name'], {}).get('weight')
|
|
pr_note = ""
|
|
if pr_weight:
|
|
pr_note = f" (Last: {pr_weight})"
|
|
|
|
output += f"{i}. *{ex['name']}*: {ex['sets']}x{ex['reps']} @ ___ {pr_note}\n"
|
|
output += f" Rest: {ex['rest_sec']}s | {ex['notes']}\n"
|
|
|
|
output += "\n_Reply with results: 'Bench 4x8 @ 185'_ "
|
|
return output
|
|
|
|
def get_meal_suggestion():
|
|
"""Generate T1D-friendly high protein, low carb meal"""
|
|
meals = [
|
|
("Grilled Chicken & Veggies", "Chicken breast, broccoli, olive oil", "45g protein, 8g net carbs"),
|
|
("Salmon & Asparagus", "Salmon fillet, asparagus, lemon", "40g protein, 6g net carbs"),
|
|
("Turkey & Egg Scramble", "Ground turkey, eggs, spinach", "42g protein, 5g net carbs"),
|
|
("Steak & Cauliflower", "Ribeye steak, cauliflower rice", "48g protein, 7g net carbs"),
|
|
("Tuna Salad", "Canned tuna, mayo, celery, lettuce", "35g protein, 4g net carbs"),
|
|
("Greek Yogurt Bowl", "Greek yogurt, almonds, berries", "25g protein, 9g net carbs"),
|
|
("Protein Shake + Nuts", "Whey isolate, almonds, peanut butter", "30g protein, 6g net carbs"),
|
|
]
|
|
|
|
meal = random.choice(meals)
|
|
return f"🍽️ *{meal[0]}*\n\nIngredients: {meal[1]}\nMacros: {meal[2]}\n\n_{meal[0].lower()} with veggies for easy prep_"
|
|
|
|
def get_weekly_progress():
|
|
"""Generate weekly progress report"""
|
|
profile = load_json(PROFILE_FILE)
|
|
prs = load_json(PRS_FILE)
|
|
|
|
weight = profile['stats']['weight_lbs']
|
|
weight_history = profile['stats'].get('weight_history', [])
|
|
trend = ""
|
|
if len(weight_history) > 1:
|
|
change = weight_history[-1]['weight'] - weight_history[0]['weight']
|
|
trend = f"{'+' if change > 0 else ''}{change:.1f} lbs"
|
|
|
|
report = f"📊 *Weekly Progress Report*\n\n"
|
|
report += f"*Current Weight:* {weight} lbs {trend}\n"
|
|
report += f"*Program:* {profile['program']['type']}\n"
|
|
report += f"*Experience:* {profile['stats']['experience'].title()}\n\n"
|
|
|
|
# Count workouts
|
|
completed = len(prs.get('workout_history', []))
|
|
report += f"*Workouts Logged:* {completed}\n"
|
|
|
|
# PRs
|
|
pr_count = sum(1 for v in prs['personal_records'].values() if v.get('weight'))
|
|
report += f"*PRs Tracked:* {pr_count}/5\n\n"
|
|
|
|
report += "_Keep grinding. Consistency > intensity._"
|
|
return report
|
|
|
|
def send_telegram(msg):
|
|
"""Send message via Telegram"""
|
|
try:
|
|
subprocess.run(['openclaw', 'message', 'send', '--text', msg],
|
|
capture_output=True, timeout=10)
|
|
except Exception as e:
|
|
log(f"Telegram send failed: {e}")
|
|
|
|
def main():
|
|
import sys
|
|
cmd = sys.argv[1] if len(sys.argv) > 1 else 'workout'
|
|
|
|
if cmd == 'workout':
|
|
print(get_todays_workout())
|
|
elif cmd == 'meal':
|
|
print(get_meal_suggestion())
|
|
elif cmd == 'progress':
|
|
print(get_weekly_progress())
|
|
elif cmd == 'log' and len(sys.argv) > 2:
|
|
# Log workout result
|
|
result = ' '.join(sys.argv[2:])
|
|
log(f"WORKOUT LOG: {result}")
|
|
print(f"✅ Logged: {result}")
|
|
else:
|
|
print("Iron Fitness Coach - Commands:")
|
|
print(" workout - Get today's workout")
|
|
print(" meal - Get meal suggestion")
|
|
print(" progress - Weekly progress report")
|
|
print(" log <result> - Log workout result")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|