Dieta e Treino: Telegram bot & web dashboard
Telegram-first food and training log, on-demand dashboard, opt-in Groups, and a Stripe trial.
Rejected path: Build on WhatsApp Business API / Cloud API instead (higher ops overhead for this scale and use case)
Overview
Dieta e Treino started from a simple idea: keep food and training in the chat people already use, then open a panel only when they want the bigger picture. The same bot works in private chat or inside Telegram groups. Meals go through Ana (text, photo, or voice); workouts through Renan. When someone wants day totals, ~70-day consistency, or a light comparison with peers, they ask for the panel and open the link. Day-to-day logging stays in chat; the web app covers aggregated views, marketing, and billing.
Problem
Memory skews toward extremes. Without a steady record it is hard to see what you actually eat across days, or how training volume and frequency move over time. Consumer diet apps add installs and logins that break the habit before it starts.
Constraints
- Self-hosted workflow runner for the bot (no managed cloud bill). Production ships as one bundled worker while the repo stays modular.
- Telegram webhook delivery is time-bounded; the handler must finish in time and stay idempotent so retries do not double-write. Shared groups add noise, so heuristics catch food and workouts without requiring a slash command on every line.
- Dashboard access is a short-lived link from the bot. Privileged database access stays server-side. Group ranking is opt-in in the bot, never automatic; peers see aggregates, not meal text.
- Bot and dashboard must agree on each user’s local calendar day. Clock-sensitive UI renders on the client to avoid SSR/hydration mismatch.
- Commercial path mirrors StackBrief (Checkout / Portal / webhooks, 14-day trial without card, soft-block of bot writes after trial) while keeping one Telegram identity = one subscription and privacy rules intact.
Architecture
Telegram triggers a self-hosted Windmill webhook; a bundled Bun job runs the bot. Gemini parses and transcribes; Postgres (Supabase) holds state. The webhook path finishes with a safe response after persist so retries stay clean. The dashboard is Next.js on Vercel with a Portuguese marketing landing (Ana/Renan, pricing, FAQ) and a token-gated panel: macros, heatmaps, training volume, Groups, and subscription. Structured events feed a scheduled quality digest; optional Sentry covers production errors. Lightweight ops alerts stay out of the user path.
Key Decisions
Telegram instead of WhatsApp as the chat surface
People already live in messaging apps; the goal was no new install. Telegram’s Bot API fits a small, iterative product: straightforward webhooks, bots in private chats and groups, and room to change how the bot behaves without a business-console workflow. WhatsApp’s path for comparable automation is heavier (Business API, stricter messaging rules, and cost signals that did not match this product).
- Build on WhatsApp Business API / Cloud API instead (higher ops overhead for this scale and use case)
- A standalone mobile app (rejected: extra install and separate login)
Self-hosted Windmill instead of cloud or a custom Node service
Workflows, retries, and schedules in one place; cost is the box I already have. Tradeoff is ops on that host.
- Windmill Cloud or Railway/Fly with hand-rolled workers (extra cost or glue for the same outcome)
Named agents (Ana / Renan) instead of a single generic bot voice
Food and training are different intents in chat. Named agents make that split obvious in the bot and on the landing page.
- One unnamed assistant for everything (cheaper copy, weaker mental model)
Gemini for text, image, and voice; heuristics for group noise
One API key and quota. Keywords and patterns filter the group so we do not call a model on every irrelevant line.
- Slash-only commands or a classifier on every message (friction or cost)
Productize with Stripe trial + Telegram subscribe path
Early users proved the loop; a public site and paid tier need the same access rules as StackBrief without breaking the bot-first habit. Trial starts on authorize; after trial, soft-block writes while the panel still offers the subscription screen.
- Keep the product invite-only with no billing
- Force web signup before any Telegram use (rejected: breaks the core promise)
Tech stack
- TypeScript (Bun on Windmill)
- Windmill (self-hosted)
- Supabase (PostgreSQL, RLS)
- Google Gemini
- Telegram Bot API
- Next.js (App Router, Server Components)
- React, Tailwind CSS
- Stripe Checkout / Portal / Webhooks (BRL)
- Vercel (landing + dashboard)
Impact
Logging stays in Telegram with no second install or login. Day macros, ~70-day consistency, and training volume open behind a short-lived link; peer comparison only after explicit opt-in. Billing follows trial → Checkout → soft-block while the bot stays the primary surface.
Learnings
- Tuning who stays silent in a group chat took more iterations than swapping models.
- Duplicated date logic plus a test was more reliable than a shared package we would not maintain.
- Server Components and narrow types do most of the privacy work before the browser runs.
- At this scale, structured events plus a weekly digest beat digging through raw logs for errors and latency.
- Once the loop works, naming and landing copy still have to read as a product, without promising clinical accuracy.
- Billing for a Telegram-first product is mostly access rules; Checkout should not ship ahead of trial and soft-block behavior.
Product snapshots
Live at dietaetreino.erbsu.com. Logging stays in Telegram; the panel opens from a bot link. Screens below are from the live product (Portuguese UI).
Telegram with Ana and Renan
Ask Ana about today’s macros (or Renan about training). She answers in chat and offers Ver no Dashboard when you want the full summary.

Summary and diet consistency


Training progress


Groups and peer progress
If you authorize Telegram groups in the bot, the Groups tab lists those chats and shows each member’s diet and workout consistency side by side: the same heatmaps as your own view, plus safe aggregates so you can see who is consistent or falling behind; not the full text of someone else’s meals. Opt-in is explicit (never automatic).

Subscription
