.schet
DotMath
Solve one problem and watch the same result land on four leaderboards at once.
the essence
A bilingual Telegram mental-math trainer built around retention: day-streaks, reminders, and a leaderboard that reads the same result four ways: streak, solved, accuracy, and a weighted score where harder problems count more. A shared daily challenge pits everyone against the same set, and retry-mistakes assembles a separate session from whatever you missed.
Under the hood
An interactive breakdown of the core mechanics. Pick a domain to see how it works inside.
Pick what to train.
Set up
what to trainSolve
problem 1 of 7do it in your head and pick the answer
misses pile up: 'retry mistakes' would build a fresh set from those only
Board
one result: four cutslongest no-skip run
- Anya 0
- Boris 0
- Vera 0
- you you 0
Day streak
last 7 days- day with a correct answer
- a skip resets the streak
- a 2nd session that day does nothing
current streak: 4 d.
ProblemGenerator · DIFFICULTY_CONFIG · StatsService: 4 cuts · weights 1·2·3 · daily challenge · aiogram 3 · PostgreSQL + asyncpg · Redis FSM · APScheduler · ~9k lines · RU/EN
scale
size and timeframe: what stands behind the product.
architecture
Update source; the bot reaches it via long polling
- DotMath bot polling
APScheduler cron sends reminders (Europe/Moscow)
- Telegram API cron
Dispatcher: handler routers, FSM, service DI
- Problem gen
- Scoring · top
- PostgreSQL 16
- Redis · FSM
7 ops x 3 levels; seeded RNG for the daily challenge
Four-way ranking, difficulty-weighted score
- PostgreSQL 16
pg_dump every 12h, recent dumps kept on the host
- PostgreSQL 16 pg_dump
Active-training FSM; AOF survives a restart
Users, sessions, problems and the daily challenge
stack
- Language 1
- Framework 4
- Data 3
- Infrastructure 5
- Client 1
Language
- Python 3.12
Framework
- aiogram 3.15
- SQLAlchemy 2.0
- Pydantic 2
- APScheduler 3.10
Data
- PostgreSQL 16
- Redis
- asyncpg
Infrastructure
- Alembic 1.14
- Docker
- GitHub Actions
- pg_dump backups
- testcontainers
Client
- Telegram Bot API
what it does
the product's key capabilities right now.
Drills
Seven operation types, from addition to powers and square roots, across three difficulty levels. Pick an answer from the choices or type it yourself.
The level changes both the number range and the set size: easy gives 5 problems and 2 choices, medium 7 and 3, hard 10 and 4. Wrong choices are picked as plausible neighbours (a Gaussian spread around the answer), and in the type-the-answer mode you enter the result yourself.
Daily challenge
One shared set of 10 problems for the calendar day, a single attempt per person, and a separate daily leaderboard by correct answers and time.
The set is seeded, so everyone gets the same one. UNIQUE on the date with ON CONFLICT DO NOTHING makes the first click race-safe, and UNIQUE on (user, date) enforces exactly one attempt. The day boundary is Europe/Moscow.
A four-way leaderboard
The same result reads four ways: streak, total solved, accuracy, and weighted points where harder problems count more.
Weighted points: easy × 1, medium × 2, hard × 3 over correct answers, so a different person can lead each cut. You can hide your name in the top, and the list paginates.
Habit and retention
A day-streak holds until you skip a day; configurable reminders bring you back, and Quick Start launches your favorite mode in one tap.
A day with at least one correct answer extends the streak; a skip resets it, and a second session that day does nothing. Reminders are presets (morning, lunch, evening, or three times a day) plus custom times on an in-process APScheduler.
Retry mistakes
A separate session is assembled only from the problems you got wrong or skipped, and correct answers stay hidden during it.
Every problem is stored with its formatting and metadata (remainder, exponent, radicand), so retry rebuilds it exactly rather than as garbage like "144 √ 0".
RU + EN and restart resilience
Full localization with an on-the-fly language switch, active-training state in Redis, and daily database backups.
The active-training FSM lives in Redis with AOF, so a session survives a container restart; without Redis it falls back to MemoryStorage for local dev. pg_dump writes a dump every 12 hours and keeps the last 20.
timeline
how the product grew from its first version.
-
14 Feb 2026
.schet kickoff
MVP in a day: 7 operations × 3 levels, the type-the-answer mode, a sorted leaderboard, and APScheduler reminders (presets + custom times).
-
4 Mar 2026
Admin panel and leaderboard privacy
Admin panel, name-hiding in the top with pagination, and password-gated backup export from chat.
-
13 May 2026
SQLite → PostgreSQL
Full migration to PostgreSQL, pg_dump backups every 12 hours, and a UI refactor.
-
30 May 2026
Daily challenge, mistake review, production wiring
A shared daily challenge and retry-mistakes, Quick Start with a favorite mode, anchor-message UX on CallbackData; Redis FSM, Docker, and CI.
-
16 Jun 2026
Closed network, Postgres-only
The Docker image gets hardened onto tini with a non-root user, Postgres and Redis lose their last exposed port, CI gains an image-build job. Right after, SQLite tooling is dropped entirely: the project is left with a single database, Postgres.
-
Jun 2026
Now: secrets and permissions under lock
Container and CI permissions get tightened and the backup password gains a timing-safe check; right after, pg_dump moves off a logged connection string onto an environment variable, the bot's healthcheck runs off a heartbeat file, GitHub Actions get pinned to a commit, and SECURITY.md opens a private vulnerability-disclosure channel via GitHub Security Advisories.