Conversation
Editors on Windows hosts were rewriting the whole tree to CRLF, which turned 32 unrelated files into noise in every diff. Normalize on LF at the git level and mirror it in the editor config.
Describe the trophy submission, avatar rotation and monthly survey flow, the command reference, configuration, installation, project layout, database schema and the Fly.io deployment pipeline.
A Python 3.12 container with a PostgreSQL service, so the bot can be run against a local database without installing anything on the host.
get_chat_settings() asked for exactly one *Default* record, so the query raised NoResultFound in every chat where no default avatar had been set.
The bot session used to be closed twice through asyncio.run() on an already closed loop, and neither the scheduler nor the connection pool was released at all. Close the session in a finally block, release both resources from a dispatcher shutdown hook, and let Request own its engine so it can be disposed and rebuilt. Reconnection is now verified instead of assumed: check_db_connection() re-runs the probe query after reconnecting and reports the outcome, and the engine is created with pool_pre_ping so stale connections are detected before a query uses them.
get_avatar() removed the record as part of reading it, so a failure in set_chat_photo or send_photo — losing admin rights, for instance — threw the trophy away without ever showing it. Return the record id instead and delete it from change_avatar once the rotation has actually succeeded; a failed rotation now simply retries on the next tick. The result is returned as a QueuedAvatar named tuple: six positional values, three of them strings in a row, were easy to unpack wrongly.
change_avatar() persisted the next date but never updated where_run, so every rotation kept computing the interval from the same original date.
A malformed date or a non-numeric interval raised straight out of the handler; both now answer with the expected format. /history no longer falls back to the caller's username, which returned nothing for users who have none — it looks the caller up by user id instead.
/set_date passes no interval and /set_delta passes no date, but the missing half was only filled in when a job already existed. For a chat that had no job yet — start() skips chats the bot was not a member of at boot — /set_date reached IntervalTrigger with days=None and raised TypeError. A partially filled where_run entry was just as fatal later, since change_avatar adds delta to date unguarded. Resolve both values against the stored state before branching, falling back to now and to DEFAULT_DELTA, and always write the entry back whole. This adds the first tests to the project, so it also brings the pytest setup: the root conftest supplies the environment variables that src.bot.settings reads at import time.
Also install the native libraries WeasyPrint and pdf2image need: the lint step only parsed the sources, while the tests import them.
CI resolved pylint 3.3 from the compatible-release range while the dev container ships 4.0.6, and the two disagree about R0917: 3.3 counts self as a positional argument, 4.0 does not, so three signatures that pass locally failed the lint step. Pin all three dev tools exactly.
The root carries an __init__.py, so pytest treats it as a package and inserts its parent directory instead of the root itself. `import src` therefore only resolved under `python -m pytest`, which injects the cwd on its own; the bare `pytest` that CI runs failed to collect at all.
aiogram 3.5 -> 3.30 brings two breaking changes that the code had to follow: Bot() no longer takes parse_mode, so the default now goes through DefaultBotProperties, and Telegram models are frozen, so the media caption is set at construction instead of being assigned afterwards. Also declare python-dateutil, which jobs.py imports directly and which only arrived as a transitive dependency of pandas, and drop the typing backport — it targets Python below 3.5 and has no business in a 3.12 image.
NickLyrick
approved these changes
Aug 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.