Commit graph

58 commits

Author SHA1 Message Date
Danny
9e50686983 feat: scripts/set-bot-presence.py for chat-side bot presence
Idempotent one-shot script that calls the Telegram Bot API to:
- set the persistent menu button → "Open Workout Tracker" launching
  the Mini App at $WEBAPP_URL
- publish a short description + long description so the chat tells
  users what to do before they /start (which now returns silence —
  we removed the polling bot)
- clear the published commands list (no more stale /start, /history,
  etc. in the / menu)

Loads BOT_TOKEN from env first, then ~/.secrets/bigbiggerbiggestbot
to match start.py. Pure-stdlib (urllib) so it has no extra deps.

The prod systemd unit gets an ExecStartPost hook for this (dotfiles
change in a sister commit). Errors are non-fatal — the dash prefix
on ExecStartPost means a failed presence update never blocks the
backend from being healthy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 12:01:39 +02:00
Danny
9f146d60fa feat: last-session recall when starting an exercise
When you start an exercise, the Mini App now fetches the most
recent time you logged it and shows a hint line in the sets card
("Last time: 8×60, 6×60, 5×60 · 3 days ago"), plus pre-fills the
weight input with the last set's weight.

- db.get_last_exercise(user_id, name): most recent non-deleted
  entry, case-insensitive name match, sets_detail parsed.
- GET /api/exercises/last?name=<name>.
- webapp: loadLastSession() on startExercise + draft restore;
  hint cleared on editExercise (the set rows are the reference
  there). Pre-fill only when the weight field is empty and no
  sets logged yet, so it never clobbers user input.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 12:52:37 +02:00
Danny
5e3636201f docs: add last-session recall to ROADMAP
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 12:49:51 +02:00
Danny
d6f8fa41fb docs: shipyard staging now has a stable URL
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 14:02:57 +02:00
Danny
459c751414 fix(start.py): line-buffer stdout so journals show progress promptly
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 13:42:51 +02:00
Danny
17248e239b refactor: drop the slash-command bot — Mini App is the only interface
bot.py is gone. /start, /history, /stats, /delete, /export,
/feedback, and the text-message workout parser are no longer
exposed. Everything those commands did is already available in
the Mini App (history listing, stats, edit/delete, JSON export
via /api/export/json, etc.).

Why: prod runs behind a Mini App URL, and shipyard staging is a
tenant under the existing shipyard_poc_bot which polls Telegram
itself. A second polling process on the same token would 409. By
removing polling entirely, prod and staging share one
architecture: a pure HTTP server validated against whatever
BOT_TOKEN is provided.

Changes:
- delete bot.py
- start.py: stop spawning the bot subprocess; load token, start
  server, optionally start cloudflared. WEBAPP_URL still skips
  the tunnel.
- flake.nix / requirements.txt: drop python-telegram-bot.
- README: rewrite to reflect Mini-App-only architecture.

The prod systemd unit doesn't need to change — its ExecStart is
`python start.py`, which now boots only the server (+ no tunnel
since WEBAPP_URL is set in the unit env).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 13:40:28 +02:00
Danny
aa43e492c3 docs: shipyard uses existing shipyard_poc_bot, not a new bot
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 13:12:30 +02:00
Danny
418a53986d docs: tick off shipyard staging in ROADMAP
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 12:49:54 +02:00
Danny
967c7880fc chore: prefer BOT_TOKEN env var over secrets file
Backward-compatible reorder: env var wins, then file. This lets
multiple instances on the same host (prod + shipyard staging)
each load a distinct token via systemd EnvironmentFile, instead
of fighting over the single ~/.secrets/bigbiggerbiggestbot file.

Also documents the new two-environment workflow in README.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 12:44:25 +02:00
DannyDannyDanny
c0caf6cdf4 Merge branch 'feat/webapp-url-override' 2026-04-24 12:47:16 +02:00
DannyDannyDanny
c5569fd49e start.py: skip cloudflared tunnel when WEBAPP_URL is set in env
Allows the bot to run behind an external reverse proxy (e.g. a VPS
running Caddy on a public TLS-terminated domain forwarding to
localhost:$API_PORT over a private overlay network) instead of
spinning up an ephemeral trycloudflare.com quick tunnel. Set
WEBAPP_URL=https://your.domain/... in the service environment and
start.py will use it verbatim and skip spawning cloudflared.

Behavior unchanged when WEBAPP_URL is unset: still launches cloudflared
and picks up the auto-generated trycloudflare.com URL as before.
2026-04-24 12:47:16 +02:00
Danny
4320b14441 feat(webapp): settings toggle for the +/- weight button
Users on devices with a proper numeric+sign keyboard (most Android,
desktop) don't need the +/- button and may find it clutter. Added
a "Negative weight button" toggle in Settings; default on.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:28:57 +02:00
Danny
6f9d04a0bf docs: add super subtle animations item to ROADMAP
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 11:25:24 +02:00
Danny
4d210bc2e8 docs: tick off feedback #9 in ROADMAP
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 15:36:56 +02:00
Danny
9673bcbb7c feat(webapp): +/- sign toggle on weight input (feedback #9)
iOS numeric keypad has no minus key, so users couldn't enter
negative weights for assisted bodyweight exercises (e.g. -20 kg
assisted pull-ups).

Added a small +/- button next to the weight input that flips the
sign of whatever's there (or seeds a lone "-" when the field is
empty so the user can type digits after it). Active state lights
up the button so you can see at a glance that the value is
negative. Parses and stores as a negative REAL in SQLite; existing
display code (fmtWeight) already handles negative numbers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 15:36:21 +02:00
Danny
a127cc8f82 docs: add negative-weight input item to ROADMAP (feedback #9)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 15:34:42 +02:00
Danny
194d677777 docs: add editable workout timestamp item to ROADMAP
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:44:46 +02:00
Danny
585463dd74 docs: mark profile/settings partially shipped
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:37:34 +02:00
Danny
6d1de53b2e feat: profile/settings (rest timer toggle)
Settings infrastructure + one working preference:

- New user_settings table (JSON blob per user, so adding
  future keys needs no migration).
- db.get_settings / update_settings helpers (merge semantics).
- GET/PUT /api/settings endpoints.
- New Settings tab in the Mini App with a rest-timer on/off
  toggle. Setting is loaded on init and written through on
  change; the rest-timer display now respects it.

Units (kg/lb) and language are intentionally left unwired for
now — each needs end-to-end display/input changes and deserve
focused passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:36:06 +02:00
Danny
9636d6870e fix(webapp): add delete button to workout history cards
Previously only the bot's /delete command could remove a workout —
the Mini App history view had an edit pencil but no delete button.
Added a trash-icon button next to the pencil with a native
Telegram confirm dialog before deleting.

Also added the per-user workout number to each history card header
(e.g. "#3 · Sun 19 Apr 2026, 14:30") so users can correlate with
the number shown in save toasts and /history.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:33:24 +02:00
Danny
24b8ceaac2 docs: tick off rest timer in ROADMAP
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:16:00 +02:00
Danny
dabceeeb18 feat(webapp): rest timer in sets header
Shows mm:ss since the last set was added for the current exercise.
Purely client-side — no round trip to the server. Resets on new
exercise, clears when no current exercise or 0 sets, and survives
draft restore.

The settings-toggle gate is still TBD (Profile/settings feature
isn't built yet); the timer is small and muted enough to keep
always-on in the meantime.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:15:27 +02:00
Danny
f36912febe docs: tick off interaction/event logging in ROADMAP
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:04:28 +02:00
Danny
52277e99de feat: interaction / event logging
New `events` table with (user_id, kind, created_at, data JSON).
Instruments:

Bot:
- cmd.start, cmd.history, cmd.stats, cmd.delete, cmd.export, cmd.feedback
- workout.save (source=text), workout.delete (source=bot)

Server:
- workout.save (source=webapp), workout.update, workout.delete (source=webapp)
- POST /api/events for Mini App client-side events

Mini App:
- miniapp.open on init()
- set.add on addSet(), with exercise name / reps / weight
  (per-set timestamps unlock the rest-timer feature later)

log_event swallows failures so it can never break a caller.
get_events supports user_id / kind filtering for inspection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:03:42 +02:00
Danny
1d3e7d5e80 docs: tick off global autocomplete in ROADMAP
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 13:57:28 +02:00
Danny
0e4bf65d5b feat: global exercise-name autocomplete
Autocomplete now draws from every user's logged exercises, not
just the requesting user's history. New users get suggestions from
day one.

- db.get_all_exercise_names(): case-insensitive grouping, ordered
  by usage count desc, alphabetical tiebreak, excludes names that
  only appear in soft-deleted workouts.
- server.api_get_exercise_names simplified to a one-liner.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 13:56:53 +02:00
Danny
3209136189 docs: tick off #7 in ROADMAP
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 22:33:04 +02:00
Danny
bc1d44b556 feat: per-user workout numbering (#7)
Display workouts as "#N" based on each user's own ordered list of
non-deleted workouts (rank by timestamp ascending). Global auto-
increment id stays the primary key, used only internally and in
exports. User-visible surfaces now all use the per-user number:
- /history listing
- /delete now accepts the per-user number
- Save confirmations (bot text and Mini App toast)

Deleting a workout renumbers the later ones downward, as expected
for a pure display transform.

New db helpers: get_user_workout_number, resolve_user_number, and
get_workouts now includes user_number per row via SQLite window
function.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 22:32:24 +02:00
Danny
8e22cdb29d docs: clarify version-display ROADMAP entry (format + fallback)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 19:34:04 +02:00
Danny
7f061c5b11 feat(server): version badge now shows ISO date + short SHA
Format: 'YYYY-MM-DD <short-sha>'. Preferred path uses
`git log -1 --format='%cs %h'`.

Fallback path (no git on PATH) resolves HEAD (loose or packed refs)
and parses the loose commit object via zlib to extract committer
date, converted to UTC date. Degrades gracefully to SHA-only if
the commit object is packed or unreadable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 19:31:30 +02:00
Danny
53dbaa1d4e docs: tick off #1 and version display; leave semver tags open
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 19:26:58 +02:00
Danny
8e9a2b7208 ui(webapp): split current exercise and next-exercise input into separate cards
The two concerns sharing one gray block felt crowded. Giving the
next-exercise input its own card creates clear visual separation
and a natural "what comes next" feel.

Drop the now-redundant top-border/padding on .sets-section since
it's its own card now.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 18:52:26 +02:00
Danny
512c565f54 ui(webapp): move next-exercise input to bottom of add-exercise card
The empty "Exercise name" field above the current exercise's sets
felt counter-intuitive. Putting it below the sets reads naturally:
current exercise on top, prompt to start the next one at the bottom.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 18:46:24 +02:00
Danny
0872d545d5 fix(server): pure-python fallback for version when git is not on PATH
The systemd service environment on the NixOS deploy host has a
minimal PATH without git, so subprocess.run(['git', ...]) fails
with FileNotFoundError and /api/version returned 'unknown'.

Fall back to reading .git/HEAD directly (resolving the ref) and
returning the short SHA. Loses tag/dirty detection, but unblocks
the version badge in deployed environments.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 18:39:26 +02:00
Danny
b4d76b0eca feat(webapp): show running version in Mini App footer
Compute `git describe --tags --always --dirty` at server startup
and expose via unauthenticated /api/version. Render as small muted
text at the bottom of the Mini App so the running version can be
confirmed at a glance.

Once tags exist, the badge will show e.g. v0.1.0 or v0.1.0-3-gSHA.
Until then it shows the short SHA.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:38:52 +02:00
Danny
31d426d53e docs: promote version display to Next tier (after #1)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:29:50 +02:00
Danny
41a1854c58 docs: add shipyard staging to ROADMAP
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:22:25 +02:00
Danny
06ff46438c docs: add interaction logging, rest timer, About section to ROADMAP
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:16:04 +02:00
Danny
4d6c4b1151 fix(webapp): hide next-exercise input until current has >=1 set
Typing in the exercise-name input while an exercise was in progress
but had no sets would silently replace it. Hide the name row in that
state so the user commits to adding a set (or explicitly removes the
exercise) before starting another.

Because the name row was the only way to abandon a 0-set exercise,
always show the "Remove exercise" escape hatch whenever a current
exercise exists.

Consolidate the save/name-row/delete visibility logic in syncEditorUI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:14:56 +02:00
Danny
fbe1f63e19 docs: add news, profile/settings, cardio tracking to ROADMAP
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:09:19 +02:00
Danny
c39e0a61a4 docs: add global exercise name suggestions to ROADMAP
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:06:16 +02:00
Danny
395b3c7bc0 docs: add semantic versioning item to ROADMAP
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:00:01 +02:00
Danny
40592ed935 docs: tick off #6 in ROADMAP.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 16:54:33 +02:00
Danny
60171d9b1c fix(webapp): show save button while first exercise is in progress
Save button was gated on workout.length, which stays 0 until a
second exercise is started (the transition that flushes the current
exercise into workout[]). Single-exercise workouts had no reachable
save path.

Gate on workout.length OR currentExercise sets, and recompute
visibility when sets are added/removed and on draft restore.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 16:53:42 +02:00
Danny
9ba5cdf769 docs: add ROADMAP.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 16:37:54 +02:00
Danny
547b07dba6 docs: add muscle emoji to README heading
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:41:40 +02:00
Danny
a4ccd9a107 docs: add LICENSE (MIT) and README
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 14:21:12 +02:00
Danny
42d79c9d90 chore(tg-fitness-bot): remove obsolete prototype subdirectory
The telegram-fitness-bot/ subdirectory was the initial scaffold,
superseded by the top-level implementation and unused since.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 11:29:59 +02:00
Danny
3703d87426 feat(tg-fitness-bot): /feedback command, feedback SQLite table
Users can send /feedback <text> to record feedback. Stored in a new
feedback table with user_id, text, created_at. Updated /start help text.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 11:22:43 +02:00
Danny
a529416599 feat(tg-fitness-bot): structured mini-app, edit workouts, draft persistence
Rebuilt Log view: exercise name input with autocomplete, per-set
entry (reps x weight), exercise cards with edit/remove. Comma and
dot both work as decimal separators. Notes field. Collapsible raw
text input as fallback.

Edit saved workouts from History (pencil icon). Loads exercises
into editor, Save becomes Update, Cancel returns to History.

localStorage draft persistence: auto-saves on every state change,
restores on reopen (24h expiry), clears on save.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:41:47 +02:00