diff --git a/README.md b/README.md index aad27d1..cdba169 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ class AppEntity(BotEntity): ) -app = QBotApp() # bot application based on FastAPI application +app = QuickBot() # bot application based on FastAPI application # providing Telegram API webhook handler @app.command( # decorator for bot commands definition diff --git a/docs/index.md b/docs/index.md index c8c0345..fac0752 100644 --- a/docs/index.md +++ b/docs/index.md @@ -73,7 +73,7 @@ class AppEntity(BotEntity): ) -app = QBotApp() # bot application based on FastAPI application +app = QuickBot() # bot application based on FastAPI application # providing Telegram API webhook handler @app.command( # decorator for bot commands definition diff --git a/pyproject.toml b/pyproject.toml index 6070da1..1d821e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "quickbot" version = "0.1.0" -description = "QBot - Rapid Application Development Framework for Telegram Bots" +description = "quickbot - Rapid Application Development Framework for Telegram Bots" readme = "README.md" requires-python = ">=3.12" classifiers = [ diff --git a/src/quickbot/__init__.py b/src/quickbot/__init__.py index aef2fa3..48efe04 100644 --- a/src/quickbot/__init__.py +++ b/src/quickbot/__init__.py @@ -1,4 +1,4 @@ -from .main import QBotApp as QBotApp, Config as Config +from .main import QuickBot as QuickBot, Config as Config from .router import Router as Router from .model.bot_entity import BotEntity as BotEntity from .model.bot_process import BotProcess as BotProcess diff --git a/src/quickbot/api_route/depends.py b/src/quickbot/api_route/depends.py index e61a949..319bf5b 100644 --- a/src/quickbot/api_route/depends.py +++ b/src/quickbot/api_route/depends.py @@ -9,7 +9,7 @@ from quickbot.db import get_db from quickbot.model.user import UserBase if TYPE_CHECKING: - from quickbot import QBotApp + from quickbot import QuickBot security_scheme = HTTPBearer( scheme_name="bearerAuth", @@ -27,7 +27,7 @@ async def get_current_user( user_id = payload.get("sub") if user_id is None: raise HTTPException(status_code=401, detail="Invalid token") - app: QBotApp = request.app + app: QuickBot = request.app user = await app.user_class.get( session=db_session, id=int(user_id), diff --git a/src/quickbot/api_route/models.py b/src/quickbot/api_route/models.py index ebe4016..d705fdb 100644 --- a/src/quickbot/api_route/models.py +++ b/src/quickbot/api_route/models.py @@ -8,7 +8,7 @@ from ..model.descriptors import EntityDescriptor from .depends import get_current_user if TYPE_CHECKING: - from ..main import QBotApp + from ..main import QuickBot from ..model.user import UserBase @@ -40,7 +40,7 @@ async def get_me( request: Request, current_user: Annotated["UserBase", Depends(get_current_user)], ): - app: "QBotApp" = request.app + app: "QuickBot" = request.app user = await app.user_class.bot_entity_descriptor.crud.get_by_id( db_session=db_session, user=current_user, diff --git a/src/quickbot/api_route/telegram.py b/src/quickbot/api_route/telegram.py index 2efd6a2..ff272ca 100644 --- a/src/quickbot/api_route/telegram.py +++ b/src/quickbot/api_route/telegram.py @@ -4,7 +4,7 @@ from sqlmodel.ext.asyncio.session import AsyncSession from typing import Annotated from ..db import get_db -from ..main import QBotApp +from ..main import QuickBot from ..auth.telegram import check_telegram_auth from ..auth.jwt import create_access_token @@ -21,7 +21,7 @@ async def telegram_webhook( request: Request, ): logger.debug("Webhook request %s", await request.json()) - app: QBotApp = request.app + app: QuickBot = request.app if app.webhook_handler: return await app.webhook_handler(app=app, request=request) diff --git a/src/quickbot/bot/handlers/common/filtering_callbacks.py b/src/quickbot/bot/handlers/common/filtering_callbacks.py index 3ebfe42..94f4b6c 100644 --- a/src/quickbot/bot/handlers/common/filtering_callbacks.py +++ b/src/quickbot/bot/handlers/common/filtering_callbacks.py @@ -20,7 +20,7 @@ from ..editors.entity import render_entity_picker from .routing import route_callback if TYPE_CHECKING: - from ....main import QBotApp + from ....main import QuickBot router = Router() @@ -42,7 +42,7 @@ async def view_filter_edit(query: CallbackQuery, **kwargs): cmd = args[1] db_session: AsyncSession = kwargs["db_session"] - app: "QBotApp" = kwargs["app"] + app: "QuickBot" = kwargs["app"] user: UserBase = kwargs["user"] entity_descriptor = get_entity_descriptor(app=app, callback_data=callback_data) @@ -129,7 +129,7 @@ async def view_filter_edit_input(message: Message, **kwargs): callback_data = ContextData.unpack(state_data["context_data"]) db_session: AsyncSession = kwargs["db_session"] user: UserBase = kwargs["user"] - app: "QBotApp" = kwargs["app"] + app: "QuickBot" = kwargs["app"] entity_descriptor = get_entity_descriptor(app=app, callback_data=callback_data) filter = message.text await ViewSetting.set_filter( diff --git a/src/quickbot/bot/handlers/common/routing.py b/src/quickbot/bot/handlers/common/routing.py index 66852ca..0299250 100644 --- a/src/quickbot/bot/handlers/common/routing.py +++ b/src/quickbot/bot/handlers/common/routing.py @@ -3,7 +3,7 @@ from aiogram.fsm.context import FSMContext from typing import TYPE_CHECKING if TYPE_CHECKING: - from quickbot.main import QBotApp + from quickbot.main import QuickBot from ..context import CallbackCommand @@ -51,7 +51,7 @@ async def route_callback(message: Message | CallbackQuery, back: bool = True, ** elif context.command == CallbackCommand.FIELD_EDITOR: await editor.field_editor(message, **kwargs) elif context.command == CallbackCommand.USER_COMMAND: - app: "QBotApp" = kwargs["app"] + app: "QuickBot" = kwargs["app"] cmd = app.bot_commands.get(context.user_command.split("&")[0]) await user_handler.command_handler(message=message, cmd=cmd, **kwargs) diff --git a/src/quickbot/bot/handlers/editors/common.py b/src/quickbot/bot/handlers/editors/common.py index e7f423e..5f638b8 100644 --- a/src/quickbot/bot/handlers/editors/common.py +++ b/src/quickbot/bot/handlers/editors/common.py @@ -4,7 +4,7 @@ from datetime import datetime, time from typing import TYPE_CHECKING if TYPE_CHECKING: - from quickbot.main import QBotApp + from quickbot.main import QuickBot from quickbot.utils.serialization import deserialize from ....model.bot_entity import BotEntity @@ -27,7 +27,7 @@ async def show_editor(message: Message | CallbackQuery, **kwargs): callback_data: ContextData = kwargs.get("callback_data", None) state_data: dict = kwargs["state_data"] db_session = kwargs["db_session"] - app: "QBotApp" = kwargs["app"] + app: "QuickBot" = kwargs["app"] value_type = field_descriptor.type_base diff --git a/src/quickbot/bot/handlers/editors/date.py b/src/quickbot/bot/handlers/editors/date.py index 428f65a..4376562 100644 --- a/src/quickbot/bot/handlers/editors/date.py +++ b/src/quickbot/bot/handlers/editors/date.py @@ -14,7 +14,7 @@ from ....utils.main import get_send_message, get_field_descriptor from .wrapper import wrap_editor if TYPE_CHECKING: - from ....main import QBotApp + from ....main import QuickBot logger = getLogger(__name__) @@ -23,7 +23,7 @@ router = Router() @router.callback_query(ContextData.filter(F.command == CallbackCommand.TIME_PICKER)) async def time_picker_callback( - query: CallbackQuery, callback_data: ContextData, app: "QBotApp", **kwargs + query: CallbackQuery, callback_data: ContextData, app: "QuickBot", **kwargs ): if not callback_data.data: return @@ -316,7 +316,7 @@ async def date_picker_year( user: UserBase, **kwargs, ): - app: "QBotApp" = kwargs["app"] + app: "QuickBot" = kwargs["app"] start_date = datetime.strptime(callback_data.data, "%Y-%m-%d %H-%M") state_data = await state.get_data() @@ -407,7 +407,7 @@ async def date_picker_year( ContextData.filter(F.command == CallbackCommand.DATE_PICKER_MONTH) ) async def date_picker_month(query: CallbackQuery, callback_data: ContextData, **kwargs): - app: "QBotApp" = kwargs["app"] + app: "QuickBot" = kwargs["app"] field_descriptor = get_field_descriptor(app, callback_data) state: FSMContext = kwargs["state"] state_data = await state.get_data() diff --git a/src/quickbot/bot/handlers/editors/entity.py b/src/quickbot/bot/handlers/editors/entity.py index 329af42..12bf482 100644 --- a/src/quickbot/bot/handlers/editors/entity.py +++ b/src/quickbot/bot/handlers/editors/entity.py @@ -31,7 +31,7 @@ from ..common.filtering import add_filter_controls from .wrapper import wrap_editor if TYPE_CHECKING: - from ....main import QBotApp + from ....main import QuickBot logger = getLogger(__name__) router = Router() @@ -362,7 +362,7 @@ async def render_entity_picker( send_message = get_send_message(message) await send_message(text=edit_prompt, reply_markup=keyboard_builder.as_markup()) else: - app: "QBotApp" = kwargs["app"] + app: "QuickBot" = kwargs["app"] await app.bot.send_message( chat_id=user.id, text=edit_prompt, reply_markup=keyboard_builder.as_markup() ) @@ -378,7 +378,7 @@ async def entity_picker_callback( query: CallbackQuery, callback_data: ContextData, db_session: AsyncSession, - app: "QBotApp", + app: "QuickBot", state: FSMContext, **kwargs, ): diff --git a/src/quickbot/bot/handlers/editors/main.py b/src/quickbot/bot/handlers/editors/main.py index 6d72012..ed11113 100644 --- a/src/quickbot/bot/handlers/editors/main.py +++ b/src/quickbot/bot/handlers/editors/main.py @@ -34,7 +34,7 @@ from .boolean import router as bool_editor_router from .entity import router as entity_picker_router if TYPE_CHECKING: - from ....main import QBotApp + from ....main import QuickBot logger = getLogger(__name__) @@ -54,7 +54,7 @@ async def field_editor(message: Message | CallbackQuery, **kwargs): callback_data: ContextData = kwargs.get("callback_data", None) db_session: AsyncSession = kwargs["db_session"] user: UserBase = kwargs["user"] - app: "QBotApp" = kwargs["app"] + app: "QuickBot" = kwargs["app"] state: FSMContext = kwargs["state"] state_data: dict = kwargs["state_data"] diff --git a/src/quickbot/bot/handlers/editors/main_callbacks.py b/src/quickbot/bot/handlers/editors/main_callbacks.py index e5a6cc6..a56d31d 100644 --- a/src/quickbot/bot/handlers/editors/main_callbacks.py +++ b/src/quickbot/bot/handlers/editors/main_callbacks.py @@ -37,7 +37,7 @@ from ..common.routing import route_callback from .common import show_editor if TYPE_CHECKING: - from ....main import QBotApp + from ....main import QuickBot router = Router() @@ -68,7 +68,7 @@ async def _validate_value( ContextData.filter(F.command == CallbackCommand.FIELD_EDITOR_CALLBACK) ) async def field_editor_callback(message: Message | CallbackQuery, **kwargs): - app: "QBotApp" = kwargs["app"] + app: "QuickBot" = kwargs["app"] callback_data: ContextData = kwargs.get("callback_data", None) state: FSMContext = kwargs["state"] @@ -205,7 +205,7 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs CommandContext.ENTITY_FIELD_EDIT, CommandContext.COMMAND_FORM, ]: - app: "QBotApp" = kwargs["app"] + app: "QuickBot" = kwargs["app"] entity_descriptor = get_entity_descriptor(app, callback_data) entity_data = state_data.get("entity_data", {}) diff --git a/src/quickbot/bot/handlers/forms/entity_form.py b/src/quickbot/bot/handlers/forms/entity_form.py index bd56562..a8a1c78 100644 --- a/src/quickbot/bot/handlers/forms/entity_form.py +++ b/src/quickbot/bot/handlers/forms/entity_form.py @@ -35,7 +35,7 @@ from ....utils.navigation import ( ) if TYPE_CHECKING: - from ....main import QBotApp + from ....main import QuickBot logger = getLogger(__name__) @@ -61,7 +61,7 @@ async def entity_item( callback_data: ContextData, db_session: AsyncSession, user: UserBase, - app: "QBotApp", + app: "QuickBot", navigation_stack: list[ContextData], **kwargs, ): diff --git a/src/quickbot/bot/handlers/forms/entity_form_callbacks.py b/src/quickbot/bot/handlers/forms/entity_form_callbacks.py index f1cffe2..cf3b8a3 100644 --- a/src/quickbot/bot/handlers/forms/entity_form_callbacks.py +++ b/src/quickbot/bot/handlers/forms/entity_form_callbacks.py @@ -20,7 +20,7 @@ from ....utils.main import ( from ..common.routing import route_callback if TYPE_CHECKING: - from ....main import QBotApp + from ....main import QuickBot router = Router() @@ -31,7 +31,7 @@ async def entity_delete_callback(query: CallbackQuery, **kwargs): callback_data: ContextData = kwargs["callback_data"] user: UserBase = kwargs["user"] db_session: AsyncSession = kwargs["db_session"] - app: "QBotApp" = kwargs["app"] + app: "QuickBot" = kwargs["app"] state: FSMContext = kwargs["state"] state_data = await state.get_data() kwargs["state_data"] = state_data diff --git a/src/quickbot/bot/handlers/forms/entity_list.py b/src/quickbot/bot/handlers/forms/entity_list.py index 1a38f15..34935b4 100644 --- a/src/quickbot/bot/handlers/forms/entity_list.py +++ b/src/quickbot/bot/handlers/forms/entity_list.py @@ -31,7 +31,7 @@ from ..common.filtering import add_filter_controls from ....utils.navigation import pop_navigation_context, save_navigation_context if TYPE_CHECKING: - from ....main import QBotApp + from ....main import QuickBot logger = getLogger(__name__) @@ -65,7 +65,7 @@ async def entity_list( callback_data: ContextData, db_session: AsyncSession, user: UserBase, - app: "QBotApp", + app: "QuickBot", navigation_stack: list[ContextData], **kwargs, ): diff --git a/src/quickbot/bot/handlers/menu/entities.py b/src/quickbot/bot/handlers/menu/entities.py index 74aba6a..6f8ea6b 100644 --- a/src/quickbot/bot/handlers/menu/entities.py +++ b/src/quickbot/bot/handlers/menu/entities.py @@ -12,7 +12,7 @@ from ....utils.main import get_send_message, get_callable_str from ....utils.navigation import save_navigation_context, pop_navigation_context if TYPE_CHECKING: - from ....main import QBotApp + from ....main import QuickBot logger = getLogger(__name__) @@ -35,7 +35,7 @@ async def menu_entry_entities(message: CallbackQuery, **kwargs): async def entities_menu( message: Message | CallbackQuery, - app: "QBotApp", + app: "QuickBot", state: FSMContext, navigation_stack: list[ContextData], **kwargs, diff --git a/src/quickbot/bot/handlers/start.py b/src/quickbot/bot/handlers/start.py index 72ec235..e2a0a1f 100644 --- a/src/quickbot/bot/handlers/start.py +++ b/src/quickbot/bot/handlers/start.py @@ -5,7 +5,7 @@ from aiogram.types import Message from logging import getLogger from sqlmodel.ext.asyncio.session import AsyncSession -from ...main import QBotApp +from ...main import QuickBot from ...model.settings import Settings from ...model.language import LanguageBase from ...model.user import UserBase @@ -18,7 +18,7 @@ router = Router() @router.message(CommandStart()) async def start(message: Message, **kwargs): - app: QBotApp = kwargs["app"] + app: QuickBot = kwargs["app"] state: FSMContext = kwargs["state"] state_data = await state.get_data() @@ -35,7 +35,7 @@ async def start(message: Message, **kwargs): async def default_start_handler[UserType: UserBase]( message: Message, db_session: AsyncSession, - app: QBotApp, + app: QuickBot, state: FSMContext, **kwargs, ) -> tuple[UserType, bool]: diff --git a/src/quickbot/bot/handlers/user_handlers/command_handler.py b/src/quickbot/bot/handlers/user_handlers/command_handler.py index d151af1..e50fe2d 100644 --- a/src/quickbot/bot/handlers/user_handlers/command_handler.py +++ b/src/quickbot/bot/handlers/user_handlers/command_handler.py @@ -15,7 +15,7 @@ from quickbot.model.descriptors import BotCommand, CommandCallbackContext from quickbot.model.settings import Settings if TYPE_CHECKING: - from quickbot.main import QBotApp + from quickbot.main import QuickBot from quickbot.model.user import UserBase from ..context import ContextData, CallbackCommand, CommandContext @@ -25,7 +25,7 @@ async def command_handler(message: Message | CallbackQuery, cmd: BotCommand, **k callback_data: ContextData = kwargs["callback_data"] state: FSMContext = kwargs["state"] state_data: dict = kwargs["state_data"] - app: "QBotApp" = kwargs["app"] + app: "QuickBot" = kwargs["app"] user: "UserBase" = kwargs["user"] entity_data_dict: dict = state_data.get("entity_data") diff --git a/src/quickbot/bot/handlers/user_handlers/main.py b/src/quickbot/bot/handlers/user_handlers/main.py index 1071f02..ff43e61 100644 --- a/src/quickbot/bot/handlers/user_handlers/main.py +++ b/src/quickbot/bot/handlers/user_handlers/main.py @@ -7,7 +7,7 @@ from quickbot.utils.main import clear_state from quickbot.utils.navigation import save_navigation_context if TYPE_CHECKING: - from quickbot.main import QBotApp + from quickbot.main import QuickBot from ..context import ContextData, CallbackCommand from .command_handler import command_handler @@ -43,7 +43,7 @@ async def command_callback(message: CallbackQuery, **kwargs): async def process_command_handler(message: Message | CallbackQuery, **kwargs): state_data: dict = kwargs["state_data"] callback_data: ContextData = kwargs["callback_data"] - app: "QBotApp" = kwargs["app"] + app: "QuickBot" = kwargs["app"] cmd = app.bot_commands.get(callback_data.user_command.split("&")[0]) if cmd is None: diff --git a/src/quickbot/main.py b/src/quickbot/main.py index b518921..55f107c 100644 --- a/src/quickbot/main.py +++ b/src/quickbot/main.py @@ -1,7 +1,7 @@ """ main.py - QuickBot RAD Framework Main Application Module -Defines QBotApp, the main entry point for the QuickBot rapid application development (RAD) framework for Telegram bots. +Defines QuickBot, the main entry point for the QuickBot rapid application development (RAD) framework for Telegram bots. Integrates FastAPI (HTTP API), Aiogram (Telegram bot logic), SQLModel (async DB), and i18n (internationalization). Key Features: @@ -66,7 +66,7 @@ ConfigType = TypeVar("ConfigType", bound=Config, default=Config) @asynccontextmanager -async def default_lifespan(app: "QBotApp"): +async def default_lifespan(app: "QuickBot"): logger.debug("starting qbot app") if app.lifespan_bot_init: @@ -88,7 +88,7 @@ async def default_lifespan(app: "QBotApp"): logger.info("qbot app stopped") -class QBotApp(Generic[UserType, ConfigType], FastAPI): +class QuickBot(Generic[UserType, ConfigType], FastAPI): """ Main application class for QuickBot RAD framework. @@ -121,7 +121,7 @@ class QBotApp(Generic[UserType, ConfigType], FastAPI): lifespan: Lifespan[AppType] | None = None, lifespan_bot_init: bool = True, lifespan_set_webhook: bool = True, - webhook_handler: Callable[["QBotApp", Request], Any] = None, + webhook_handler: Callable[["QuickBot", Request], Any] = None, allowed_updates: list[str] | None = None, **kwargs, ): diff --git a/src/quickbot/model/bot_metadata.py b/src/quickbot/model/bot_metadata.py index 481c4d7..f272f11 100644 --- a/src/quickbot/model/bot_metadata.py +++ b/src/quickbot/model/bot_metadata.py @@ -2,7 +2,7 @@ from fastapi.datastructures import State from typing import TYPE_CHECKING if TYPE_CHECKING: - from quickbot.main import QBotApp + from quickbot.main import QuickBot from .descriptors import EntityDescriptor, ProcessDescriptor from ._singleton import Singleton @@ -12,5 +12,5 @@ class BotMetadata(metaclass=Singleton): def __init__(self): self.entity_descriptors: dict[str, EntityDescriptor] = {} self.process_descriptors: dict[str, ProcessDescriptor] = {} - self.app: "QBotApp" = None + self.app: "QuickBot" = None self.app_state: State = None diff --git a/src/quickbot/model/descriptors.py b/src/quickbot/model/descriptors.py index 4475a01..29eb073 100644 --- a/src/quickbot/model/descriptors.py +++ b/src/quickbot/model/descriptors.py @@ -17,7 +17,7 @@ from ..bot.handlers.context import ContextData if TYPE_CHECKING: from .bot_entity import BotEntity - from ..main import QBotApp + from ..main import QuickBot from .user import UserBase from .crud_service import CrudService from .bot_process import BotProcess @@ -328,7 +328,7 @@ class CommandCallbackContext: callback_data: ContextData db_session: AsyncSession user: "UserBase" - app: "QBotApp" + app: "QuickBot" app_state: State state_data: dict[str, Any] state: FSMContext @@ -340,7 +340,7 @@ class CommandCallbackContext: @dataclass(kw_only=True) class BotContext: db_session: AsyncSession - app: "QBotApp" + app: "QuickBot" app_state: State user: "UserBase" message: Message | CallbackQuery | None = None diff --git a/src/quickbot/plugin.py b/src/quickbot/plugin.py index 7e1f466..c7c1ae2 100644 --- a/src/quickbot/plugin.py +++ b/src/quickbot/plugin.py @@ -1,9 +1,9 @@ from typing import TYPE_CHECKING, Protocol, runtime_checkable if TYPE_CHECKING: - from quickbot import QBotApp + from quickbot import QuickBot @runtime_checkable class Registerable(Protocol): - def register(self, app: "QBotApp") -> None: ... + def register(self, app: "QuickBot") -> None: ... diff --git a/src/quickbot/utils/main.py b/src/quickbot/utils/main.py index be3bd78..55ef1ad 100644 --- a/src/quickbot/utils/main.py +++ b/src/quickbot/utils/main.py @@ -31,7 +31,7 @@ from ..bot.handlers.context import CallbackCommand, ContextData, CommandContext if TYPE_CHECKING: from ..model.bot_entity import BotEntity from ..model.user import UserBase - from ..main import QBotApp + from ..main import QuickBot def get_local_text(text: str, locale: str = None) -> str: @@ -179,7 +179,7 @@ async def get_callable_str( def get_entity_descriptor( - app: "QBotApp", callback_data: ContextData + app: "QuickBot", callback_data: ContextData ) -> EntityDescriptor: if callback_data.entity_name: return app.bot_metadata.entity_descriptors[callback_data.entity_name] @@ -187,7 +187,7 @@ def get_entity_descriptor( def get_field_descriptor( - app: "QBotApp", callback_data: ContextData + app: "QuickBot", callback_data: ContextData ) -> FieldDescriptor | None: if callback_data.context == CommandContext.SETTING_EDIT: return Settings.list_params()[callback_data.field_name] diff --git a/tests/bot.py b/tests/bot.py index 0f2becf..7a4c73b 100644 --- a/tests/bot.py +++ b/tests/bot.py @@ -1,5 +1,5 @@ from quickbot import ( - QBotApp, + QuickBot, BotEntity, Entity, EntityForm, @@ -116,7 +116,7 @@ class Entity(BotEntity): ) -app = QBotApp( +app = QuickBot( user_class=User, )