minor fixes and updates
All checks were successful
Build Docs / changes (push) Successful in 5s
Build Docs / build-docs (push) Has been skipped
Build Docs / deploy-docs (push) Has been skipped

This commit is contained in:
Alexander Kalinovsky
2025-03-19 14:46:23 +07:00
parent f0db2b2830
commit fe0380f9f3
6 changed files with 53 additions and 50 deletions

View File

@@ -31,7 +31,11 @@ async def telegram_webhook(
return Response(status_code=400) return Response(status_code=400)
try: try:
await app.dp.feed_webhook_update( 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: except Exception:
logger.error("Error processing update", exc_info=True) logger.error("Error processing update", exc_info=True)

View File

@@ -24,24 +24,11 @@ class Config(BaseSettings):
def DATABASE_URI(self) -> str: def DATABASE_URI(self) -> str:
return f"postgresql+asyncpg://{self.DB_USER}:{self.DB_PASSWORD}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}" 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 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" TELEGRAM_BOT_TOKEN: str = "changethis"
ADMIN_TELEGRAM_ID: int ADMIN_TELEGRAM_ID: int

View File

@@ -1,6 +1,8 @@
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from typing import Callable, Any from typing import Callable, Any
from aiogram import Bot, Dispatcher 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.client.default import DefaultBotProperties
from aiogram.types import Message, BotCommand as AiogramBotCommand from aiogram.types import Message, BotCommand as AiogramBotCommand
from aiogram.utils.callback_answer import CallbackAnswerMiddleware from aiogram.utils.callback_answer import CallbackAnswerMiddleware
@@ -35,8 +37,8 @@ async def default_lifespan(app: "QBotApp"):
logger.info("qbot app started") logger.info("qbot app started")
if app.lifespan: if app.lifespan:
async with app.lifespan(app): async with app.lifespan(app) as state:
yield yield state
else: else:
yield yield
@@ -88,8 +90,14 @@ class QBotApp[UserType: UserBase](FastAPI):
self.entity_metadata: EntityMetadata = user_class.entity_metadata self.entity_metadata: EntityMetadata = user_class.entity_metadata
self.config = config self.config = config
self.lifespan = lifespan 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( self.bot = Bot(
token=self.config.TELEGRAM_BOT_TOKEN, token=self.config.TELEGRAM_BOT_TOKEN,
session=session,
default=DefaultBotProperties(parse_mode="HTML"), default=DefaultBotProperties(parse_mode="HTML"),
) )
@@ -124,7 +132,7 @@ class QBotApp[UserType: UserBase](FastAPI):
from .api_route.telegram import router as telegram_router 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 = Router()
self.root_router._commands = self.bot_commands self.root_router._commands = self.bot_commands
self.command = self.root_router.command self.command = self.root_router.command
@@ -177,6 +185,13 @@ class QBotApp[UserType: UserBase](FastAPI):
commands_captions[locale] = [] commands_captions[locale] = []
commands_captions[locale].append((command_name, description)) 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(): for locale, commands in commands_captions.items():
await self.bot.set_my_commands( await self.bot.set_my_commands(
[ [
@@ -186,12 +201,7 @@ class QBotApp[UserType: UserBase](FastAPI):
language_code=None if locale == "default" else locale, 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): async def bot_close(self):
await self.bot.delete_webhook() await self.bot.delete_webhook()
await self.bot.log_out()
await self.bot.close()

View File

@@ -67,14 +67,31 @@ class BotEntityMetaclass(SQLModelMetaclass):
descriptor_kwargs = attribute_value.__dict__.copy() descriptor_kwargs = attribute_value.__dict__.copy()
sm_descriptor = descriptor_kwargs.pop("sm_descriptor", None) # type: FieldInfo sm_descriptor = descriptor_kwargs.pop("sm_descriptor", None) # type: FieldInfo
if attribute_value.default is not None: if sm_descriptor:
if ( if (
sm_descriptor attribute_value.default is not None
and sm_descriptor.default is PydanticUndefined and sm_descriptor.default is PydanticUndefined
): ):
sm_descriptor.default = attribute_value.default sm_descriptor.default = attribute_value.default
else: if (
sm_descriptor = Field(default=attribute_value.default) 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: if sm_descriptor:
namespace[annotation] = sm_descriptor namespace[annotation] = sm_descriptor
@@ -167,23 +184,6 @@ class BotEntityMetaclass(SQLModelMetaclass):
fields_descriptors=bot_fields_descriptors, 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(): for field_descriptor in bot_fields_descriptors.values():
field_descriptor.entity_descriptor = namespace["bot_entity_descriptor"] field_descriptor.entity_descriptor = namespace["bot_entity_descriptor"]

View File

@@ -102,6 +102,7 @@ class _BaseFieldDescriptor:
ep_child_field: str | None = None ep_child_field: str | None = None
dt_type: Literal["date", "datetime"] = "date" dt_type: Literal["date", "datetime"] = "date"
default: Any = None default: Any = None
default_factory: Callable[[], Any] | None = None
@dataclass(kw_only=True) @dataclass(kw_only=True)

View File

@@ -227,7 +227,9 @@ class Settings(metaclass=SettingsMetaclass):
) )
return ( return (
param.default param.default_factory()
if param.default_factory
else param.default
if param.default if param.default
else ( else (
[] []
@@ -249,7 +251,6 @@ class Settings(metaclass=SettingsMetaclass):
session=session, session=session,
type_=setting.type_, type_=setting.type_,
value=db_setting.value, value=db_setting.value,
default=setting.default,
) )
cls._loaded = True cls._loaded = True