feat: Add Chatwoot Agent Bot prototype and FAQ knowledge base

- 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
This commit is contained in:
2026-04-01 16:26:05 -04:00
parent 7ba19752de
commit 5319bcd30b
1074 changed files with 456376 additions and 0 deletions

View File

@@ -0,0 +1,145 @@
#!/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()

View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Send morning workout via Telegram
WORKSPACE="/Users/claw/.openclaw/workspace/agents/fitness-coach"
WORKOUT=$(python3 "$WORKSPACE/scripts/iron-agent.py" workout)
MSG="☀️ *GOOD MORNING! Today's Workout:*
$WORKOUT
Time to grind! 💪"
# Send via Telegram to Chris
openclaw message send --channel telegram --target telegram:8269921691 --message "$MSG" 2>/dev/null
# Log it
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Workout sent" >> "$WORKSPACE/logs/workout-sent.log"