first commit

This commit is contained in:
2026-04-12 21:58:52 +03:00
commit acfaa2a40c
44 changed files with 2895 additions and 0 deletions

0
bot/database/__init__.py Normal file
View File

82
bot/database/db_models.py Normal file
View File

@@ -0,0 +1,82 @@
# sqlalchemy
from sqlalchemy.orm import declarative_base
from sqlalchemy import (
Column,
Integer,
String,
BIGINT,
VARCHAR,
Boolean,
DateTime,
SmallInteger,
ARRAY,
DOUBLE_PRECISION,
Enum,
Numeric,
Text,
)
from sqlalchemy.dialects.postgresql import JSONB
# types
from database.db_types import *
# init baseModel
BaseModel = declarative_base()
class User(BaseModel):
__tablename__ = "users"
user_id = Column(BIGINT, primary_key=True)
username = Column(VARCHAR(33), nullable=True)
fullname = Column(VARCHAR(128), nullable=False)
register_date = Column(DateTime(timezone=True), nullable=False)
class Admin(BaseModel):
__tablename__ = "admins"
user_id = Column(BIGINT, primary_key=True)
username = Column(VARCHAR(33), nullable=True)
fullname = Column(VARCHAR(128), nullable=False)
class Blacklist(BaseModel):
__tablename__ = "blacklist"
user_id = Column(BIGINT, primary_key=True)
class Setting(BaseModel):
__tablename__ = "settings"
name = Column(String, primary_key=True)
value = Column(JSONB, nullable=True)
class Payment(BaseModel):
__tablename__ = "payments"
id = Column(Integer, primary_key=True, autoincrement=True)
order_id = Column(VARCHAR(64), unique=True, nullable=False, index=True)
user_id = Column(BIGINT, nullable=False, index=True)
amount = Column(Numeric(12, 2), nullable=False)
currency = Column(VARCHAR(3), nullable=False, default="RUB")
description = Column(VARCHAR(255), nullable=True)
status = Column(VARCHAR(32), nullable=False, default="created")
payment_link_id = Column(VARCHAR(36), nullable=True)
payment_url = Column(Text, nullable=True)
payment_link_status = Column(VARCHAR(32), nullable=True)
transaction_id = Column(VARCHAR(36), nullable=True)
transaction_status = Column(VARCHAR(32), nullable=True)
error_code = Column(VARCHAR(64), nullable=True)
error_description = Column(Text, nullable=True)
created_at = Column(DateTime(timezone=True), nullable=False)
updated_at = Column(DateTime(timezone=True), nullable=False)
paid_at = Column(DateTime(timezone=True), nullable=True)

16
bot/database/db_types.py Normal file
View File

@@ -0,0 +1,16 @@
import enum
# class Type(enum.Enum):
# FIELD1 = "field1"
# FIELD2 = "field2"
# @classmethod
# def from_string(cls, value: str):
# for item in cls:
# if item.value == value:
# return item
# raise ValueError(f"{value} is not a valid Type")
# def __str__(self):
# return self.value

18
bot/database/engine.py Normal file
View File

@@ -0,0 +1,18 @@
# sqlalchemy imports
from sqlalchemy.engine import URL
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
from sqlalchemy.ext.asyncio import create_async_engine as _create_async_engine
from sqlalchemy.orm import sessionmaker
# another
from typing import Union
def create_async_engine(url: Union[URL, str]) -> AsyncEngine:
return _create_async_engine(url=url, pool_pre_ping=True, pool_recycle=3600)
def get_session_maker(engine: AsyncEngine) -> AsyncSession:
return sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

323
bot/database/orm.py Normal file
View File

@@ -0,0 +1,323 @@
# sqlalchemy import
from sqlalchemy import update, select, delete, func
# Database engine
from database.engine import create_async_engine, get_session_maker
# DB Models
from database.db_models import *
# Config
from decouple import config
# Another
from datetime import datetime
from decimal import Decimal
from typing import Any
class ORM:
def __init__(self):
self.async_engine = create_async_engine(
url=f"postgresql+asyncpg://{config('POSTGRES_USER')}:{config('POSTGRES_PASSWORD')}@{config('POSTGRES_HOST')}:{config('POSTGRES_PORT')}/{config('POSTGRES_DB')}"
)
self.session_maker = get_session_maker(self.async_engine)
async def proceed_schemas(self) -> None:
async with self.async_engine.begin() as conn:
await conn.run_sync(BaseModel.metadata.create_all)
# *############################
# *# USERS #
# *############################
async def is_user_exists(self, user_id: int) -> bool:
async with self.session_maker() as session:
async with session.begin():
query = await session.execute(
select(User.user_id).where(User.user_id == user_id)
)
return query.one_or_none() is not None
async def create_user(
self, user_id: int, username: str, fullname: str, register_date: datetime
) -> int:
async with self.session_maker() as session:
async with session.begin():
if not await self.is_user_exists(user_id):
user = User(
user_id=user_id,
username=username,
fullname=fullname,
register_date=register_date,
)
session.add(user)
await session.flush()
return user.user_id
else:
return
async def set_users_field(
self, user_id: int, field: str, value: int | str | bool
) -> None:
async with self.session_maker() as session:
async with session.begin():
await session.execute(
update(User)
.where(User.user_id == user_id)
.values({getattr(User, field): value})
)
async def get_user(self, user_id: int) -> User:
async with self.session_maker() as session:
async with session.begin():
query = await session.scalars(
select(User).where(User.user_id == user_id)
)
return query.one_or_none()
async def get_all_users(self) -> list[User]:
async with self.session_maker() as session:
async with session.begin():
query = await session.scalars(select(User))
return query.all()
async def get_users_count(self) -> int:
async with self.session_maker() as session:
async with session.begin():
query = await session.scalars(select(func.count()).select_from(User))
return query.one_or_none()
async def get_all_user_ids(self) -> list[int]:
async with self.session_maker() as session:
async with session.begin():
query = await session.scalars(select(User.user_id))
return query.all()
# *############################
# *# ADMINS #
# *############################
async def is_admin_exists(self, user_id: int) -> bool:
async with self.session_maker() as session:
async with session.begin():
query = await session.execute(
select(Admin.user_id).where(Admin.user_id == user_id)
)
return query.one_or_none() is not None
async def create_admin(self, user_id: int, username: str, fullname: str) -> None:
async with self.session_maker() as session:
async with session.begin():
admin = Admin(user_id=user_id, username=username, fullname=fullname)
await session.merge(admin)
async def get_admin(self, user_id: int) -> Admin:
async with self.session_maker() as session:
async with session.begin():
query = await session.scalars(
select(Admin).where(Admin.user_id == user_id)
)
return query.one_or_none()
async def get_all_admins(self) -> list[Admin]:
async with self.session_maker() as session:
async with session.begin():
query = await session.scalars(select(Admin))
return query.all()
async def delete_admin(self, user_id: int) -> None:
async with self.session_maker() as session:
async with session.begin():
await session.execute(delete(Admin).where(Admin.user_id == user_id))
async def set_admin_field(
self, user_id: int, field: str, value: int | str | bool
) -> None:
async with self.session_maker() as session:
async with session.begin():
await session.execute(
update(Admin)
.where(Admin.user_id == user_id)
.values({getattr(Admin, field): value})
)
# *############################
# *# SETTINGS #
# *############################
async def is_setting_exists(self, name: str) -> bool:
async with self.session_maker() as session:
async with session.begin():
query = await session.execute(
select(Setting).where(Setting.name == name)
)
return query.one_or_none() is not None
async def create_setting(self, name: str, value: Any) -> None:
async with self.session_maker() as session:
async with session.begin():
setting = Setting(name=name, value=value)
await session.merge(setting)
async def init_settings(self) -> None: ...
async def get_setting_value(self, name: str) -> Any:
async with self.session_maker() as session:
async with session.begin():
query = await session.scalars(
select(Setting.value).where(Setting.name == name)
)
return query.one_or_none()
async def update_setting_value(self, name: str, value: dict | list) -> None:
async with self.session_maker() as session:
async with session.begin():
await session.execute(
update(Setting)
.where(Setting.name == name)
.values({getattr(Setting, "value"): value})
)
# *############################
# *# BLACKLIST #
# *############################
async def is_blacklisted(self, user_id: int) -> bool:
async with self.session_maker() as session:
async with session.begin():
query = await session.execute(
select(Blacklist).where(Blacklist.user_id == user_id)
)
return query.one_or_none() is not None
async def create_blacklist(self, user_id: int) -> None:
async with self.session_maker() as session:
async with session.begin():
blacklist = Blacklist(user_id=user_id)
await session.merge(blacklist)
async def get_all_blacklist(self) -> list[int]:
async with self.session_maker() as session:
async with session.begin():
query = await session.scalars(
select(Blacklist.user_id).order_by(Blacklist.user_id)
)
return query.all()
async def delete_blacklist(self, user_id: int) -> None:
async with self.session_maker() as session:
async with session.begin():
await session.execute(
delete(Blacklist).where(Blacklist.user_id == user_id)
)
# *############################
# *# PAYMENTS #
# *############################
async def create_payment(
self,
user_id: int,
order_id: str,
amount: Decimal,
currency: str,
description: str,
created_at: datetime,
) -> int:
async with self.session_maker() as session:
async with session.begin():
payment = Payment(
user_id=user_id,
order_id=order_id,
amount=amount,
currency=currency,
description=description,
status="created",
created_at=created_at,
updated_at=created_at,
)
session.add(payment)
await session.flush()
return payment.id
async def get_payment_by_order_id(self, order_id: str) -> Payment:
async with self.session_maker() as session:
async with session.begin():
query = await session.scalars(
select(Payment).where(Payment.order_id == order_id)
)
return query.one_or_none()
async def update_payment_link(
self,
order_id: str,
payment_link_id: str,
payment_url: str,
payment_link_status: str,
updated_at: datetime,
) -> None:
async with self.session_maker() as session:
async with session.begin():
await session.execute(
update(Payment)
.where(Payment.order_id == order_id)
.values(
payment_link_id=payment_link_id,
payment_url=payment_url,
payment_link_status=payment_link_status,
status="link_created",
updated_at=updated_at,
)
)
async def update_payment_status(
self,
order_id: str,
status: str,
transaction_status: str | None,
updated_at: datetime,
transaction_id: str | None = None,
error_code: str | None = None,
error_description: str | None = None,
paid_at: datetime | None = None,
) -> None:
async with self.session_maker() as session:
async with session.begin():
values = {
"status": status,
"transaction_status": transaction_status,
"updated_at": updated_at,
"error_code": error_code,
"error_description": error_description,
}
if transaction_id:
values["transaction_id"] = transaction_id
if paid_at:
values["paid_at"] = paid_at
await session.execute(
update(Payment).where(Payment.order_id == order_id).values(values)
)