"""Entry point: settings → texts → dispatcher → long polling (ADR-002).""" import asyncio import contextlib import logging from aiogram import Bot, Dispatcher from app import __version__ from app.bot.fsm import make_storage from app.bot.middlewares.access import AccessMiddleware from app.bot.routers.common import build_router from app.config import Settings from app.db.maintenance import cleanup_loop from app.db.migrate import upgrade from app.db.session import SessionMaker, make_engine, make_sessionmaker from app.health import heartbeat from app.logs_setup import setup_logging from app.texts import Texts, load_texts log = logging.getLogger("tgit") def build_dispatcher(settings: Settings, texts: Texts, sessions: SessionMaker) -> Dispatcher: admin_ids = settings.access.admin_ids async def is_member(tg_id: int) -> bool: return tg_id in admin_ids dp = Dispatcher(storage=make_storage(sessions)) access = AccessMiddleware(is_member, texts) # Outer middleware: runs before filters, so nothing — not even a stale button — reaches a handler unchecked. dp.message.outer_middleware(access) dp.callback_query.outer_middleware(access) dp.include_router(build_router(texts, admin_ids)) return dp async def run(settings: Settings) -> None: texts = load_texts(settings.runtime.config_dir) engine = make_engine(settings.database.url) await upgrade(engine) sessions = make_sessionmaker(engine) dp = build_dispatcher(settings, texts, sessions) rt = settings.runtime background = [ asyncio.create_task(heartbeat(rt.heartbeat_file, rt.heartbeat_interval)), asyncio.create_task(cleanup_loop(sessions)), ] bot = Bot(settings.telegram.bot_token.get_secret_value()) log.info("tgit %s starting, %d bootstrap admin(s)", __version__, len(settings.access.admin_ids)) try: await dp.start_polling(bot, handle_signals=True) finally: for task in background: task.cancel() for task in background: with contextlib.suppress(asyncio.CancelledError): await task await bot.session.close() await engine.dispose() log.info("tgit stopped") def main() -> None: settings = Settings() setup_logging(settings.runtime.log_level) asyncio.run(run(settings))