ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
You can contact @solovyinamova_bot right away.
Translation pending
EducationPython · ProcfileGitHub @nktvlchkOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Telegram bot that helps Russian speakers write natural Ukrainian: live translation, mistake checking, and spaced-repetition reminders
Loading the full README. The saved excerpt is shown below.
Ukrainian is often called «солов'їна мова», the nightingale language. A Telegram assistant for Russian speakers who are switching to Ukrainian and want to write more naturally, understand their mistakes and remember the corrections. It is a learning aid. It does not replace a professional translator or a teacher, and its model-based checks can be wrong. The screenshot is a mock-up with synthetic text, not a real conversation.Read the full README ↗
Try it in Telegram: @solovyinamova_bot
Ukrainian is often called «солов'їна мова», the nightingale language.
English · Українська
A Telegram assistant for Russian speakers who are switching to Ukrainian and want to write more naturally, understand their mistakes and remember the corrections.
It is a learning aid. It does not replace a professional translator or a teacher, and its model-based checks can be wrong.
The screenshot is a mock-up with synthetic text, not a real conversation.
Many people who grew up with Russian now want to use Ukrainian every day. The
hard part is not vocabulary but the small things: calques from Russian, case
government, the у/в alternation, stress. Generic translators answer
"what does it mean", not "how would a native speaker say it" or "what exactly
did I get wrong".
/start shows the mode buttons on the keyboard.мне нужно отпроситься с работы → a natural Ukrainian
phrasing, with the source of the answer and a 🔊 button.Я ходив в магазін. → the corrected text and up to
three explanations. Every correction has its own buttons:
✅ Це помилка · ❌ Тут немає помилки · ✍️ Лише стиль./practice) → up to five of those cards, answered by
typing. A match counts as Good; anything else shows the right answer and
stays due for later. /progress is the last 7 days.| Feature | How |
|---|---|
| Translation ru ⇄ uk | Claude detects the language and translates; dictionary for single known words; Google Translate / MyMemory as fallback |
/check | explanations in Ukrainian only |
| "Як сказати?" (natural phrasing) | model → MyMemory fallback |
"Перевір мене" (/check) | model returns validated JSON: max 3 corrections, error vs style, "no errors" allowed |
| Feedback per correction | stored with model name and prompt version, re-voting allowed, no duplicates |
"Мої помилки" (/mistakes) | corrections you confirmed as errors |
Review cards (/review) | Anki-like again / hard / good / easy. Deck = confirmed mistakes + words saved with 📌 or /remember |
Practice (/practice, /progress) | Up to five typed answers from cards that are due, ordered toward frequent error types. The log stores only right/wrong |
"Поясни слово" (/explain) | Ukrainian dictionary-style definition of a word or short expression, with part of speech, stress, example and synonyms; never a Russian translation |
| Photo in a private chat | Claude reads one image and explains the Ukrainian words on it. The file and caption are not stored |
| Audio | gTTS, only on button press, generated in memory (no temp files); speech synthesis, not pronunciation assessment |
| Direction | auto-detection is only a hint; unknown → the bot asks; /direction pins it |
| Privacy | /privacy, /delete_my_data, retention, no names/usernames/message history |
| Product analytics | /stats [days] for ADMIN_IDS: active/new/returning users, actions by feature, translation sources, share of "error / not an error / style" ratings, quality per model and prompt version; /export_stats sends a CSV of pseudonymous events for 30 days |
Commands: /start, /help, /tr, /say, /check, /explain, /mistakes,
/review, /practice, /progress, /remember, /direction, /privacy, /delete_my_data, /about,
/stats (admin).
In group chats the bot stays silent unless asked: it ignores ordinary
messages and answers only /tr (translate the text after the command, or the
message you reply to), /check, /say and /explain. The optional drip.py worker can
post study cards into the same group once a day, at a time drawn at random
inside a daily window.
src/bot/
main.py, app.py, config.py entry point, wiring, env validation
handlers/ Telegram only: common, translation, modes,
correction, feedback, review, admin
services/ business logic: dictionary, translation chain,
correction, tts, limits
providers/ external APIs: wiktionary, mymemory, anthropic
(shared HTTP helper with timeouts/retries)
storage/ SQLite + repositories
models/ TranslationResult, Correction, parsing
language.py, formatting.py detection, HTML escaping, message splitting
data/ dictionary + sources (see data/README.md)
scripts/ build_dictionary.py, check_secrets.py
tests/ pytest, no network
drip.py optional spaced-repetition poster for a class chat (deck hosted privately)
Every translation returns TranslationResult(text, source, confidence, metadata)
where source is the provider that actually produced the answer.
Main path (any text, direction not pinned): Claude detects whether the text is Russian or Ukrainian and translates it into the other language in one call. No language question is asked.
Single word with an exact dictionary hit: answered locally (instant, free, several meanings, part of speech) plus Wiktionary stress/definitions.
Fallback (no ANTHROPIC_API_KEY, budget exhausted, or a pinned direction):
Claude → Google Translate (official API, if GOOGLE_TRANSLATE_API_KEY is set) →
MyMemory. Every answer must look like the target language (e.g. no ы/э/ё/ъ
in a Ukrainian result), otherwise the next provider is tried. If the language
was only guessed, the opposite direction is tried too.
Як сказати?: Claude (natural phrasing) → Google Translate → MyMemory.
Providers are called one after another, never in parallel "just in case". If a provider returns the input unchanged, it is not treated as a translation.
Requires Python 3.11+.
python -m venv .venv && source .venv/bin/activate
pip install -r requirements-dev.txt
cp .env.example .env # put your own token in .env
PYTHONPATH=src python -m bot
| Variable | Required | Default | Purpose |
|---|---|---|---|
TELEGRAM_BOT_TOKEN | yes | — | from @BotFather |
ANTHROPIC_API_KEY | no (recommended) | — | Claude: main translator, /check, /explain, "Як сказати?" |
GOOGLE_TRANSLATE_API_KEY | no | — | official Google Cloud Translation API as fallback |
REDIRECT_TO_BOT | no | — | redirect mode for a retired bot: answers everything with "moved to @…" |
LLM_MODEL | no | claude-haiku-4-5-20251001 | model name (stored with each correction) |
LLM_DAILY_LIMIT | no | 500 | max paid model calls per day (all users) |
LLM_USER_DAILY_LIMIT | no | 60 | max model calls per user per day (in memory) |
ADMIN_IDS | no | — | comma-separated Telegram IDs allowed to use /stats |
DATABASE_PATH | no | data/runtime/bot.sqlite3 | SQLite file (git-ignored) |
DICTIONARY_PATH | no | data/dictionary.tsv | generated dictionary file |
STORE_RAW_TEXT | no | false | store the full checked text (off by default) |
RETENTION_DAYS | no | 180 | stored corrections are deleted after this |
MAX_INPUT_LENGTH | no | 1000 | max characters per message |
MAX_TTS_LENGTH | no | 300 | max characters for audio |
USER_RATE_LIMIT_PER_MINUTE | no | 20 | per-user request limit |
The bot exits with a clear message if TELEGRAM_BOT_TOKEN is missing; it never
prints the value.
See DEPLOY_GUIDE.md. Secrets live only in the hosting provider's variables, never in the repository.
STORE_RAW_TEXT=true).LOG_LEVEL=DEBUG); tokens/keys are redacted,
tracebacks included./delete_my_data removes everything linked to your ID; old records are
purged after RETENTION_DAYS.The project is licensed under GPL-2.0-or-later (LICENSE), because the dictionary is built from GPL-2.0 data.
data/sources/apertium/.Details: THIRD_PARTY_NOTICES.md.
ruff check . && ruff format --check .
python scripts/build_dictionary.py --check # dictionary is reproducible
pytest # no network, no real keys
pip-audit -r requirements.txt
python scripts/check_secrets.py
CI runs all of the above on every pull request (.github/workflows/ci.yml), plus gitleaks.
Out of scope for now: a full course, a voice tutor, pronunciation scoring.
MAX_CONCURRENT_REQUESTS | no | 8 | concurrent external requests |
LOG_LEVEL | no | INFO | logging level |