Install
openclaw skills install workout-tracker个人健身跟踪器 — 通过文字记录健身训练数据,自动解析动作、组数、次数和重量,实时写入本地 MySQL 数据库。支持制定专属训练计划、查询历史记录、追踪进步曲线,以及球类和有氧运动记录。
openclaw skills install workout-tracker记录健身数据,自动解析训练内容并写入数据库。
# 安装 Python MySQL 连接器
pip install mysql-connector-python
# 验证安装
python3 -c "import mysql.connector; print('✅ mysql-connector-python 已安装')"
# 创建 .env 配置文件
cat > .workout-tracker.env << 'EOF'
MYSQL_SOCKET="/tmp/mysql.sock"
MYSQL_USER="workout_user"
MYSQL_PASSWORD="your_secure_password" # 替换为实际密码
MYSQL_DATABASE="workout_tracker"
EOF
# 设置文件权限(仅当前用户可读)
chmod 600 .workout-tracker.env
# 加载配置
source .workout-tracker.env
# 仅在当前终端会话中有效
export MYSQL_SOCKET="/tmp/mysql.sock"
export MYSQL_USER="workout_user"
export MYSQL_PASSWORD="your_password" # 注意:密码会出现在 shell 历史中
export MYSQL_DATABASE="workout_tracker"
# 使用脚本提示输入密码,不存储在任何地方
import getpass
import os
password = getpass.getpass("请输入 MySQL 密码: ")
os.environ['MYSQL_PASSWORD'] = password
⚠️ 安全警告:避免将密码存储在 /.bashrc、/.zshrc 或版本控制的文件中。
-- 在 MySQL 中执行
CREATE USER IF NOT EXISTS 'workout_user'@'localhost' IDENTIFIED BY 'your_password';
GRANT INSERT, SELECT, UPDATE, DELETE ON workout_tracker.* TO 'workout_user'@'localhost';
FLUSH PRIVILEGES;
CREATE DATABASE IF NOT EXISTS workout_tracker;
# 运行初始化脚本
python3 /path/to/workout-tracker/scripts/init_database.py
# 运行验证脚本
python3 /path/to/workout-tracker/scripts/verify_setup.py
# 创建安全的 .env 配置文件(不上传至版本控制)
cat > ~/.workout-tracker.env << 'EOF'
# Workout Tracker 数据库配置
# ⚠️ 警告:此文件包含敏感信息,不要上传到版本控制
MYSQL_SOCKET="/tmp/mysql.sock"
MYSQL_USER="workout_user"
MYSQL_PASSWORD="your_secure_password_here" # 替换为你的实际密码
MYSQL_DATABASE="workout_tracker"
EOF
# 设置文件权限(仅当前用户可读)
chmod 600 ~/.workout-tracker.env
# 在 shell 中加载配置
source ~/.workout-tracker.env
#!/bin/bash
# workout-tracker-setup-secure.sh
echo "🏋️ Workout Tracker 安全安装"
# 安装 Python 依赖
echo "📦 安装 Python 依赖..."
pip install mysql-connector-python
# 创建安全的配置模板
echo "🔐 创建安全配置模板..."
cat > workout-tracker-config-template.env << 'EOF'
# Workout Tracker 配置模板
# 1. 将此文件复制为 .workout-tracker.env
# 2. 填写实际的数据库密码
# 3. 设置文件权限: chmod 600 .workout-tracker.env
MYSQL_SOCKET="/tmp/mysql.sock"
MYSQL_USER="workout_user"
MYSQL_PASSWORD="" # ⬅️ 在此处填写你的 MySQL 密码
MYSQL_DATABASE="workout_tracker"
EOF
echo "✅ 安装完成!"
echo ""
echo "📋 后续步骤:"
echo "1. 填写数据库密码: cp workout-tracker-config-template.env ~/.workout-tracker.env"
echo "2. 编辑 ~/.workout-tracker.env 设置密码"
echo "3. 设置文件权限: chmod 600 ~/.workout-tracker.env"
echo "4. 加载配置: source ~/.workout-tracker.env"
echo "5. 验证安装: python3 scripts/verify_setup.py"
echo ""
echo "🔒 安全提示:"
echo "- 永远不要在版本控制中包含 .env 文件"
echo "- 使用强密码并定期更换"
echo "- 数据库用户仅授予最小必要权限"
本技能专为单用户本地部署设计(用户无需公开任何凭据)。数据库配置通过本地环境变量或配置文件管理,不随技能发布。
默认单用户 ID 说明:
workout_sessions 和 workout_items 中的 user_id 以匹配自己的设置exercise_types (id, name, muscle_group, equipment, unit)
-- id: 自增主键
-- name: 动作名称,如"杠铃深蹲"、"腿举机"
-- muscle_group: 主要肌群(胸/背/腿/肩/臂/腹/全身/下背)
-- equipment: 器械类型(杠铃/哑铃/器械/自重/有氧)
-- unit: 计量单位(次/分钟/米)
gym_equipment (id, name, category, muscle_main, muscle_sec, created_at)
-- id: 自增主键
-- name: 器械名称
-- category: 类别(哑铃/杠铃/器械/有氧/自由重量/辅助)
-- muscle_main/muscle_sec: 主要/次要肌群
workout_sessions (id, user_id, workout_date, start_time, end_time, duration_min, workout_type, notes)
-- id: 自增主键
-- user_id: 用户ID(默认1,单用户本地部署)
-- workout_date: 训练日期
-- duration_min: 总时长(分钟)
-- workout_type: 训练类型(力量/有氧/柔韧)
-- notes: 备注(如状态评分"状态评分: 9/10 | 非常爽")
workout_items (id, session_id, exercise_type_id, set_order, reps, weight_kg, distance_km, rpe, notes)
-- id: 自增主键
-- session_id: 关联 workout_sessions.id
-- exercise_type_id: 关联 exercise_types.id
-- set_order: 第几组(1, 2, 3...)
-- reps: 重复次数
-- weight_kg: 重量(公斤)
-- notes: 备注(如"杠铃深蹲第1组")
用户一次性报完整动作计划 → 我预排所有组 → 用户每完成一组说 done → 我逐条标记完成
用户命令示例:
"深蹲 60公斤 4组×8次" → 预排所有组,记录为待完成状态,回复"已预排4组,每完成一组说done""done" → 标记当前训练最新一组为完成状态,回复"第1组完成!还剩3组""done 这组是10次" → 同上,并更新该组次数为10次"done 加一组" → 完成当前组,并追加一组相同训练"卧推 40公斤 3组×10次" → 切换到新动作,重新预排"结束了" → 训练完成,自动计算时长,询问状态评分操作流程(预排模式):
1. 用户说"动作 + 重量 + X组×Y次"
→ 若当日尚无 workout_session,先创建(start_time=NOW())
→ 解析动作名、重量、组数、次数
→ 匹配 exercise_types.id,不存在则插入
→ 一次性插入 N 条 workout_items,notes 标"⏳待完成"
→ 回复"已预排X组,每完成一组说done"
→ 随机(30%概率)附一句简短鼓励(如"这个重量稳的!")
2. 用户说 "done"(或"done 这组是X次")
→ 找到当前 session 最新未标记 ✓ 的记录
→ 更新 notes 追加" ✓"(若用户报了次数,同时更新 reps)
→ 回复"第N组完成!还剩X组"(若还有未完成的)
→ 若动作全部完成 → 回复"XX动作完成!下一个动作是什么?"
→ 随机(30%概率)附一句简短鼓励
3. 用户说 "done 加一组"
→ 执行步骤2的标记完成逻辑
→ 在当前动作下追加一条新的"⏳待完成"记录(reps/weight 继承上一组)
→ 回复"已追加第N+1组 ⏳"
4. 用户说 "动作名 重量 X组×Y次"
→ 切换到新动作,重复步骤1
5. 用户说 "结束了"
→ 自动计算:TIMESTAMPDIFF(MINUTE, start_time, NOW()),start_time 来自 workout_sessions
→ UPDATE workout_sessions SET duration_min=? WHERE id=current_session
→ 查询汇总数据,回复训练完成汇总
→ 主动询问:"今天状态怎么样?状态评分1-10"
情绪价值 — 鼓励语料库(随机选取):
每完成一组时,随机附上以下类型的鼓励语:
"又完成一组,腿部在燃烧!"、"离目标又近了一步!"、"感觉怎么样?这组有没有更稳?""这重量稳的!"、"继续保持这个节奏"、"你的深蹲越来越扎实了""不错不错!"、"这组完成得很干净"、"看得出状态很好!""腿已经软了?坚持住!"、"疼痛是成长的象征 😄"、"汗水不会骗人 💦""这个动作拿捏了!"、"太强了,下一个!"、"肌肉在尖叫,继续!"鼓励触发规则:
SQL模板(参数化查询):
-- 创建新训练 session(如尚无今日 session,start_time 自动为 NOW())
INSERT INTO workout_sessions (user_id, workout_date, workout_type, start_time)
VALUES (?, CURDATE(), ?, NOW()); -- user_id, workout_type
-- 获取当前 session_id
SELECT id FROM workout_sessions WHERE user_id=? ORDER BY id DESC LIMIT 1; -- user_id
-- 预排 N 组(用户说"动作 重量 X组×Y次")
INSERT INTO workout_items (session_id, exercise_type_id, set_order, reps, weight_kg, notes) VALUES
(?, ?, 1, ?, ?, CONCAT(?, ' 第1组 ⏳')),
(?, ?, 2, ?, ?, CONCAT(?, ' 第2组 ⏳')),
...
(?, ?, N, ?, ?, CONCAT(?, ' 第N组 ⏳')); -- sid, eid, reps, weight, exercise_name
-- 标记第N组完成(用户说 done 时)
UPDATE workout_items
SET notes = CONCAT(?, ' 第', ?, '组 ✓'), -- exercise_name, set_order
reps = ? -- 如用户报了次数
WHERE session_id=? AND set_order=?; -- sid, set_order
-- 追加一组(用户说 done 加一组 时)
INSERT INTO workout_items (session_id, exercise_type_id, set_order, reps, weight_kg, notes)
VALUES (?, ?, (SELECT IFNULL(MAX(set_order),0)+1 FROM workout_items WHERE session_id=?), ?, ?, CONCAT(?, ' 第N组 ⏳'));
-- sid, eid, sid, reps, weight, exercise_name
-- 完成训练(duration_min = NOW() - start_time)
SELECT TIMESTAMPDIFF(MINUTE, start_time, NOW()) as duration_min
FROM workout_sessions WHERE id=?; -- sid
UPDATE workout_sessions SET duration_min=? WHERE id=?; -- minutes, sid
-- 状态评分更新(后续用户说"X分"时)
UPDATE workout_sessions SET notes=? WHERE id=?; -- score_note, sid
用户一次性报完整训练内容:
今天练了深蹲 4组×10次×30公斤,腿举 3组×8次×72公斤
操作流程:
用户说"今天练腿给我一个计划" → 根据 gym_equipment 中的器械,生成针对性的训练计划 → 回复格式:热身 → 主项 → 辅助项 → 拉伸
当用户说"今天练X"、"我要练X"、"给我一个X训练计划"时,自动执行:
第一步:查询本周已训练过的肌群(避免重复)
-- 查询本周(近7天)训练过的肌群及动作
SELECT DISTINCT ws.workout_date, ws.workout_type,
et.muscle_group, et.name as exercise_name,
wi.weight_kg, wi.reps, wi.set_order
FROM workout_sessions ws
JOIN workout_items wi ON wi.session_id = ws.id
JOIN exercise_types et ON et.id = wi.exercise_type_id
WHERE ws.user_id=1 AND ws.workout_date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
ORDER BY ws.workout_date DESC, et.muscle_group;
第二步:查询用户器械(确保计划可执行)
SELECT id, name, category, muscle_main, muscle_sec
FROM gym_equipment
WHERE muscle_main LIKE '%背%' -- 动态替换目标肌群关键词
OR muscle_sec LIKE '%背%'
ORDER BY category;
第三步:分析后生成计划
回复模板:
📋 今日{肌群}训练计划(智能生成)
🗓️ 本周已练:{已训练肌群及日期}
🏋️ 可用器械:{器械列表}
① 热身(5-10分钟)
② 主项(3-4组×8-12次)— 标注动作名 [器械名]
③ 辅助项(2-3组×10-15次)— 标注动作名 [器械名]
④ 拉伸
💡备注:{上次训练情况及建议}
用户问"今天练了什么"、"上周训练几次"等 → 执行查询SQL → 整理成表格回复
SELECT ws.workout_date, ws.duration_min, ws.workout_type, ws.notes,
et.name, COUNT(*) as sets, SUM(wi.reps) as total_reps, wi.weight_kg
FROM workout_items wi
JOIN workout_sessions ws ON ws.id = wi.session_id
JOIN exercise_types et ON et.id = wi.exercise_type_id
WHERE wi.session_id = ?
GROUP BY et.name, wi.weight_kg
ORDER BY et.name;
SELECT ws.workout_date, ws.duration_min, ws.workout_type,
et.name, wi.set_order, wi.reps, wi.weight_kg
FROM workout_sessions ws
JOIN workout_items wi ON wi.session_id = ws.id
JOIN exercise_types et ON et.id = wi.exercise_type_id
WHERE ws.user_id=1 AND ws.workout_date=CURDATE()
ORDER BY ws.id, wi.set_order;
SELECT COUNT(DISTINCT workout_date) as days,
SUM(duration_min) as total_min
FROM workout_sessions
WHERE user_id=1 AND workout_date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY);
SELECT ws.workout_date, et.name, wi.reps, wi.weight_kg
FROM workout_items wi
JOIN workout_sessions ws ON ws.id = wi.session_id
JOIN exercise_types et ON et.id = wi.exercise_type_id
WHERE ws.user_id=1 AND et.name LIKE '%深蹲%'
ORDER BY ws.workout_date DESC LIMIT 10;
用户输入动作简称时,自动识别对应 exercise_types:
如果完全匹配不到,先插入新动作。
训练结束汇总回复后,必须主动询问:
"今天状态怎么样?状态评分1-10"
用户回复后,更新 workout_sessions.notes:
UPDATE workout_sessions SET notes = CONCAT('状态评分: ', ?, '/10 | ', ?) WHERE id = ?; -- score, feeling, sid
根据关键词判断 workout_type:
有氧柔韧力量用户说"网球一小时"、"游泳半小时"等,直接创建 session,不走预排组流程。
识别规则:
操作流程:
exercise_types.name(模糊匹配 sport_name)sport_name 字段写入运动名,workout_type 设为 有氧duration_min 写入实际时长(分钟)workout_items(这类运动不需要组数记录)SQL:
-- 创建运动记录
INSERT INTO workout_sessions (user_id, workout_date, sport_name, workout_type, duration_min, start_time)
VALUES (1, CURDATE(), '网球发球机', '有氧', 60, NOW());
回复模板:
🎾 网球发球机训练记录完成
───
⏱️ 时长:60 分钟
🏃 类型:有氧
───
状态评分 1-10?
所有用户输入必须经过参数化查询,禁止任何形式的字符串拼接 SQL。
# Python + MySQL Connector 示例
import mysql.connector
# 使用参数化查询
cursor.execute(
"INSERT INTO workout_items (session_id, exercise_type_id, set_order, reps, weight_kg) VALUES (%s, %s, %s, %s, %s)",
(session_id, exercise_type_id, set_order, reps, weight_kg)
)
# LIKE 查询也必须参数化
search_term = "深蹲"
cursor.execute(
"SELECT * FROM exercise_types WHERE name LIKE %s",
(f"%{search_term}%",) # 参数化,安全
)
# ❌ 危险:字符串拼接(极易 SQL 注入)
cursor.execute(f"INSERT INTO ... VALUES ({session_id}, {exercise_type_id}, ...)")
# ❌ 危险:使用 .format() 或 %
cursor.execute("SELECT * FROM exercise_types WHERE name LIKE '%{}%'".format(user_input))
# ❌ 危险:直接拼接用户输入到 SQL
sql = "SELECT * FROM exercise_types WHERE name LIKE '%" + user_input + "%'"
cursor.execute(sql)