diff --git a/src/qbot/api_route/telegram.py b/src/qbot/api_route/telegram.py index 431f755..79818aa 100644 --- a/src/qbot/api_route/telegram.py +++ b/src/qbot/api_route/telegram.py @@ -31,7 +31,11 @@ async def telegram_webhook( return Response(status_code=400) try: await app.dp.feed_webhook_update( - app.bot, update, db_session=db_session, app=app + app.bot, + update, + db_session=db_session, + app=app, + **(request.state if request.state else {}), ) except Exception: logger.error("Error processing update", exc_info=True) diff --git a/src/qbot/config/__init__.py b/src/qbot/config/__init__.py index a541f64..c4263af 100644 --- a/src/qbot/config/__init__.py +++ b/src/qbot/config/__init__.py @@ -24,24 +24,11 @@ class Config(BaseSettings): def DATABASE_URI(self) -> str: return f"postgresql+asyncpg://{self.DB_USER}:{self.DB_PASSWORD}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}" - DOMAIN: str = "localhost" - - @computed_field - @property - def API_DOMAIN(self) -> str: - if self.ENVIRONMENT == "local": - return self.DOMAIN - return f"{self.DOMAIN}" - - @computed_field - @property - def API_URL(self) -> str: - if self.USE_NGROK: - return self.NGROK_URL - return f"https://{self.API_DOMAIN}" - API_PORT: int = 8000 + TELEGRAM_WEBHOOK_URL: str = "http://localhost:8000" + TELEGRAM_BOT_SERVER: str = "https://api.telegram.org" + TELEGRAM_BOT_SERVER_IS_LOCAL: bool = False TELEGRAM_BOT_TOKEN: str = "changethis" ADMIN_TELEGRAM_ID: int diff --git a/src/qbot/main.py b/src/qbot/main.py index e7bc6c1..1244c80 100644 --- a/src/qbot/main.py +++ b/src/qbot/main.py @@ -1,6 +1,8 @@ from contextlib import asynccontextmanager from typing import Callable, Any from aiogram import Bot, Dispatcher +from aiogram.client.session.aiohttp import AiohttpSession +from aiogram.client.telegram import TelegramAPIServer from aiogram.client.default import DefaultBotProperties from aiogram.types import Message, BotCommand as AiogramBotCommand from aiogram.utils.callback_answer import CallbackAnswerMiddleware @@ -35,8 +37,8 @@ async def default_lifespan(app: "QBotApp"): logger.info("qbot app started") if app.lifespan: - async with app.lifespan(app): - yield + async with app.lifespan(app) as state: + yield state else: yield @@ -88,8 +90,14 @@ class QBotApp[UserType: UserBase](FastAPI): self.entity_metadata: EntityMetadata = user_class.entity_metadata self.config = config self.lifespan = lifespan + api_server = TelegramAPIServer.from_base( + self.config.TELEGRAM_BOT_SERVER, + is_local=self.config.TELEGRAM_BOT_SERVER_IS_LOCAL, + ) + session = AiohttpSession(api=api_server) self.bot = Bot( token=self.config.TELEGRAM_BOT_TOKEN, + session=session, default=DefaultBotProperties(parse_mode="HTML"), ) @@ -124,7 +132,7 @@ class QBotApp[UserType: UserBase](FastAPI): from .api_route.telegram import router as telegram_router - self.include_router(telegram_router, prefix="/api/telegram", tags=["telegram"]) + self.include_router(telegram_router, prefix="/telegram", tags=["telegram"]) self.root_router = Router() self.root_router._commands = self.bot_commands self.command = self.root_router.command @@ -177,6 +185,13 @@ class QBotApp[UserType: UserBase](FastAPI): commands_captions[locale] = [] commands_captions[locale].append((command_name, description)) + await self.bot.set_webhook( + url=f"{self.config.TELEGRAM_WEBHOOK_URL}/telegram/webhook", + drop_pending_updates=True, + allowed_updates=self.allowed_updates, + secret_token=self.bot_auth_token, + ) + for locale, commands in commands_captions.items(): await self.bot.set_my_commands( [ @@ -186,12 +201,7 @@ class QBotApp[UserType: UserBase](FastAPI): language_code=None if locale == "default" else locale, ) - await self.bot.set_webhook( - url=f"{self.config.API_URL}/api/telegram/webhook", - drop_pending_updates=True, - allowed_updates=self.allowed_updates, - secret_token=self.bot_auth_token, - ) - async def bot_close(self): await self.bot.delete_webhook() + await self.bot.log_out() + await self.bot.close() diff --git a/src/qbot/model/bot_entity.py b/src/qbot/model/bot_entity.py index 56eec38..af63325 100644 --- a/src/qbot/model/bot_entity.py +++ b/src/qbot/model/bot_entity.py @@ -67,14 +67,31 @@ class BotEntityMetaclass(SQLModelMetaclass): descriptor_kwargs = attribute_value.__dict__.copy() sm_descriptor = descriptor_kwargs.pop("sm_descriptor", None) # type: FieldInfo - if attribute_value.default is not None: + if sm_descriptor: if ( - sm_descriptor + attribute_value.default is not None and sm_descriptor.default is PydanticUndefined ): sm_descriptor.default = attribute_value.default - else: - sm_descriptor = Field(default=attribute_value.default) + if ( + attribute_value.default_factory is not None + and sm_descriptor.default_factory is PydanticUndefined + ): + sm_descriptor.default_factory = ( + attribute_value.default_factory + ) + else: + if ( + attribute_value.default is not None + or attribute_value.default_factory is not None + ): + sm_descriptor = Field() + if attribute_value.default is not None: + sm_descriptor.default = attribute_value.default + if attribute_value.default_factory is not None: + sm_descriptor.default_factory = ( + attribute_value.default_factory + ) if sm_descriptor: namespace[annotation] = sm_descriptor @@ -167,23 +184,6 @@ class BotEntityMetaclass(SQLModelMetaclass): fields_descriptors=bot_fields_descriptors, ) - # descriptor_fields_sequence = [ - # key - # for key, val in bot_fields_descriptors.items() - # if not (val.is_optional or val.name == "id" or val.name[:-3] == "_id") - # ] - - # entity_descriptor: EntityDescriptor = namespace["bot_entity_descriptor"] - - # if entity_descriptor.default_form.edit_field_sequence is None: - # entity_descriptor.default_form.edit_field_sequence = ( - # descriptor_fields_sequence - # ) - - # for form in entity_descriptor.forms.values(): - # if form.edit_field_sequence is None: - # form.edit_field_sequence = descriptor_fields_sequence - for field_descriptor in bot_fields_descriptors.values(): field_descriptor.entity_descriptor = namespace["bot_entity_descriptor"] diff --git a/src/qbot/model/descriptors.py b/src/qbot/model/descriptors.py index 8b92284..0a20d44 100644 --- a/src/qbot/model/descriptors.py +++ b/src/qbot/model/descriptors.py @@ -102,6 +102,7 @@ class _BaseFieldDescriptor: ep_child_field: str | None = None dt_type: Literal["date", "datetime"] = "date" default: Any = None + default_factory: Callable[[], Any] | None = None @dataclass(kw_only=True) diff --git a/src/qbot/model/settings.py b/src/qbot/model/settings.py index 4c518cd..ed22011 100644 --- a/src/qbot/model/settings.py +++ b/src/qbot/model/settings.py @@ -227,7 +227,9 @@ class Settings(metaclass=SettingsMetaclass): ) return ( - param.default + param.default_factory() + if param.default_factory + else param.default if param.default else ( [] @@ -249,7 +251,6 @@ class Settings(metaclass=SettingsMetaclass): session=session, type_=setting.type_, value=db_setting.value, - default=setting.default, ) cls._loaded = True