This is a Telegram bot project.
The project has a modular structure where each component has a clearly defined responsibility.
/bot/callbacks/: ContainsCallbackDatafactories for handling inline button clicks./bot/configs/: Manages configuration usingpydantic-settings. Settings are loaded from environment variables and a.envfile./bot/handlers/: The main logic for processing incoming messages and callbacks from Telegram. TheHandlersclass contains all the handler methods./bot/i18n/: Implements internationalization (i18n). Contains translation files (ru.py,en.py) and theLexiconclass for accessing texts in the desired language./bot/keyboards/: TheKeyboardsclass for generating Reply and Inline keyboards./bot/middlewares/: Middleware.i18n.py: Determines the user's language.throttling.py: Limits the rate of user requests.context.py: Gathers dependencies into a single context object.
/bot/services/: The service layer and business logic.bot_service.py: An example of a service with business logic (e.g.,upper()).context.py: TheHandlerContextdataclasswhich aggregates all major dependencies.
/bot/states/: State definitions for the FSM (Finite State Machine) usingStatesGroup.main.py: The application's entry point. This is where the initialization ofFastAPI, the bot, the dispatcher, and the registration of all handlers, middlewares, and dependencies occurs.
The project is built on the aiogram 3, FastAPI, and Pydantic stack. It uses webhooks to receive updates from Telegram, which is the preferred method for a production environment.
Instead of passing multiple dependencies into each handler, we use a single context object.
How it works:
- Singleton Initialization: In
main.py, within theregister_workflow_datafunction, we create a single instance of all key services (Keyboards,BotService,Lexicon) and store them indp.workflow_data. - Context Assembly: The
ContextMiddlewareruns with every incoming update. It retrieves the previously created services fromdataand packs them into theHandlerContextdataclass. - Injection into Handler: The prepared
ctx: HandlerContextobject is automatically passed as an argument to any handler that requests it via a type hint.
Agent Instruction:
When adding new functionality that requires access to services (e.g., a database), follow this pattern:
- Create a new service class (e.g.,
DatabaseService).- Add an instance of it to
workflow_datainmain.py.- Add a new field to the
HandlerContextdataclass.- Add the initialization for this field in
ContextMiddleware.- You can now access your service in any handler via
ctx.database_service.
- Telegram sends a JSON update to the FastAPI endpoint (
/webhook/tg). - The
webhook_handlerinmain.pyvalidates the data and passes it todispatcher.feed_update(). - The dispatcher sequentially runs the outer middlewares:
I18nMiddlewaredetermines theuser_lang.ThrottlingMiddlewarechecks the request frequency.ContextMiddlewareassembles thectxobject.
- The dispatcher finds a suitable handler based on filters (
CommandStart,F.text, etc.). - The dispatcher calls the found handler, passing it all necessary data, including
message,state, and ourctx.
Maintaining a consistent code style is critically important for the project's readability and maintainability.
- Language: Python 3.12+, line length 120. Strict typing is non-negotiable.
- Type Hinting: Mandatory. All new functions, methods, and variables must have strict type annotations. This is the foundation for DI and static analysis.
- Naming:
- Classes:
PascalCase(e.g.,BotService,HandlerContext). - Functions, methods, variables:
snake_case(e.g.,start_command,user_lang). - Constants:
UPPER_SNAKE_CASE(e.g.,LEXICON_RU). - Modules:
snake_case(e.g.,bot_service.py).
- Classes:
- Internationalization: All user-visible strings (messages, button text) must be moved to the lexicon files in
/i18n/and retrieved usingctx.lexicon.get_text(). Do not hardcode strings in handlers and keyboards.