Files
Training-Bot/docs/superpowers/specs/2026-05-20-training-assistant-design.md
T

8.5 KiB
Raw Blame History

Training Assistant — Design Spec

Date: 2026-05-20 Status: Approved


Overview

A personal Telegram bot that tracks anal training sessions, sends reminders, and uses an AI coach (via OpenRouter) to adaptively manage the training schedule and provide progress feedback.

Built with Node.js/TypeScript, Telegraf, SQLite, and node-cron. Designed so a web dashboard (Approach C) can be added later as a read-only layer on the same SQLite file.


Architecture

A single TypeScript process with four internal components:

┌─────────────────────────────────────────┐
│  Telegram Bot (Telegraf)                │
│  - command routing                      │
│  - message handling                     │
└──────────────┬──────────────────────────┘
               │
       ┌───────┼───────┐
       ▼       ▼       ▼
 ┌──────────┐ ┌──────┐ ┌─────────────┐
 │ Session  │ │ Cron │ │  AI Coach   │
 │ Logger   │ │ Jobs │ │ (OpenRouter)│
 └────┬─────┘ └──┬───┘ └──────┬──────┘
      │          │             │
      └──────────┴─────────────┘
                 │
          ┌──────▼──────┐
          │   SQLite    │
          └─────────────┘

Components:

  • Bot handler — Telegraf v4, routes commands and free-text messages
  • Session logger — writes training sessions to SQLite, validates input
  • Cron jobsnode-cron, sends reminders at configured times
  • AI coach — OpenRouter via OpenAI-compatible client; reads history from DB, generates coaching and schedule proposals
  • SQLitebetter-sqlite3, single file, easy to back up
  • Config.env for Telegram token, OpenRouter API key, user chat ID, reminder times, preferred model

Data Model

sessions

Column Type Notes
id INTEGER PRIMARY KEY
date TEXT YYYY-MM-DD
duration_min INTEGER
notes TEXT Optional free text
created_at TEXT ISO timestamp

schedule

Column Type Notes
id INTEGER PRIMARY KEY
date TEXT YYYY-MM-DD
planned_min INTEGER
status TEXT pending | completed | skipped
skip_reason TEXT Optional
ai_rationale TEXT Why the AI scheduled this
created_at TEXT ISO timestamp

settings

Column Type Notes
key TEXT PRIMARY KEY
value TEXT

Used for: reminder times, preferred OpenRouter model, training goal, reminder enabled flag.

Session logging writes to sessions and updates the matching schedule row to completed. The AI reads both tables to understand history and the current plan.

The existing .ics calendar is not imported. The AI generates a fresh adaptive schedule on first run, seeded by onboarding answers.


Commands

Logging (no AI, always fast)

Command Example Action
/log <duration> /log 2h, /log 90m Record a completed session for today
/skip [reason] /skip work trip Mark today as skipped
/snooze /snooze Postpone today's reminder by 2 hours

Status & history

Command Action
/status Today's plan, completion state, next reminder time
/history Last 7 sessions (date, duration, notes)
/streak Current streak and longest streak

AI-powered

Command Action
/progress AI analyzes recent weeks, gives assessment and flags patterns
/next AI proposes the next training step (duration or frequency change)
/reschedule [note] AI regenerates upcoming schedule; optional context ("busy next week")
/chat <message> Free-form coaching conversation

Setup

Command Action
/setup First-run onboarding: current level, typical availability, goal — generates initial schedule
/settings model <id> Switch OpenRouter model at runtime without redeploying

Fallback: Unrecognized messages prompt the user to use /chat for free-form questions.

Error handling: AI commands show a clear error message if OpenRouter is unavailable. Logging commands (/log, /skip) always work regardless of AI availability.


AI Integration

Client: openai npm package pointed at https://openrouter.ai/api/v1. Model is read from settings at runtime.

Context sent on every AI call:

  • Training goal (from setup)
  • Last 30 sessions (date, duration, notes)
  • Upcoming schedule with statuses
  • Recent skips and their reasons
  • Current streak

Default model: google/gemini-2.5-flash-preview Chosen for strong reasoning, low cost, fast responses, and large context window. Swappable at runtime via /settings model <id>.

Alternative models (all via OpenRouter):

  • deepseek/deepseek-chat — very cheap, good for analytical tasks
  • meta-llama/llama-3.3-70b-instruct — free on some providers
  • openai/gpt-4o-mini — reliable, cheap, structured-prompt-friendly
  • qwen/qwen-2.5-72b-instruct — strong instruction following, low cost

Prompt strategy:

  • Coaching prompts live in a prompts/ directory — tweakable without touching logic
  • The AI is instructed to be encouraging but honest, explain its reasoning, and flag overtraining or undertraining
  • Schedule proposals are requested as JSON for reliable parsing:
[
  { "date": "2026-05-22", "planned_min": 60, "rationale": "Continuing current level" },
  { "date": "2026-05-24", "planned_min": 60, "rationale": "Rest day between" },
  { "date": "2026-05-26", "planned_min": 75, "rationale": "Step up if 22nd went well" }
]

Reminder & Scheduling Logic

Cron jobs

Job Default time Condition
Morning reminder 05:30 Session scheduled today and status is pending
Evening nudge 16:30 Session scheduled today and status still pending
Snooze 2 hours after /snooze One-off, clears itself after firing

No reminders fire on days with no scheduled session, or on days already completed or skipped.

Adaptive schedule generation

/reschedule sends full history to the AI, which returns a JSON array of upcoming sessions (714 days). The bot parses this into schedule rows. Skip reasons are included in the next /reschedule call so the AI accounts for gaps.


Project Structure

src/
  bot/          # Telegraf setup, command handlers
  db/           # SQLite schema, queries
  scheduler/    # node-cron jobs
  ai/           # OpenRouter client, context builder
  prompts/      # Prompt templates
  config.ts     # Env loading and validation
  index.ts      # Entry point
.env.example
Dockerfile
docker-compose.yml
.dockerignore
package.json
tsconfig.json

Deployment

Local development

Run directly with npm run dev (ts-node or tsx). Uses .env file for config.

Production — Docker

The app runs as a Docker container on the home server. SQLite data is persisted via a named volume mounted at /app/data/.

Dockerfile — multi-stage build:

  1. builder stage: installs all dependencies, compiles TypeScript to dist/
  2. runner stage: copies only dist/ and production node_modules, runs as a non-root user

docker-compose.yml — single service definition:

  • Mounts a named volume for the SQLite file (./data:/app/data)
  • Loads env vars from .env (not baked into the image)
  • restart: unless-stopped so it survives reboots

SQLite file location: /app/data/assistant.db inside the container, mapped to ./data/assistant.db on the host — easy to back up by copying that file.

.dockerignore excludes: node_modules/, src/, .env, data/, *.md.

Timezone: Container timezone set via TZ env var (e.g. TZ=Europe/Amsterdam) so cron jobs fire at the correct local times.

Updating: docker compose pull && docker compose up -d --build rebuilds from source and restarts with zero downtime for a single-container setup.


Future Extension Points

  • Web dashboard (Approach C): Read-only layer on the same SQLite file — no schema changes needed
  • Multi-user: settings and schedule tables can gain a user_id column
  • Export: Sessions table trivially exportable to CSV or JSON