From 43a200288972e6d9096096056f1f433647478937 Mon Sep 17 00:00:00 2001 From: geekiot Date: Thu, 30 Oct 2025 18:39:49 +0500 Subject: [PATCH 1/6] Refactor [database]: update function for connect to db name --- src/database/__init__.py | 4 ++-- src/database/session.py | 2 +- src/main.py | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/database/__init__.py b/src/database/__init__.py index 4c8fbb8..0d430c9 100644 --- a/src/database/__init__.py +++ b/src/database/__init__.py @@ -1,4 +1,4 @@ -__all__ = ["create_session", "create_db", "drop_db"] +__all__ = ["get_session", "create_db", "drop_db"] -from .session import create_db, create_session, drop_db +from .session import create_db, drop_db, get_session diff --git a/src/database/session.py b/src/database/session.py index 4fd389c..6cd6c06 100644 --- a/src/database/session.py +++ b/src/database/session.py @@ -11,7 +11,7 @@ from .models import Base engine: AsyncEngine -def create_session( +def get_session( url: str, engine_echo: bool = True ) -> async_sessionmaker[AsyncSession]: global engine diff --git a/src/main.py b/src/main.py index 17de15b..a786e00 100644 --- a/src/main.py +++ b/src/main.py @@ -2,7 +2,7 @@ import asyncio from bot import start_bot from config import configure_logger, settings -from database import create_db, create_session +from database import create_db, get_session from redis_client import get_redis_client @@ -17,7 +17,7 @@ def main() -> None: listen_logging=settings.listen_logging, ) - session = create_session( + database_session = get_session( url=settings.database_url, engine_echo=True ) @@ -33,7 +33,7 @@ def main() -> None: start_bot( bot_token=settings.bot_token, redis_client=redis_client, - database_session=session, + database_session=database_session, ) ) -- 2.47.3 From 563668dabe82e8eed90774ad4bfe987851377a15 Mon Sep 17 00:00:00 2001 From: geekiot Date: Sat, 1 Nov 2025 14:43:29 +0500 Subject: [PATCH 2/6] Add [db middleware]: add db dispatcher middleware & update code struct --- src/bot/__init__.py | 25 ++++--- src/bot/handlers/commands/help.py | 13 +++- src/bot/middlewares/__init__.py | 15 +++- src/bot/middlewares/database.py | 23 ++++++ src/bot/services/database/__init__.py | 56 +++++++++++++- src/config/__init__.py | 3 +- src/config/logger.py | 94 ++++++++++++++++++++++++ src/config/{utils.py => settings.py} | 102 +++----------------------- src/database/__init__.py | 18 ++++- src/database/models.py | 5 -- src/database/session.py | 36 ++++----- src/main.py | 29 +++----- src/redis_client/__init__.py | 24 ++---- 13 files changed, 264 insertions(+), 179 deletions(-) create mode 100644 src/bot/middlewares/database.py create mode 100644 src/config/logger.py rename src/config/{utils.py => settings.py} (52%) diff --git a/src/bot/__init__.py b/src/bot/__init__.py index fd3daba..f9328bb 100644 --- a/src/bot/__init__.py +++ b/src/bot/__init__.py @@ -5,7 +5,7 @@ from aiogram import Bot, Dispatcher from aiogram.fsm.storage.redis import RedisStorage from loguru import logger as loguru_logger from redis.asyncio.client import Redis -from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker +from sqlalchemy.ext.asyncio import async_sessionmaker from .handlers import include_routers, registry from .middlewares import connect_middlewares @@ -15,7 +15,7 @@ from .middlewares import connect_middlewares async def start_bot( bot_token: str, redis_client: Redis, - database_session: async_sessionmaker[AsyncSession], + session_maker: async_sessionmaker, ) -> None: """ Start Telegram bot. @@ -23,7 +23,7 @@ async def start_bot( Args: bot_token (str): Telegram API bot token. redis_client (Redis): async configured client for redis. - database_session (AsyncSession): async database session. + session_maker (AsyncSession): async database session maker. """ bot = Bot(bot_token) @@ -31,20 +31,23 @@ async def start_bot( storage=RedisStorage(redis=redis_client), ) - include_routers(dispatcher) - loguru_logger.debug("Routers have been successfully included.") - - connect_middlewares(dispatcher) + connect_middlewares( + dispatcher=dispatcher, + session_maker=session_maker, + ) loguru_logger.debug( "Middlewares have been successfully connected." ) - await bot.delete_webhook(drop_pending_updates=True) - loguru_logger.debug( - "All updates for the Telegram bot have been dropped." - ) + include_routers(dispatcher) + loguru_logger.debug("Routers have been successfully included.") await registry.set_menu(bot) loguru_logger.debug("The bot menu has been successfully set.") + await bot.delete_webhook(drop_pending_updates=True) + loguru_logger.debug( + "All updates for the Telegram bot have been dropped.", + ) + await dispatcher.start_polling(bot) diff --git a/src/bot/handlers/commands/help.py b/src/bot/handlers/commands/help.py index ce56a3b..efaf1c6 100644 --- a/src/bot/handlers/commands/help.py +++ b/src/bot/handlers/commands/help.py @@ -2,6 +2,7 @@ from aiogram.types import Message from bot.handlers import registry from bot.handlers.utils.types import ChatType +from bot.services.database import check_user @registry.register( @@ -9,5 +10,13 @@ from bot.handlers.utils.types import ChatType chat_types=ChatType.PRIVATE, description="Help Command Function Description", ) -async def cmd_help(message: Message) -> None: - await message.answer("Help Command Function Answer Text") +async def cmd_help(message: Message, session) -> None: + if message.from_user is None: + return + + user = await check_user(session, message.from_user.id) + + await message.answer( + "Help Command Function Answer Text. " + f"Your locale is {user.lang}" + ) diff --git a/src/bot/middlewares/__init__.py b/src/bot/middlewares/__init__.py index c9ca463..be444ef 100644 --- a/src/bot/middlewares/__init__.py +++ b/src/bot/middlewares/__init__.py @@ -1,9 +1,16 @@ __all__ = ["connect_middlewares"] -from aiogram import Router +from aiogram import Dispatcher +from sqlalchemy.ext.asyncio import async_sessionmaker + +from .database import DatabaseSessionMiddleware -def connect_middlewares(dispatcher: Router): - # TODO: Add database and localization middleware and connect them. - ... +def connect_middlewares( + dispatcher: Dispatcher, + session_maker: async_sessionmaker, +) -> None: + database_middleware = DatabaseSessionMiddleware(session_maker) + + dispatcher.update.middleware(database_middleware) diff --git a/src/bot/middlewares/database.py b/src/bot/middlewares/database.py new file mode 100644 index 0000000..a814e92 --- /dev/null +++ b/src/bot/middlewares/database.py @@ -0,0 +1,23 @@ +from typing import Any, Awaitable, Callable, Dict + +from aiogram import BaseMiddleware +from aiogram.types import TelegramObject +from sqlalchemy.ext.asyncio import async_sessionmaker + + +class DatabaseSessionMiddleware(BaseMiddleware): + def __init__(self, session_maker: async_sessionmaker): + super().__init__() + self.session_maker = session_maker + + async def __call__( + self, + handler: Callable[ + [TelegramObject, Dict[str, Any]], Awaitable[Any] + ], + event: TelegramObject, + data: Dict[str, Any], + ) -> Any: + async with self.session_maker() as session: + data["session"] = session + return await handler(event, data) diff --git a/src/bot/services/database/__init__.py b/src/bot/services/database/__init__.py index a9a2c5b..b6e7f59 100644 --- a/src/bot/services/database/__init__.py +++ b/src/bot/services/database/__init__.py @@ -1 +1,55 @@ -__all__ = [] +__all__ = ["check_user"] + + +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from database.models import TelegramUser + + +async def _get_user( + session: AsyncSession, + telegram_id: int, +) -> TelegramUser | None: + """ + Получить пользователя из БД. + Возвращает либо объект пользователя, либо None. + """ + query = select(TelegramUser).where(TelegramUser.id == telegram_id) + result = await session.execute(query) + + return result.scalar() + + +async def _add_user( + session: AsyncSession, + telegram_id: int, +) -> TelegramUser: + """ + Добавить пользователя в БД. + Возвращает только что добавленный объект пользователя. + """ + user = TelegramUser(id=telegram_id) + + session.add(user) + + await session.commit() + + return user + + +async def check_user( + session: AsyncSession, + telegram_id: int, +) -> TelegramUser: + """ + Проверяет, есть ли пользователь в БД. + Если его нет, то добавляет. + Всегда возвращает объект пользователя. + """ + result = await _get_user(session, telegram_id) + + if result is not None: + return result + + return await _add_user(session, telegram_id) diff --git a/src/config/__init__.py b/src/config/__init__.py index a2811c6..8e8d2da 100644 --- a/src/config/__init__.py +++ b/src/config/__init__.py @@ -1,6 +1,7 @@ __all__ = ["settings", "configure_logger"] -from .utils import Settings, configure_logger +from .logger import configure_logger +from .settings import Settings settings = Settings() diff --git a/src/config/logger.py b/src/config/logger.py new file mode 100644 index 0000000..56dc1e7 --- /dev/null +++ b/src/config/logger.py @@ -0,0 +1,94 @@ +import logging +import sys +from pathlib import Path + +from loguru import logger as loguru_logger + +# Constant paths for project. +LOG_DIR = Path("logs") +LOG_DIR.mkdir(parents=True, exist_ok=True) + +# Log format for loguru logger. +LOG_FORMAT = ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "{level:<8} | " + "{process}.{thread.name} | " + "{module}.{function}:{line} – " + "{message}" +) + + +class InterceptHandler(logging.Handler): + """ + Class for intercepting logs from logging, including Aiogram. + """ + + def emit(self, record: logging.LogRecord) -> None: + """ + Try to intercepting logs from logging. + + Args: + record (logging.LogRecord): record from logging. + """ + try: + level = loguru_logger.level(record.levelname).name + except ValueError: + level = record.levelno + + frame, depth = logging.currentframe(), 2 + + while frame and frame.f_code.co_filename == logging.__file__: + frame = frame.f_back + depth += 1 + + if frame is None: + break + + loguru_logger.opt( + depth=depth, + exception=record.exc_info, + ).log( + level, + record.getMessage(), + ) + + +def configure_logger( + file_log_level: str, + listen_logging: bool, + console_log_level: str, +) -> None: + """ + Configure loguru logger. + + Args: + file_log_level (str): logging level for the .log file. + listen_logging (bool): intercepting logs from logging. + console_log_level (str): logging level for the console. + """ + if listen_logging: + logging.root.handlers.clear() + logging.root.setLevel(logging.DEBUG) + logging.root.addHandler(InterceptHandler()) + + loguru_logger.remove() + + loguru_logger.add( + sys.stdout, + level=console_log_level, + colorize=True, + enqueue=True, + format=LOG_FORMAT, + ) + + loguru_logger.add( + LOG_DIR / "{time:YYYY-MM-DD}.log", + level=file_log_level, + colorize=False, + enqueue=True, + format=LOG_FORMAT, + rotation="10 MB", + retention="14 days", + compression="zip", + serialize=False, + ) diff --git a/src/config/utils.py b/src/config/settings.py similarity index 52% rename from src/config/utils.py rename to src/config/settings.py index 19813a2..6ebcf88 100644 --- a/src/config/utils.py +++ b/src/config/settings.py @@ -1,25 +1,9 @@ -import logging -import sys from pathlib import Path from typing import Literal -from loguru import logger as loguru_logger from pydantic import Field from pydantic_settings import BaseSettings, SettingsConfigDict -# Constant paths for project. -LOG_DIR = Path("logs") -LOG_DIR.mkdir(parents=True, exist_ok=True) - -# Log format for loguru logger. -LOG_FORMAT = ( - "{time:YYYY-MM-DD HH:mm:ss} | " - "{level:<8} | " - "{process}.{thread.name} | " - "{module}.{function}:{line} – " - "{message}" -) - class Settings(BaseSettings): """ @@ -40,6 +24,12 @@ class Settings(BaseSettings): PostgreSQL user password. postgres_name (str): PostgreSQL database name. + postgres_url (str):database_url + Full PostgreSQL database url. + + database_engine_echo (bool): + Enable SQLAlchemy engine echo. + Defaults to True. redis_host (str): Redis host name. @@ -69,8 +59,10 @@ class Settings(BaseSettings): postgres_pass: str = Field(frozen=True) postgres_name: str = Field(frozen=True) + database_engine_echo: bool = Field(frozen=True, default=True) + @property - def database_url(self): + def postgres_url(self): """Get PostgreSQL database url.""" return ( f"postgresql+asyncpg://{self.postgres_user}:" @@ -113,79 +105,3 @@ class Settings(BaseSettings): env_file_encoding="utf-8", case_sensitive=False, ) - - -class InterceptHandler(logging.Handler): - """ - Class for intercepting logs from logging, including Aiogram. - """ - - def emit(self, record: logging.LogRecord) -> None: - """ - Try to intercepting logs from logging. - - Args: - record (logging.LogRecord): record from logging. - """ - try: - level = loguru_logger.level(record.levelname).name - except ValueError: - level = record.levelno - - frame, depth = logging.currentframe(), 2 - - while frame and frame.f_code.co_filename == logging.__file__: - frame = frame.f_back - depth += 1 - - if frame is None: - break - - loguru_logger.opt( - depth=depth, - exception=record.exc_info, - ).log( - level, - record.getMessage(), - ) - - -def configure_logger( - file_log_level: str, - listen_logging: bool, - console_log_level: str, -) -> None: - """ - Configure loguru logger. - - Args: - file_log_level (str): logging level for the .log file. - listen_logging (bool): intercepting logs from logging. - console_log_level (str): logging level for the console. - """ - if listen_logging: - logging.root.handlers.clear() - logging.root.setLevel(logging.DEBUG) - logging.root.addHandler(InterceptHandler()) - - loguru_logger.remove() - - loguru_logger.add( - sys.stdout, - level=console_log_level, - colorize=True, - enqueue=True, - format=LOG_FORMAT, - ) - - loguru_logger.add( - LOG_DIR / "{time:YYYY-MM-DD}.log", - level=file_log_level, - colorize=False, - enqueue=True, - format=LOG_FORMAT, - rotation="10 MB", - retention="14 days", - compression="zip", - serialize=False, - ) diff --git a/src/database/__init__.py b/src/database/__init__.py index 0d430c9..9dd76a1 100644 --- a/src/database/__init__.py +++ b/src/database/__init__.py @@ -1,4 +1,18 @@ -__all__ = ["get_session", "create_db", "drop_db"] +__all__ = [ + "Student", + "TelegramUser", + "UniversityMember", + "create_db", + "drop_db", + "engine", + "session_maker", +] -from .session import create_db, drop_db, get_session +from .models import Student, TelegramUser, UniversityMember +from .session import ( + create_db, + drop_db, + engine, + session_maker, +) diff --git a/src/database/models.py b/src/database/models.py index cad8f11..b76f0bf 100644 --- a/src/database/models.py +++ b/src/database/models.py @@ -84,11 +84,6 @@ class TelegramUser(Base): autoincrement=False, ) - username: Mapped[str] = mapped_column( - String(64), - nullable=True, - ) - lang: Mapped[str] = mapped_column( String(2), default="ru", diff --git a/src/database/session.py b/src/database/session.py index 6cd6c06..a2bc826 100644 --- a/src/database/session.py +++ b/src/database/session.py @@ -6,35 +6,27 @@ from sqlalchemy.ext.asyncio import ( create_async_engine, ) +from config import settings + from .models import Base -engine: AsyncEngine +engine = create_async_engine( + url=settings.postgres_url, + echo=settings.database_engine_echo, +) + +session_maker = async_sessionmaker( + bind=engine, + class_=AsyncSession, + expire_on_commit=False, +) -def get_session( - url: str, engine_echo: bool = True -) -> async_sessionmaker[AsyncSession]: - global engine - engine = create_async_engine(url=url, echo=engine_echo) - - session = async_sessionmaker( - bind=engine, - class_=AsyncSession, - expire_on_commit=False, - ) - - return session - - -async def create_db(): - global engine - +async def create_db(engine: AsyncEngine): async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) -async def drop_db(): - global engine - +async def drop_db(engine: AsyncEngine): async with engine.begin() as conn: await conn.run_sync(Base.metadata.drop_all) diff --git a/src/main.py b/src/main.py index a786e00..c2f84fc 100644 --- a/src/main.py +++ b/src/main.py @@ -2,11 +2,11 @@ import asyncio from bot import start_bot from config import configure_logger, settings -from database import create_db, get_session -from redis_client import get_redis_client +from database import create_db, engine, session_maker +from redis_client import client as redis_client -def main() -> None: +async def main() -> None: """ Launch of all service components. Configure logger and start bot. @@ -17,26 +17,15 @@ def main() -> None: listen_logging=settings.listen_logging, ) - database_session = get_session( - url=settings.database_url, engine_echo=True - ) - # FIXME: Add argument for create/drop db. - asyncio.run(create_db()) + await create_db(engine) - redis_client = get_redis_client( - host=settings.redis_host, - port=settings.redis_port, - ) - - asyncio.run( - start_bot( - bot_token=settings.bot_token, - redis_client=redis_client, - database_session=database_session, - ) + await start_bot( + bot_token=settings.bot_token, + redis_client=redis_client, + session_maker=session_maker, ) if __name__ == "__main__": - main() + asyncio.run(main()) diff --git a/src/redis_client/__init__.py b/src/redis_client/__init__.py index f2425d4..34481f8 100644 --- a/src/redis_client/__init__.py +++ b/src/redis_client/__init__.py @@ -1,23 +1,11 @@ -__all__ = ["get_redis_client"] +__all__ = ["client"] from redis.asyncio.client import Redis +from config import settings -def get_redis_client(host: str, port: int) -> Redis: - """ - Get async redis client. - - Args: - host (str): redis host. - port (int): redis port. - - Returns: - Redis: async redis client. - """ - client = Redis( - host=host, - port=port, - ) - - return client +client = Redis( + host=settings.redis_host, + port=settings.redis_port, +) -- 2.47.3 From 05486d6f4b59a86ef279708a7e5cca157373cfb2 Mon Sep 17 00:00:00 2001 From: geekiot Date: Sat, 1 Nov 2025 14:48:35 +0500 Subject: [PATCH 3/6] Add [redis]: move redis to database package --- src/bot/__init__.py | 4 ++-- src/database/__init__.py | 4 +++- src/database/{session.py => postgres.py} | 0 src/{redis_client/__init__.py => database/redis.py} | 5 +---- src/main.py | 5 ++--- 5 files changed, 8 insertions(+), 10 deletions(-) rename src/database/{session.py => postgres.py} (100%) rename src/{redis_client/__init__.py => database/redis.py} (77%) diff --git a/src/bot/__init__.py b/src/bot/__init__.py index f9328bb..b6edf0c 100644 --- a/src/bot/__init__.py +++ b/src/bot/__init__.py @@ -14,7 +14,7 @@ from .middlewares import connect_middlewares @loguru_logger.catch async def start_bot( bot_token: str, - redis_client: Redis, + redis_protocol: Redis, session_maker: async_sessionmaker, ) -> None: """ @@ -28,7 +28,7 @@ async def start_bot( bot = Bot(bot_token) dispatcher = Dispatcher( - storage=RedisStorage(redis=redis_client), + storage=RedisStorage(redis=redis_protocol), ) connect_middlewares( diff --git a/src/database/__init__.py b/src/database/__init__.py index 9dd76a1..b6da8ee 100644 --- a/src/database/__init__.py +++ b/src/database/__init__.py @@ -6,13 +6,15 @@ __all__ = [ "drop_db", "engine", "session_maker", + "redis_protocol", ] from .models import Student, TelegramUser, UniversityMember -from .session import ( +from .postgres import ( create_db, drop_db, engine, session_maker, ) +from .redis import protocol as redis_protocol diff --git a/src/database/session.py b/src/database/postgres.py similarity index 100% rename from src/database/session.py rename to src/database/postgres.py diff --git a/src/redis_client/__init__.py b/src/database/redis.py similarity index 77% rename from src/redis_client/__init__.py rename to src/database/redis.py index 34481f8..2bcebff 100644 --- a/src/redis_client/__init__.py +++ b/src/database/redis.py @@ -1,11 +1,8 @@ -__all__ = ["client"] - - from redis.asyncio.client import Redis from config import settings -client = Redis( +protocol = Redis( host=settings.redis_host, port=settings.redis_port, ) diff --git a/src/main.py b/src/main.py index c2f84fc..4e1251a 100644 --- a/src/main.py +++ b/src/main.py @@ -2,8 +2,7 @@ import asyncio from bot import start_bot from config import configure_logger, settings -from database import create_db, engine, session_maker -from redis_client import client as redis_client +from database import create_db, engine, redis_protocol, session_maker async def main() -> None: @@ -22,7 +21,7 @@ async def main() -> None: await start_bot( bot_token=settings.bot_token, - redis_client=redis_client, + redis_protocol=redis_protocol, session_maker=session_maker, ) -- 2.47.3 From 3ae9b3cd72d2d5c5d40becd75aa5ec04ed1d1128 Mon Sep 17 00:00:00 2001 From: geekiot Date: Sat, 1 Nov 2025 15:01:36 +0500 Subject: [PATCH 4/6] Update [db]: finally update bot struct --- CONTRIBUTE.md | 11 +++++------ src/bot/handlers/commands/help.py | 4 ++-- src/bot/middlewares/database.py | 1 - src/bot/services/__init__.py | 5 ++--- .../{database/__init__.py => database.py} | 15 ++++++--------- src/bot/{handlers => }/utils/filters/__init__.py | 2 +- .../{handlers => }/utils/keyboards/__init__.py | 0 src/bot/{handlers => }/utils/registry/__init__.py | 4 ++-- src/bot/{handlers => }/utils/states/__init__.py | 0 src/bot/{handlers => }/utils/types/__init__.py | 0 src/bot/{handlers => }/utils/types/chat.py | 0 src/bot/{handlers => }/utils/types/handler.py | 2 +- 12 files changed, 19 insertions(+), 25 deletions(-) rename src/bot/services/{database/__init__.py => database.py} (82%) rename src/bot/{handlers => }/utils/filters/__init__.py (97%) rename src/bot/{handlers => }/utils/keyboards/__init__.py (100%) rename src/bot/{handlers => }/utils/registry/__init__.py (98%) rename src/bot/{handlers => }/utils/states/__init__.py (100%) rename src/bot/{handlers => }/utils/types/__init__.py (100%) rename src/bot/{handlers => }/utils/types/chat.py (100%) rename src/bot/{handlers => }/utils/types/handler.py (94%) diff --git a/CONTRIBUTE.md b/CONTRIBUTE.md index ccf3cc0..886813d 100644 --- a/CONTRIBUTE.md +++ b/CONTRIBUTE.md @@ -70,14 +70,13 @@ urfu-daddy/ │ │ ├── __init__.py # Функция для запуска бота. │ │ ├── handlers/ # Обработка всех ивентов. │ │ │ ├── callbacks/ # Обработка запросов. -│ │ │ ├── commands/ # Обработка комманд. -│ │ │ ├── utils/ # Вспомогательные компоненты для хендлеров. -│ │ ├── middlewares/ # Мидлвари для диспетчера. -│ │ └── services/ # Взаимодействие с другими сервисами бота. +│ │ │ └── commands/ # Обработка комманд. +│ │ ├── middlewares/ # Мидлвари для диспетчера/роутеров. +│ │ ├── services/ # Взаимодействие с другими сервисами бота. +│ │ └── utils/ # Вспомогательные компоненты для для бота. │ ├── config/ # Получение env-настроек, настройка логирования. │ ├── database/ # Инициализация и настройка БД. -│ ├── locales/ # Папка с локализацией проекта. -│ └── redis_client/ # Настройка redis-клиента. +│ └── locales/ # Папка с локализацией проекта. ├── LICENSE # Лицензия проекта. ├── README.md # Описание проекта. ├── CONTRIBUTE.md # Этот файл. diff --git a/src/bot/handlers/commands/help.py b/src/bot/handlers/commands/help.py index efaf1c6..38edafc 100644 --- a/src/bot/handlers/commands/help.py +++ b/src/bot/handlers/commands/help.py @@ -2,7 +2,7 @@ from aiogram.types import Message from bot.handlers import registry from bot.handlers.utils.types import ChatType -from bot.services.database import check_user +from bot.services.database import check_telegram_user @registry.register( @@ -14,7 +14,7 @@ async def cmd_help(message: Message, session) -> None: if message.from_user is None: return - user = await check_user(session, message.from_user.id) + user = await check_telegram_user(session, message.from_user.id) await message.answer( "Help Command Function Answer Text. " diff --git a/src/bot/middlewares/database.py b/src/bot/middlewares/database.py index a814e92..8b72c58 100644 --- a/src/bot/middlewares/database.py +++ b/src/bot/middlewares/database.py @@ -7,7 +7,6 @@ from sqlalchemy.ext.asyncio import async_sessionmaker class DatabaseSessionMiddleware(BaseMiddleware): def __init__(self, session_maker: async_sessionmaker): - super().__init__() self.session_maker = session_maker async def __call__( diff --git a/src/bot/services/__init__.py b/src/bot/services/__init__.py index 7dfd4a8..f52f96a 100644 --- a/src/bot/services/__init__.py +++ b/src/bot/services/__init__.py @@ -1,5 +1,4 @@ -__all__ = [] +__all__ = ["check_telegram_user"] -# TODO: Add bot business logic -# (working with other service components). +from .database import check_telegram_user diff --git a/src/bot/services/database/__init__.py b/src/bot/services/database.py similarity index 82% rename from src/bot/services/database/__init__.py rename to src/bot/services/database.py index b6e7f59..6472bae 100644 --- a/src/bot/services/database/__init__.py +++ b/src/bot/services/database.py @@ -1,13 +1,10 @@ -__all__ = ["check_user"] - - from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession -from database.models import TelegramUser +from database import TelegramUser -async def _get_user( +async def _get_telegram_user( session: AsyncSession, telegram_id: int, ) -> TelegramUser | None: @@ -21,7 +18,7 @@ async def _get_user( return result.scalar() -async def _add_user( +async def _add_telegram_user( session: AsyncSession, telegram_id: int, ) -> TelegramUser: @@ -38,7 +35,7 @@ async def _add_user( return user -async def check_user( +async def check_telegram_user( session: AsyncSession, telegram_id: int, ) -> TelegramUser: @@ -47,9 +44,9 @@ async def check_user( Если его нет, то добавляет. Всегда возвращает объект пользователя. """ - result = await _get_user(session, telegram_id) + result = await _get_telegram_user(session, telegram_id) if result is not None: return result - return await _add_user(session, telegram_id) + return await _add_telegram_user(session, telegram_id) diff --git a/src/bot/handlers/utils/filters/__init__.py b/src/bot/utils/filters/__init__.py similarity index 97% rename from src/bot/handlers/utils/filters/__init__.py rename to src/bot/utils/filters/__init__.py index e59a335..c4d3852 100644 --- a/src/bot/handlers/utils/filters/__init__.py +++ b/src/bot/utils/filters/__init__.py @@ -6,7 +6,7 @@ from typing import Union from aiogram.filters import BaseFilter from aiogram.types import CallbackQuery, Message -from bot.handlers.utils.types import ChatType +from bot.utils.types import ChatType class ChatTypeFilter(BaseFilter): diff --git a/src/bot/handlers/utils/keyboards/__init__.py b/src/bot/utils/keyboards/__init__.py similarity index 100% rename from src/bot/handlers/utils/keyboards/__init__.py rename to src/bot/utils/keyboards/__init__.py diff --git a/src/bot/handlers/utils/registry/__init__.py b/src/bot/utils/registry/__init__.py similarity index 98% rename from src/bot/handlers/utils/registry/__init__.py rename to src/bot/utils/registry/__init__.py index 4854455..ba6307d 100644 --- a/src/bot/handlers/utils/registry/__init__.py +++ b/src/bot/utils/registry/__init__.py @@ -13,8 +13,8 @@ from aiogram.types.bot_command_scope_all_private_chats import ( ) from loguru import logger as loguru_logger -from bot.handlers.utils.filters import ChatTypeFilter -from bot.handlers.utils.types import ( +from bot.utils.filters import ChatTypeFilter +from bot.utils.types import ( ChatType, HandlerMeta, HandlerType, diff --git a/src/bot/handlers/utils/states/__init__.py b/src/bot/utils/states/__init__.py similarity index 100% rename from src/bot/handlers/utils/states/__init__.py rename to src/bot/utils/states/__init__.py diff --git a/src/bot/handlers/utils/types/__init__.py b/src/bot/utils/types/__init__.py similarity index 100% rename from src/bot/handlers/utils/types/__init__.py rename to src/bot/utils/types/__init__.py diff --git a/src/bot/handlers/utils/types/chat.py b/src/bot/utils/types/chat.py similarity index 100% rename from src/bot/handlers/utils/types/chat.py rename to src/bot/utils/types/chat.py diff --git a/src/bot/handlers/utils/types/handler.py b/src/bot/utils/types/handler.py similarity index 94% rename from src/bot/handlers/utils/types/handler.py rename to src/bot/utils/types/handler.py index 34a2c1c..d589bb9 100644 --- a/src/bot/handlers/utils/types/handler.py +++ b/src/bot/utils/types/handler.py @@ -1,7 +1,7 @@ from dataclasses import dataclass from typing import Any, Awaitable, Callable, TypeVar -from bot.handlers.utils.types import ChatType +from bot.utils.types import ChatType HandlerType = TypeVar( "HandlerType", bound=Callable[[Any], Awaitable[Any]] -- 2.47.3 From 99e0966a0cf5856f6566bfe0ba14b7c6474dbf2f Mon Sep 17 00:00:00 2001 From: geekiot Date: Sat, 1 Nov 2025 15:04:24 +0500 Subject: [PATCH 5/6] Hotfix: change module path --- src/bot/handlers/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bot/handlers/__init__.py b/src/bot/handlers/__init__.py index da34c81..10b5cfb 100644 --- a/src/bot/handlers/__init__.py +++ b/src/bot/handlers/__init__.py @@ -6,7 +6,7 @@ from pathlib import Path from aiogram import Router from loguru import logger as loguru_logger -from .utils.registry import RouterRegistry +from bot.utils.registry import RouterRegistry # Main registry router for bot handlers. registry: RouterRegistry = RouterRegistry() -- 2.47.3 From 37b8eb29a457ee9cf043d88d1038dc9a90fae1f0 Mon Sep 17 00:00:00 2001 From: geekiot Date: Sat, 1 Nov 2025 15:10:05 +0500 Subject: [PATCH 6/6] Hoxfix: fix all bugs with imports --- src/bot/handlers/callbacks/menu.py | 4 ++-- src/bot/handlers/commands/help.py | 2 +- src/bot/handlers/commands/menu.py | 4 ++-- src/bot/utils/__init__.py | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 src/bot/utils/__init__.py diff --git a/src/bot/handlers/callbacks/menu.py b/src/bot/handlers/callbacks/menu.py index ac8ca01..a6a42d9 100644 --- a/src/bot/handlers/callbacks/menu.py +++ b/src/bot/handlers/callbacks/menu.py @@ -1,8 +1,8 @@ from aiogram.types import CallbackQuery, Message from bot.handlers import registry -from bot.handlers.utils.keyboards import get_menu_markup -from bot.handlers.utils.types import ChatType +from bot.utils.keyboards import get_menu_markup +from bot.utils.types import ChatType @registry.register( diff --git a/src/bot/handlers/commands/help.py b/src/bot/handlers/commands/help.py index 38edafc..3dadc15 100644 --- a/src/bot/handlers/commands/help.py +++ b/src/bot/handlers/commands/help.py @@ -1,8 +1,8 @@ from aiogram.types import Message from bot.handlers import registry -from bot.handlers.utils.types import ChatType from bot.services.database import check_telegram_user +from bot.utils.types import ChatType @registry.register( diff --git a/src/bot/handlers/commands/menu.py b/src/bot/handlers/commands/menu.py index 3df72e9..873c6a3 100644 --- a/src/bot/handlers/commands/menu.py +++ b/src/bot/handlers/commands/menu.py @@ -1,8 +1,8 @@ from aiogram.types import Message from bot.handlers import registry -from bot.handlers.utils.keyboards import get_menu_markup -from bot.handlers.utils.types import ChatType +from bot.utils.keyboards import get_menu_markup +from bot.utils.types import ChatType @registry.register( diff --git a/src/bot/utils/__init__.py b/src/bot/utils/__init__.py new file mode 100644 index 0000000..a9a2c5b --- /dev/null +++ b/src/bot/utils/__init__.py @@ -0,0 +1 @@ +__all__ = [] -- 2.47.3