add user model, commands handler, router setup, and related dependencies

This commit is contained in:
ruslangilfanov 2025-08-16 18:55:05 +03:00
parent 6ac3717ce2
commit 2a1f15fdf2
No known key found for this signature in database
11 changed files with 171 additions and 17 deletions

View File

@ -46,4 +46,5 @@ dev = [
"pre-commit>=4.2.0",
"pyupgrade>=3.20.0",
"ruff>=0.11.13",
"types-requests>=2.32.4.20250809",
]

View File

@ -4,8 +4,10 @@ import types
def get_app_models_modules() -> list[types.ModuleType]:
from greek_lang.glossaries import models as glossaries_models
from greek_lang.openai_manager import models as openai_manager_models
from greek_lang.users import models as users_models
return [
glossaries_models,
openai_manager_models,
users_models,
]

View File

@ -0,0 +1,54 @@
"""empty message
Revision ID: 747797032526
Revises: d30d80dee5a3
Create Date: 2025-08-16 17:53:23.785592
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = "747797032526"
down_revision: Union[str, None] = "d30d80dee5a3"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"users",
sa.Column("id", sa.BigInteger(), nullable=False),
sa.Column("is_bot", sa.Boolean(), nullable=False),
sa.Column("first_name", sa.String(), nullable=True),
sa.Column("last_name", sa.String(), nullable=True),
sa.Column("username", sa.String(), nullable=True),
sa.Column("language_code", sa.String(length=8), nullable=True),
sa.Column("is_premium", sa.Boolean(), nullable=True),
sa.Column("added_to_attachment_menu", sa.Boolean(), nullable=True),
sa.Column(
"registered_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.PrimaryKeyConstraint("id", name=op.f("pk_users")),
)
op.create_index(op.f("ix_users_id"), "users", ["id"], unique=False)
op.create_index(op.f("ix_users_username"), "users", ["username"], unique=False)
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f("ix_users_username"), table_name="users")
op.drop_index(op.f("ix_users_id"), table_name="users")
op.drop_table("users")
# ### end Alembic commands ###

View File

@ -1,11 +1,8 @@
import pydantic
from aiogram import Bot, Dispatcher, BaseMiddleware
from aiogram.enums import ParseMode
from aiogram.filters import CommandStart
from aiogram.fsm.storage.base import BaseStorage, DefaultKeyBuilder
from aiogram.fsm.storage.redis import RedisStorage
from aiogram.types import BotCommandScopeAllPrivateChats, Message
from aiogram_dialog import DialogManager, setup_dialogs
from aiogram.types import BotCommandScopeAllPrivateChats
from dependency_injector.wiring import Provide, inject
from ..configs.container import ConfigContainer
@ -26,14 +23,7 @@ def create_bot(
async def create_dispatcher() -> Dispatcher:
async def start(message: Message, dialog_manager: DialogManager) -> None:
user = message.from_user
if user is None:
return
await message.answer(
text="TEST",
parse_mode=ParseMode.HTML,
)
from .router import router as root_router
fsm_storage = await create_fsm_storage()
@ -45,10 +35,7 @@ async def create_dispatcher() -> Dispatcher:
for middleware in middlewares:
dp.update.middleware(middleware)
dp.message.register(start, CommandStart())
dp.business_message.register(start, CommandStart())
setup_dialogs(dp)
dp.include_routers(root_router)
return dp

View File

@ -0,0 +1,23 @@
from gettext import gettext
from aiogram import Router
from aiogram.enums import ParseMode
from aiogram.filters import CommandStart
from aiogram.types import Message
from greek_lang.users.manager import get_or_create_telegram_user
router = Router()
@router.message(CommandStart())
async def start(message: Message) -> None:
user = message.from_user
if user is None:
return
await get_or_create_telegram_user(user)
await message.answer(
text=gettext("Регистрация прошла.").format(user=user.full_name),
parse_mode=ParseMode.HTML,
)

View File

@ -0,0 +1,8 @@
from aiogram import Router
from .commands import router as commands_router
router = Router()
router.include_routers(
commands_router,
)

View File

View File

@ -0,0 +1,39 @@
from aiogram.types import User
from dependency_injector.wiring import Provide, inject
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
from greek_lang.database.container import DatabaseContainer
from greek_lang.users.models import User as TgUser
@inject
async def get_or_create_telegram_user(
user: User,
db_session_maker: async_sessionmaker[AsyncSession] = Provide[
DatabaseContainer.async_session_maker
],
) -> TgUser:
async with db_session_maker() as db_session, db_session.begin():
telegram_user: TgUser | None = await db_session.get(TgUser, user.id)
if telegram_user:
return telegram_user
try:
async with db_session_maker() as db_session, db_session.begin():
telegram_user = TgUser(
id=user.id,
username=user.username,
first_name=user.first_name,
last_name=user.last_name,
language_code=user.language_code,
is_bot=user.is_bot,
is_premium=user.is_premium,
added_to_attachment_menu=user.added_to_attachment_menu,
)
db_session.add(telegram_user)
return telegram_user
except IntegrityError:
telegram_user = await db_session.get(TgUser, user.id)
if telegram_user is None:
raise Exception(f"Can't find telegram_user = {user.id}") from None
return telegram_user

View File

@ -0,0 +1,26 @@
from datetime import datetime
from sqlalchemy import BigInteger, Boolean, DateTime, String, func
from sqlalchemy.orm import Mapped, mapped_column
from greek_lang.database.base import Base
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, index=True)
is_bot: Mapped[bool] = mapped_column(Boolean, nullable=False)
first_name: Mapped[str | None] = mapped_column(String, nullable=True)
last_name: Mapped[str | None] = mapped_column(String, nullable=True)
username: Mapped[str | None] = mapped_column(String, nullable=True, index=True)
language_code: Mapped[str | None] = mapped_column(String(length=8), nullable=True)
is_premium: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
added_to_attachment_menu: Mapped[bool | None] = mapped_column(
Boolean, nullable=True
)
registered_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
server_default=func.now(),
nullable=False,
)

View File

@ -6,7 +6,7 @@ import logging
import logging.config
import types
import cgitb
import cgitb # type: ignore[import-untyped]
import telebot
from requests import ReadTimeout

14
uv.lock
View File

@ -417,6 +417,7 @@ dev = [
{ name = "pre-commit" },
{ name = "pyupgrade" },
{ name = "ruff" },
{ name = "types-requests" },
]
[package.metadata]
@ -452,6 +453,7 @@ dev = [
{ name = "pre-commit", specifier = ">=4.2.0" },
{ name = "pyupgrade", specifier = ">=3.20.0" },
{ name = "ruff", specifier = ">=0.11.13" },
{ name = "types-requests", specifier = ">=2.32.4.20250809" },
]
[[package]]
@ -1292,6 +1294,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" },
]
[[package]]
name = "types-requests"
version = "2.32.4.20250809"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ed/b0/9355adb86ec84d057fea765e4c49cce592aaf3d5117ce5609a95a7fc3dac/types_requests-2.32.4.20250809.tar.gz", hash = "sha256:d8060de1c8ee599311f56ff58010fb4902f462a1470802cf9f6ed27bc46c4df3", size = 23027, upload-time = "2025-08-09T03:17:10.664Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2b/6f/ec0012be842b1d888d46884ac5558fd62aeae1f0ec4f7a581433d890d4b5/types_requests-2.32.4.20250809-py3-none-any.whl", hash = "sha256:f73d1832fb519ece02c85b1f09d5f0dd3108938e7d47e7f94bbfa18a6782b163", size = 20644, upload-time = "2025-08-09T03:17:09.716Z" },
]
[[package]]
name = "typing-extensions"
version = "4.14.1"