ESP32-S3 Bench Guide
ESP32‑S3 N16R8 벤치 가이드: 실시간 핀아웃, 강의, 시뮬레이터 및 벤치 로그
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Telegram bot for German vocabulary learning (A1–C2) with SRS, 12,800+ words, leaderboards, and multi-language support. Free & open.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A production Telegram bot for vocabulary learning (A1–C2) — ten languages in any pair, six games mixed inside every lesson, natural-voice audio for every word, speech recognition, adaptive spaced repetition and a monthly competition with medals, crowns and titles. Free and ad-free.
🤖 Live bot: t.me/GenauLingua_bot
GenauLingua is a real, deployed Telegram bot. Version 2.0 went live on 14 October 2026: the bot grew from one game and one language (German) to six languages in any combination, six games, full pronunciation and speech practice. Version 2.1 "Retention" followed on 16 October 2026: it is about bringing people back — 79% of everyone who ever studied had never studied two days in a row. Version 2.2 "Ten languages" went live on 18 October 2026: Italian, Spanish, French and Czech, a new onboarding, reports on every answer and a monthly competition.
| 2.0 (14 Oct) | 2.1 (16 Oct) | 2.2 (live, 18 Oct) | |
|---|---|---|---|
| Languages to learn | 6, any pair — 30 directions | same | 10, any pair — 90 directions |
| Interface languages | 6 | same | 10 |
| Games | 6, mixed in every lesson | I build simpler at A1–A2 | same |
| Audio | every word and example, natural voice | same | two voices per new language, female or male by choice |
| First lesson | 7 questions | 7 easy cards from the 60 most common short words | 7 questions: I read · I listen · I recall, then a 3-pair board |
| Onboarding | — | — | own language (flags) → language to learn, two taps; /language; reminders on by default for newcomers |
| Messages from the bot | daily reminder to everyone who has them on | one dispatcher: about the person, ≤ 1 a day, stops after 14 days, 10% control group | same |
| Day streak | in the learner's time zone | + weekly freeze, milestones 3/7/14/30 | same; 🔥 on the home screen |
| Ranking | per learning language, medals per language | same | Month · All time · Your language; medals for the shared month (3+ lessons), a crown per language, titles from glory, a 12-piece collection |
| Reports | from the lesson summary | same | under every answer: translation / example / audio |
| Restarts | interrupted lessons | lessons survive a restart (Redis) | same |
| Metric | Value |
|---|---|
| Registered users | 255 |
| Lessons played / questions answered (all time) | 2,513 / 29,932 |
| Word base | 12,902 entries × 10 languages, A1–C2, 20 topics; 9,159–12,372 distinct words per language after merging duplicates |
| Pronunciation clips | 197,682 needed by the code, all on disk (153,738 added in 2.2) |
| Test suite | 1,408 tests |
The release was rehearsed on a copy of the live database inside the server; on the real run 19 tables of people's data were fingerprinted before and after — identical. Downtime was 13 minutes, of which the steps themselves took 2 min 17 s.
| Change | Why |
|---|---|
| Italian, Spanish, French, Czech — to learn and as interface languages. All 12,902 entries got a translation and an example in each; each language got two voices (female and male, chosen in Settings → Audio). The 4,000 most frequent words per language and the whole interface were proofread for natural phrasing (109 + 47 fixes). | Requests from learners. |
Onboarding in two taps: your own language (with flags) → the language to learn, a back arrow on both screens, /language and a command menu set from code. | A real learner picked the wrong language and could not change it. |
| First lesson: I read · I listen · I recall, twice each, then a 3-pair board — so a newcomer sees the bot's range at once. Reminders switch on by themselves for newcomers at the hour they joined (10:00–20:00), with the 10% control group kept. | Fewer questions after the first lesson; variety from the first minute. |
| Reports under every answer — translation, example or audio, one or several; +15 points for a confirmed report. | Reporting from the summary meant remembering the word. |
Competition: tabs Month (everyone) · All time · Your language; medals 🥇🥈🥉 for the shared month with 3+ lessons and a single winner per place; a 👑 crown per language for the next month (when 3+ people have 3+ lessons); titles 🌱 → 🌌 from glory (medals and achievements); a 12-piece collection; "↑ place this month" and a monthly record in the lesson summary. "You were overtaken" exists but is off (COMPETITION_NUDGES). | A lone learner of a new language would have taken gold after one lesson; status did not accumulate. |
| Release notes page in all ten languages, linked from the broadcast with the learner's language preselected. | One message, every learner's language. |
Every item below answers a number or a case from production on 15 October 2026.
| Change | Why |
|---|---|
| Engagement messages replace the old reminders. After a break the bot writes on day 1, 3, 7 and 14, then stops; a newcomer who never started gets an invitation on day 1, 3 and 7. Those who turned reminders on get them at their own hour while the break is ≤ 2 days. Never more than one a day, at least 12 h apart, daytime only. Each message is about the person — their streak, their numbers, one of their words under a spoiler as a mini-question. "Don't send" is final; blocking the bot counts as "don't send". | The old daily reminder was "a note about the bot instead of about the person"; 24 people blocked the bot after the 2.0 reminder wave. |
| Control group and outcomes. 10% of people get no ladder messages; every decision is logged. Admin → "Messages" shows who came back within a day, who tapped the button, who unsubscribed or blocked — next to the control group. | "Track it — so we know whether it works or harms." |
| Easy first lesson: 7 cards from the 60 most frequent short words (≤ 10 letters, single word), respecting the chosen topic. | 21 of 32 abandoned lessons were abandoned on question 0–2; a Polish newcomer got "mieć na myśli" as the very first word. |
| Streak: one missed day a week is frozen, not reset; milestones at 3, 7, 14 and 30 days; 🔥 next to the name in the ranking from 3 days. | Streak mechanics shown to work elsewhere; approved by the owner. |
| Known words faster: a word answered correctly at first sight is learned after one more correct check (two in a row instead of three). There is no "I know this" button — an accidental tap is a guess. | Learner feedback: "I don't need to prove it three times." |
I build at A1–A2: phrases of up to 5–6 words, one trap, never another form of a word in the phrase (gehe/geht). | I build: 44% correct overall, 25% at A1, ~51 s per answer. |
| I speak: the card says where to record a voice message (hold 🎤 at the bottom right of Telegram). | Two voice attempts in a whole day of production. |
| Lessons in Redis: a deploy or restart no longer interrupts a lesson; an answer caught mid-restart does not lock the question. | Restarts on 15 October twice cut people's lessons. |
Journey log: taps, questions, voice attempts (what was heard, similarity, attempt number), skips and level changes — app/scripts/journey.py --user <hash> --day <date>. |
A lesson is 15 questions behind a single "Learn words" button. Games alternate inside the lesson — the learner never has to choose:
| Game | Task | Skill |
|---|---|---|
| 📖 I read | word → pick the meaning | recognition |
| 💭 I recall | meaning → pick the word | recall |
| 🎧 I listen | audio → pick the word | listening |
| 🎤 I speak | say the word in a voice message | production, pronunciation |
| 🧩 Find the pairs | match 5 words with 5 meanings on a board | consolidation |
| 🧱 I build | assemble the example sentence from word tiles with traps | grammar, word order |
Games unlock per word: a new word always arrives as a card; recall and listening open after one correct answer, speaking and sentence building after two. A newcomer's first lesson is 7 questions — I read, I listen and I recall twice each, then a 3-pair board; from the second lesson the regular mix takes over (15 questions, one of them a 5-pair board).
I build adds grammatical twins as traps (der/den/dem, ist/sind, er/sie) — one at
A1–A2 with phrases of up to 5–6 words, 2–5 at higher levels — never gives the first word away with a capital
letter, and only uses words at or below the learner's level. The result screen marks
every word ✓/✗, lists what was extra and what was missing, and says "right words,
different order" when only the order differs.
I speak uses self-hosted speech recognition (faster-whisper on the bot's own server): two attempts, then the question is skipped without a penalty — a microphone problem is not a mistake. The card says where to record (hold 🎤 in Telegram's input field). Listening and speaking both have a Skip button for when sound is not an option.
Every word and every example sentence is voiced with a natural voice; the learner can pick
a female or a male voice (Settings → Audio). All clips the code can ask for are generated in
advance and stored on the server; on first use a clip is uploaded once and then cached as a
Telegram file_id — instant playback, no server bandwidth. Listening questions use a neutral
clip title so the answer is not printed on the player. A slow reference clip is one tap away
in the speaking game.
A home screen with one skill bar per game shows the gap between skills ("I recognise it in writing but not by ear"); the detailed view adds learned / in progress / difficult, grammar accuracy, lesson history and overall progress.
An admin panel with analytics, cohorts, churn, "started / completed" lessons, "came back today out of those active in the last 7 days", per-language broadcasts and report moderation; a "Messages" report compares people who got an engagement message with the control group. A per-person journey log answers "where exactly did this person stop". Release notes go out in every learner's interface language, and the bot sends follow-up reports on how many came back.
app/
├── bot/
│ ├── handlers/
│ │ ├── quiz/ # one module per concern:
│ │ │ ├── lesson.py # lesson flow: start, next question, repeat mistakes
│ │ │ ├── lesson_state.py # FSM keys, the per-chat lock, restart-safe answer claims
│ │ │ ├── question.py # question and answer screens
│ │ │ ├── choice.py # 📖 💭 🎧 answers by button
│ │ │ ├── listen.py # 🎧 audio and its fallback to text
│ │ │ ├── speech.py # 🎤 voice answers, slow reference, skip
│ │ │ ├── match.py # 🧩 pairs board
│ │ │ ├── build.py # 🧱 sentence building
│ │ │ ├── summary.py # lesson summary, praise, level offer
│ │ │ ├── expired.py # buttons of a lesson the bot no longer remembers
│ │ │ └── home / stats / settings / report / help
│ │ ├── leaderboard/ # monthly, all-time, tables
│ │ ├── reminders.py, start.py, admin.py
│ ├── routers.py # the router set and its ORDER — one source for bot and tests
│ └── keyboards/, states.py
├── services/ # business logic: quiz engine, SRS, scoring, leaderboards,
│ # build_service, speech_check, audio, word groups,
│ # nudge_plan / nudge_texts (who gets which message), journal
├── database/ # SQLAlchemy 2.0 models (19 tables), enums, async session
├── schedulers/ # engagement messages (nudges.py), monthly season rollover
├── locales/ # uk / en / de / pl / cs / es / fr / it / tr / ru
├── testing/ # fake Telegram + the real dispatcher, shared by tests and tooling
├── scripts/ # 127 tools: acceptance suites, deploy, wordbase, TTS, reports,
│ # journey log, message forecast
└── main.py
alembic/ # 49 migrations
tests/ # 1,408 tests
deploy/ # preflight and backup scripts
About 36k lines of application code (including ten locales), 30k lines of tooling, 21k lines of tests.
Design principles
QuizChannel, counted_channels()); the scoring formula lives in
MonthlyStats.calculate_monthly_score(); who is ranked in a month is one predicate
(ranked_in_month). Every one of these was once duplicated, and every duplicate drifted.services/ and is tested without Telegram.nudge_plan.decide) decides who
gets what; a conditional UPDATE claims each send, so two processes never send twice.Verification beyond unit tests — every change is also checked by acceptance suites that play the real dispatcher on the real word base:
| Suite | What it proves |
|---|---|
audit_round --full | 38 lesson compositions played to the end: numbering, counters, mistakes, reports, channels and points agree |
verify_quiz_channels | every game builds correctly in every language pair |
verify_quiz_round --full | 960 lessons, 14,400 questions across all ten languages — every question has a word or a sound |
audit_accounting | 17 checks across five accounting layers: answers ↔ lessons ↔ progress ↔ month ↔ all-time |
verify_buttons | no button leads nowhere, menus work in every interface language |
release_snapshot --compare | a deploy loses nothing: users, progress, answers, awards |
nudge_forecast | who would get which engagement message, hour by hour — nothing is sent |
Tests are written "break it on purpose": a fix is accepted only after the test was seen to fail with the fix removed.
| Layer | Technology |
|---|---|
| Language | Python 3.11 |
| Telegram | aiogram 3.15 (async) |
| Database | PostgreSQL 16, SQLAlchemy 2.0 async, asyncpg, Alembic |
| Lesson state | Redis 7 (append-only), aiogram RedisStorage |
| Speech recognition | faster-whisper (CTranslate2, int8, on CPU) |
| Text-to-speech | edge-tts |
| Scheduling | APScheduler |
| Config | Pydantic Settings |
| Errors | Sentry (optional) |
| Deployment | Docker Compose on Hetzner (EU) |
git clone https://github.com/Kanaki-K/GenauLingua.git
cd GenauLingua
cp .env.example .env # BOT_TOKEN, ADMIN_USER, database credentials
docker compose up -d --build
Migrations run automatically on container start. The speech-recognition model is baked into the image at build time, so the first voice answer does not wait for a download.
Run the tests (a PostgreSQL test database on port 5433 is expected; override with
TEST_DATABASE_URL):
pytest
DEPLOY.md is the step-by-step procedure used for the 2.0, 2.1 and 2.2 releases:
preflight, backup, snapshot, migrations, word-base transfer, the "nothing lost" comparison
before the accounting repair, and rollback. Each release is rehearsed on a copy of the
production database first; since 2.2 the rehearsal runs inside the server on a temporary
database, so no copy of people's data ever leaves it. For 2.1 it adds a forecast of who gets
which message in the next 48 hours (nudge_forecast) — run after the migration and before the
bot starts, so a release never sends a wave of messages. Release notes go out with
send_release_notes — every learner in their interface language, resumable without duplicates.
Releases are tagged v2.0, v2.1, v2.2.
The project notes are in Russian, the owner's language:
STATE.md · ROADMAP.md · REWARDS.md
(the competition) · LANGUAGES_PLAN.md · WORDBASE_REVIEW.md ·
AUDIO_STORAGE.md · DEPLOY.md.
All secrets are supplied via environment variables in .env; no secrets are stored in the
repository. See .env.example.
İ/ı,
German noun capitalisation, Polish impersonal politeness, French non-breaking spaces,
formal address everywhere — Sie, vous, usted, Lei, Vy).The bot processes only what it needs to run (Telegram ID, learning progress, preferences). Details: PRIVACY_POLICY.md.
Built as an independent product. The bot is free to use and runs without ads.
| 15 October's analysis had to reconstruct a path from indirect evidence. |
| Word base: slang meanings moved behind a neutral first meaning (13 words); 19 example translations aligned with the German original (e.g. meinen). | A 750-pair check against the German original found ~2% flawed examples. |
| One look for every message: hyphen instead of a long dash, a blank line between lines, no full stop at the end, formal address in all six languages. | Owner: "full stops visually constrain". |