From baa8efe14580886c4312fa458786e2242ce245c3 Mon Sep 17 00:00:00 2001 From: AlexandrKudrya Date: Sat, 5 Apr 2025 18:25:08 +0700 Subject: [PATCH 01/29] Adding DB interaction code --- telegram_bot/database/__init__.py | 3 + telegram_bot/database/create_db.py | 14 +++ telegram_bot/database/env.py | 15 +++ telegram_bot/database/main.py | 16 +++ telegram_bot/database/methods/__init__.py | 45 +++++++ .../database/methods/merch_category.py | 24 ++++ telegram_bot/database/methods/merch_item.py | 32 +++++ telegram_bot/database/methods/pk_requests.py | 35 ++++++ .../database/methods/promo_activations.py | 30 +++++ telegram_bot/database/methods/promocodes.py | 47 ++++++++ telegram_bot/database/methods/purchases.py | 25 ++++ telegram_bot/database/methods/users.py | 113 ++++++++++++++++++ telegram_bot/database/migrations/README | 1 + telegram_bot/database/migrations/__init__.py | 0 telegram_bot/database/migrations/env.py | 92 ++++++++++++++ .../database/migrations/script.py.mako | 28 +++++ .../database/migrations/versions/__init__.py | 0 telegram_bot/database/models/Base.py | 6 + telegram_bot/database/models/Events.py | 11 ++ telegram_bot/database/models/MerchCategory.py | 14 +++ telegram_bot/database/models/MerchItem.py | 17 +++ telegram_bot/database/models/PKRequests.py | 19 +++ .../database/models/PromoActivation.py | 17 +++ telegram_bot/database/models/PromoCode.py | 22 ++++ telegram_bot/database/models/Purchase.py | 19 +++ telegram_bot/database/models/User.py | 25 ++++ telegram_bot/database/models/__init__.py | 22 ++++ 27 files changed, 692 insertions(+) create mode 100644 telegram_bot/database/__init__.py create mode 100644 telegram_bot/database/create_db.py create mode 100644 telegram_bot/database/env.py create mode 100644 telegram_bot/database/main.py create mode 100644 telegram_bot/database/methods/__init__.py create mode 100644 telegram_bot/database/methods/merch_category.py create mode 100644 telegram_bot/database/methods/merch_item.py create mode 100644 telegram_bot/database/methods/pk_requests.py create mode 100644 telegram_bot/database/methods/promo_activations.py create mode 100644 telegram_bot/database/methods/promocodes.py create mode 100644 telegram_bot/database/methods/purchases.py create mode 100644 telegram_bot/database/methods/users.py create mode 100644 telegram_bot/database/migrations/README create mode 100644 telegram_bot/database/migrations/__init__.py create mode 100644 telegram_bot/database/migrations/env.py create mode 100644 telegram_bot/database/migrations/script.py.mako create mode 100644 telegram_bot/database/migrations/versions/__init__.py create mode 100644 telegram_bot/database/models/Base.py create mode 100644 telegram_bot/database/models/Events.py create mode 100644 telegram_bot/database/models/MerchCategory.py create mode 100644 telegram_bot/database/models/MerchItem.py create mode 100644 telegram_bot/database/models/PKRequests.py create mode 100644 telegram_bot/database/models/PromoActivation.py create mode 100644 telegram_bot/database/models/PromoCode.py create mode 100644 telegram_bot/database/models/Purchase.py create mode 100644 telegram_bot/database/models/User.py create mode 100644 telegram_bot/database/models/__init__.py diff --git a/telegram_bot/database/__init__.py b/telegram_bot/database/__init__.py new file mode 100644 index 0000000..ed15e8e --- /dev/null +++ b/telegram_bot/database/__init__.py @@ -0,0 +1,3 @@ +from .main import engine, async_session + +__name__ = "database" diff --git a/telegram_bot/database/create_db.py b/telegram_bot/database/create_db.py new file mode 100644 index 0000000..66b0c60 --- /dev/null +++ b/telegram_bot/database/create_db.py @@ -0,0 +1,14 @@ +import asyncio +from models import Base +from main import engine + +async def init_db(): + async with engine.begin() as conn: + # Удаляем все таблицы (для теста, можно убрать в продакшене) + await conn.run_sync(Base.metadata.drop_all) + # Создаём все таблицы + await conn.run_sync(Base.metadata.create_all) + print("База данных успешно создана!") + +if __name__ == "__main__": + asyncio.run(init_db()) \ No newline at end of file diff --git a/telegram_bot/database/env.py b/telegram_bot/database/env.py new file mode 100644 index 0000000..806e5f0 --- /dev/null +++ b/telegram_bot/database/env.py @@ -0,0 +1,15 @@ +from os import environ +from pathlib import Path +from typing import Final + + +class PostgresKeys: + HOST: Final[str] = environ.get('DOCKER_POSTGRES_HOST', default='postgres') + PORT: Final[str] = environ.get('DOCKER_POSTGRES_PORT', default='5432') + + USER: Final[str] = environ.get('POSTGRES_USER', default='postgres') + PASSWORD: Final[str] = environ.get('POSTGRES_PASSWORD', default='') + + DATABASE: Final[str] = environ.get('POSTGRES_DB', default=USER) + + URL: Final[str] = f'postgresql+asyncpg://{USER}:{PASSWORD}@{HOST}:{PORT}/{DATABASE}' \ No newline at end of file diff --git a/telegram_bot/database/main.py b/telegram_bot/database/main.py new file mode 100644 index 0000000..67d6830 --- /dev/null +++ b/telegram_bot/database/main.py @@ -0,0 +1,16 @@ +from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession +from sqlalchemy.orm import sessionmaker + +from env import PostgresKeys + +engine = create_async_engine(PostgresKeys.URL) +async_session = sessionmaker( + engine, + class_=AsyncSession, + expire_on_commit=False +) + + +async def get_session() -> AsyncSession: + async with async_session() as session: + yield session diff --git a/telegram_bot/database/methods/__init__.py b/telegram_bot/database/methods/__init__.py new file mode 100644 index 0000000..f92d84d --- /dev/null +++ b/telegram_bot/database/methods/__init__.py @@ -0,0 +1,45 @@ +from .users import ( + create_user, + get_user_by_telegram_id, + update_user_balance, + has_permission, + has_event_responsibility, + update_user_rights, + add_event_responsibility, + remove_event_responsibility, + get_users_by_creation_date, + set_promoter +) +from .promocodes import create_promo_code, get_promo_code_by_code, activate_promo_code +from .promo_activations import record_promo_activation, has_activated_promo +from .merch_item import create_merch_item, get_merch_item_by_id, get_available_merch_items +from .purchases import record_purchase, get_user_purchases +from .pk_requests import create_privilege_request, get_pending_requests +from .merch_category import create_merch_category, get_all_categories + +__all__ = [ + "create_user", + "get_user_by_telegram_id", + "update_user_balance", + "has_permission", + "has_event_responsibility", + "update_user_rights", + "add_event_responsibility", + "remove_event_responsibility", + "get_users_by_creation_date", + "set_promoter", + "create_promo_code", + "get_promo_code_by_code", + "activate_promo_code", + "record_promo_activation", + "has_activated_promo", + "create_merch_item", + "get_merch_item_by_id", + "get_available_merch_items", + "record_purchase", + "get_user_purchases", + "create_privilege_request", + "get_pending_requests", + "create_merch_category", + "get_all_categories", +] \ No newline at end of file diff --git a/telegram_bot/database/methods/merch_category.py b/telegram_bot/database/methods/merch_category.py new file mode 100644 index 0000000..cb575d0 --- /dev/null +++ b/telegram_bot/database/methods/merch_category.py @@ -0,0 +1,24 @@ +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from models import MerchCategory, MerchItem + + +async def create_merch_category(session: AsyncSession, name: str) -> MerchCategory: + """Создаёт новую категорию товаров.""" + category = MerchCategory(name=name) + session.add(category) + await session.commit() + return category + + +async def get_all_categories(session: AsyncSession) -> list[MerchCategory]: + """Возвращает список всех категорий.""" + result = await session.execute(select(MerchCategory)) + return result.scalars().all() + + +async def get_merch_by_category(session: AsyncSession, category_id: int) -> list[MerchItem]: + """Возвращает список товаров в указанной категории.""" + result = await session.execute(select(MerchItem).where(MerchItem.category_id == category_id)) + return result.scalars().all() diff --git a/telegram_bot/database/methods/merch_item.py b/telegram_bot/database/methods/merch_item.py new file mode 100644 index 0000000..813fbbf --- /dev/null +++ b/telegram_bot/database/methods/merch_item.py @@ -0,0 +1,32 @@ +from sqlalchemy import select, update +from sqlalchemy.ext.asyncio import AsyncSession + +from models import MerchItem + + +async def create_merch_item(session: AsyncSession, merch_name: str, price: int, stock: int, + category_id: int) -> MerchItem: + """Создаёт новый товар.""" + item = MerchItem(merch_name=merch_name, price=price, stock=stock, category_id=category_id) + session.add(item) + await session.commit() + return item + + +async def get_merch_item_by_id(session: AsyncSession, item_id: int) -> MerchItem | None: + """Возвращает товар по ID или None, если не найден.""" + return await session.get(MerchItem, item_id) + + +async def update_merch_stock(session: AsyncSession, item_id: int, quantity: int): + """Обновляет количество товара.""" + await session.execute( + update(MerchItem).where(MerchItem.id == item_id).values(stock=MerchItem.stock + quantity) + ) + await session.commit() + + +async def get_available_merch_items(session: AsyncSession) -> list[MerchItem]: + """Возвращает список доступных товаров.""" + result = await session.execute(select(MerchItem).where(MerchItem.is_available, MerchItem.stock > 0)) + return result.scalars().all() diff --git a/telegram_bot/database/methods/pk_requests.py b/telegram_bot/database/methods/pk_requests.py new file mode 100644 index 0000000..642867a --- /dev/null +++ b/telegram_bot/database/methods/pk_requests.py @@ -0,0 +1,35 @@ +from datetime import datetime + +from sqlalchemy import select, update +from sqlalchemy.ext.asyncio import AsyncSession + +from models import PKRequests + + +async def create_privilege_request(session: AsyncSession, user_id: int): + """Создаёт заявку на привилегированный статус.""" + request = PKRequests(user_id=user_id) + session.add(request) + await session.commit() + + +async def update_request_status(session: AsyncSession, request_id: int, status: str, reviewed_by: int): + """Обновляет статус заявки.""" + await session.execute( + update(PKRequests) + .where(PKRequests.id == request_id) + .values(status=status, reviewed_at=datetime.utcnow(), reviewed_by=reviewed_by) + ) + await session.commit() + + +async def get_pending_requests(session: AsyncSession) -> list[PKRequests]: + """Возвращает список заявок со статусом 'pending'.""" + result = await session.execute(select(PKRequests).where(PKRequests.status == "pending")) + return result.scalars().all() + + +async def get_user_request(session: AsyncSession, user_id: int) -> PKRequests | None: + """Возвращает заявку пользователя или None, если не найдена.""" + result = await session.execute(select(PKRequests).where(PKRequests.user_id == user_id)) + return result.scalar_one_or_none() diff --git a/telegram_bot/database/methods/promo_activations.py b/telegram_bot/database/methods/promo_activations.py new file mode 100644 index 0000000..563f65f --- /dev/null +++ b/telegram_bot/database/methods/promo_activations.py @@ -0,0 +1,30 @@ +from models import PromoActivation +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy import select + + +async def record_promo_activation(session: AsyncSession, user_id: int, promo_id: int): + """Записывает активацию промокода пользователем.""" + activation = PromoActivation(user_id=user_id, promo_id=promo_id) + session.add(activation) + await session.commit() + + +async def has_activated_promo(session: AsyncSession, user_id: int, promo_id: int) -> bool: + """Проверяет, активировал ли пользователь указанный промокод.""" + result = await session.execute( + select(PromoActivation).where(PromoActivation.user_id == user_id, PromoActivation.promo_id == promo_id) + ) + return result.scalar_one_or_none() is not None + + +async def get_user_activations(session: AsyncSession, user_id: int) -> list[PromoActivation]: + """Возвращает список всех активаций промокодов пользователя.""" + result = await session.execute(select(PromoActivation).where(PromoActivation.user_id == user_id)) + return result.scalars().all() + + +async def get_users_by_code(session: AsyncSession, promo_id: int) -> list[PromoActivation]: + """Возвращает список всех пользователей, активировавших промокод.""" + result = await session.execute(select(PromoActivation).where(PromoActivation.promo_id == promo_id)) + return result.scalars().all() \ No newline at end of file diff --git a/telegram_bot/database/methods/promocodes.py b/telegram_bot/database/methods/promocodes.py new file mode 100644 index 0000000..c7b1ccf --- /dev/null +++ b/telegram_bot/database/methods/promocodes.py @@ -0,0 +1,47 @@ +from models import PromoCode +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy import select, update + + +async def create_promo_code( + session: AsyncSession, + code: str, + value: int, + creator_id: int, + expired_at=None, + max_uses=10000, +) -> PromoCode: + """Создаёт новый промокод.""" + promo = PromoCode( + code=code, + value=value, + is_active=True, + creator_id=creator_id, + expired_at=expired_at, + max_uses=max_uses, + ) + session.add(promo) + await session.commit() + return promo + + +async def get_promo_code_by_code(session: AsyncSession, code: str) -> PromoCode | None: + """Возвращает промокод по его значению или None, если не найден.""" + result = await session.execute(select(PromoCode).where(PromoCode.code == code)) + return result.scalar_one_or_none() + + +async def activate_promo_code(session: AsyncSession, promo_id: int): + """Увеличивает счётчик использований промокода.""" + await session.execute( + update(PromoCode).where(PromoCode.id == promo_id).values(used_count=PromoCode.used_count + 1) + ) + await session.commit() + + +async def deactivate_promo_code(session: AsyncSession, promo_id: int): + """Деактивирует промокод.""" + await session.execute( + update(PromoCode).where(PromoCode.id == promo_id).values(is_active=False) + ) + await session.commit() \ No newline at end of file diff --git a/telegram_bot/database/methods/purchases.py b/telegram_bot/database/methods/purchases.py new file mode 100644 index 0000000..5327255 --- /dev/null +++ b/telegram_bot/database/methods/purchases.py @@ -0,0 +1,25 @@ +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.sql import func +from models import Purchase + + +async def record_purchase(session: AsyncSession, user_id: int, merch_id: int, quantity: int, total_cost: int): + """Записывает покупку товара пользователем.""" + purchase = Purchase(user_id=user_id, merch_id=merch_id, quantity=quantity, total_cost=total_cost) + session.add(purchase) + await session.commit() + + +async def get_user_purchases(session: AsyncSession, user_id: int) -> list[Purchase]: + """Возвращает историю покупок пользователя.""" + result = await session.execute(select(Purchase).where(Purchase.user_id == user_id)) + return result.scalars().all() + + +async def get_total_spent_by_user(session: AsyncSession, user_id: int) -> int: + """Возвращает общую сумму, потраченную пользователем.""" + result = await session.execute( + select(func.sum(Purchase.total_cost)).where(Purchase.user_id == user_id) + ) + return result.scalar_one_or_none() or 0 diff --git a/telegram_bot/database/methods/users.py b/telegram_bot/database/methods/users.py new file mode 100644 index 0000000..cd4c500 --- /dev/null +++ b/telegram_bot/database/methods/users.py @@ -0,0 +1,113 @@ +from datetime import datetime + +from sqlalchemy import select, update +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.orm import selectinload + +from models import User + + +async def create_user( + session: AsyncSession, + telegram_id: int, + username: str, + rights: int = 0, + events: int = 0, + balance: int = 0, + promoted_by: int | None = None +) -> User: + """Создаёт нового пользователя с указанными параметрами.""" + user = User( + telegram_id=telegram_id, + username=username, + rights=rights, + events=events, + balance=balance, + promoted_by=promoted_by + ) + session.add(user) + await session.commit() + return user + + +async def get_user_by_telegram_id(session: AsyncSession, telegram_id: int) -> User | None: + """Возвращает пользователя по telegram_id с предзагрузкой связанных данных.""" + result = await session.execute( + select(User) + .where(User.telegram_id == telegram_id) + .options( + selectinload(User.promoter), + selectinload(User.promo_codes), + selectinload(User.activations), + selectinload(User.purchases), + selectinload(User.requests), + selectinload(User.reviews) + ) + ) + return result.scalar_one_or_none() + + +async def update_user_balance(session: AsyncSession, user_id: int, amount: int): + """Обновляет баланс пользователя.""" + await session.execute( + update(User).where(User.id == user_id).values(balance=User.balance + amount) + ) + await session.commit() + + +async def has_permission(session: AsyncSession, user_id: int, permission: int) -> bool: + """Проверяет, есть ли у пользователя указанное право (битовая маска).""" + user = await session.get(User, user_id) + return bool(user.rights & permission) if user else False + + +async def has_event_responsibility(session: AsyncSession, user_id: int, event_flag: int) -> bool: + """Проверяет, отвечает ли пользователь за указанное мероприятие (битовая маска).""" + user = await session.get(User, user_id) + return bool(user.events & event_flag) if user else False + + +async def update_user_rights(session: AsyncSession, user_id: int, rights: int): + """Обновляет права пользователя.""" + await session.execute( + update(User).where(User.id == user_id).values(rights=rights) + ) + await session.commit() + + +async def add_event_responsibility(session: AsyncSession, user_id: int, event_flag: int): + """Добавляет ответственность за мероприятие пользователю.""" + user = await session.get(User, user_id) + if user: + new_events = user.events | event_flag + await session.execute( + update(User).where(User.id == user_id).values(events=new_events) + ) + await session.commit() + + +async def remove_event_responsibility(session: AsyncSession, user_id: int, event_flag: int): + """Удаляет ответственность за мероприятие у пользователя.""" + user = await session.get(User, user_id) + if user: + new_events = user.events & ~event_flag + await session.execute( + update(User).where(User.id == user_id).values(events=new_events) + ) + await session.commit() + + +async def get_users_by_creation_date(session: AsyncSession, start_date: datetime, end_date: datetime) -> list[User]: + """Возвращает пользователей, созданных в указанном диапазоне дат.""" + result = await session.execute( + select(User).where(User.created_at.between(start_date, end_date)) + ) + return result.scalars().all() + + +async def set_promoter(session: AsyncSession, user_id: int, promoter_id: int): + """Устанавливает пользователя, который дал права данному пользователю.""" + await session.execute( + update(User).where(User.id == user_id).values(promoted_by=promoter_id) + ) + await session.commit() diff --git a/telegram_bot/database/migrations/README b/telegram_bot/database/migrations/README new file mode 100644 index 0000000..e0d0858 --- /dev/null +++ b/telegram_bot/database/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration with an async dbapi. \ No newline at end of file diff --git a/telegram_bot/database/migrations/__init__.py b/telegram_bot/database/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/telegram_bot/database/migrations/env.py b/telegram_bot/database/migrations/env.py new file mode 100644 index 0000000..a1ad27d --- /dev/null +++ b/telegram_bot/database/migrations/env.py @@ -0,0 +1,92 @@ +import asyncio +from logging.config import fileConfig + +from sqlalchemy import pool +from sqlalchemy.engine import Connection +from sqlalchemy.ext.asyncio import async_engine_from_config + +from alembic import context +from database.models import Base +from database.env import PostgresKeys + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config +config.set_main_option('sqlalchemy.url', PostgresKeys.URL) + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def do_run_migrations(connection: Connection) -> None: + context.configure(connection=connection, target_metadata=target_metadata) + + with context.begin_transaction(): + context.run_migrations() + + +async def run_async_migrations() -> None: + """In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + connectable = async_engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + async with connectable.connect() as connection: + await connection.run_sync(do_run_migrations) + + await connectable.dispose() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode.""" + + asyncio.run(run_async_migrations()) + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/telegram_bot/database/migrations/script.py.mako b/telegram_bot/database/migrations/script.py.mako new file mode 100644 index 0000000..480b130 --- /dev/null +++ b/telegram_bot/database/migrations/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/telegram_bot/database/migrations/versions/__init__.py b/telegram_bot/database/migrations/versions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/telegram_bot/database/models/Base.py b/telegram_bot/database/models/Base.py new file mode 100644 index 0000000..1e2a451 --- /dev/null +++ b/telegram_bot/database/models/Base.py @@ -0,0 +1,6 @@ +from sqlalchemy.ext.asyncio import AsyncAttrs +from sqlalchemy.orm import DeclarativeBase + + +class Base(AsyncAttrs, DeclarativeBase): + pass \ No newline at end of file diff --git a/telegram_bot/database/models/Events.py b/telegram_bot/database/models/Events.py new file mode 100644 index 0000000..21167a4 --- /dev/null +++ b/telegram_bot/database/models/Events.py @@ -0,0 +1,11 @@ +from sqlalchemy import Column, Integer, String, DateTime + +from models import Base + + +# Таблица events +class Events(Base): + __tablename__ = "events" + id = Column(Integer, primary_key=True) + name = Column(String(255)) + date = Column(DateTime) \ No newline at end of file diff --git a/telegram_bot/database/models/MerchCategory.py b/telegram_bot/database/models/MerchCategory.py new file mode 100644 index 0000000..995a5b1 --- /dev/null +++ b/telegram_bot/database/models/MerchCategory.py @@ -0,0 +1,14 @@ +from sqlalchemy import Column, Integer, String +from sqlalchemy.orm import relationship + + +# Таблица merch_categories +from models import Base + + +class MerchCategory(Base): + __tablename__ = "merch_categories" + id = Column(Integer, primary_key=True) + name = Column(String(100), nullable=False) + + items = relationship("MerchItem", back_populates="category") \ No newline at end of file diff --git a/telegram_bot/database/models/MerchItem.py b/telegram_bot/database/models/MerchItem.py new file mode 100644 index 0000000..3e3e7d2 --- /dev/null +++ b/telegram_bot/database/models/MerchItem.py @@ -0,0 +1,17 @@ +from sqlalchemy import Column, Integer, String, Boolean, ForeignKey +from sqlalchemy.orm import relationship + +from models import Base + + +class MerchItem(Base): + __tablename__ = "merch_items" + id = Column(Integer, primary_key=True) + merch_name = Column(String(255), nullable=False) + price = Column(Integer, nullable=False) + stock = Column(Integer, default=0, nullable=False) + is_available = Column(Boolean, default=True, nullable=False) + category_id = Column(Integer, ForeignKey("merch_categories.id"), nullable=True) + + category = relationship("MerchCategory", back_populates="items") + purchases = relationship("Purchase", back_populates="merch") \ No newline at end of file diff --git a/telegram_bot/database/models/PKRequests.py b/telegram_bot/database/models/PKRequests.py new file mode 100644 index 0000000..121bfd3 --- /dev/null +++ b/telegram_bot/database/models/PKRequests.py @@ -0,0 +1,19 @@ +from datetime import datetime + +from sqlalchemy import Column, Integer, String, ForeignKey, DateTime +from sqlalchemy.orm import relationship + +from models import Base + + +class PKRequests(Base): + __tablename__ = "pk_requests" + id = Column(Integer, primary_key=True) + user_id = Column(Integer, ForeignKey("users.id"), unique=True, nullable=False) + status = Column(String(50), default="pending", nullable=False) + submitted_at = Column(DateTime, default=datetime.utcnow) + reviewed_at = Column(DateTime) + reviewed_by = Column(Integer, ForeignKey("users.id"), nullable=True) + + user = relationship("User", foreign_keys=[user_id], back_populates="requests") + reviewer = relationship("User", foreign_keys=[reviewed_by], back_populates="reviews") \ No newline at end of file diff --git a/telegram_bot/database/models/PromoActivation.py b/telegram_bot/database/models/PromoActivation.py new file mode 100644 index 0000000..a03105b --- /dev/null +++ b/telegram_bot/database/models/PromoActivation.py @@ -0,0 +1,17 @@ +from sqlalchemy import Column, Integer, String, Boolean, ForeignKey, DateTime +from sqlalchemy.ext.asyncio import AsyncAttrs +from sqlalchemy.orm import DeclarativeBase, relationship +from datetime import datetime + +from models import Base + + +class PromoActivation(Base): + __tablename__ = "promo_activations" + id = Column(Integer, primary_key=True) + user_id = Column(Integer, ForeignKey("users.id"), nullable=False) + promo_id = Column(Integer, ForeignKey("promo_codes.id"), nullable=False) + activated_at = Column(DateTime, default=datetime.utcnow) + + user = relationship("User", back_populates="activations") + promo = relationship("PromoCode", back_populates="activations") \ No newline at end of file diff --git a/telegram_bot/database/models/PromoCode.py b/telegram_bot/database/models/PromoCode.py new file mode 100644 index 0000000..86a9003 --- /dev/null +++ b/telegram_bot/database/models/PromoCode.py @@ -0,0 +1,22 @@ +from datetime import datetime + +from sqlalchemy import Column, Integer, String, Boolean, ForeignKey, DateTime +from sqlalchemy.orm import relationship + +from models import Base + + +class PromoCode(Base): + __tablename__ = "promo_codes" + id = Column(Integer, primary_key=True) + code = Column(String(50), unique=True, nullable=False) + value = Column(Integer, nullable=False) + is_active = Column(Boolean, default=True, nullable=False) + creator_id = Column(Integer, ForeignKey("users.id"), nullable=True) + created_at = Column(DateTime, default=datetime.utcnow) + expires_at = Column(DateTime) + max_uses = Column(Integer) + used_count = Column(Integer, default=0, nullable=False) + + creator = relationship("User", back_populates="promo_codes") + activations = relationship("PromoActivation", back_populates="promo") diff --git a/telegram_bot/database/models/Purchase.py b/telegram_bot/database/models/Purchase.py new file mode 100644 index 0000000..58c41f2 --- /dev/null +++ b/telegram_bot/database/models/Purchase.py @@ -0,0 +1,19 @@ +from sqlalchemy import Column, Integer, String, Boolean, ForeignKey, DateTime +from sqlalchemy.ext.asyncio import AsyncAttrs +from sqlalchemy.orm import DeclarativeBase, relationship +from datetime import datetime + +from models import Base + + +class Purchase(Base): + __tablename__ = "purchases" + id = Column(Integer, primary_key=True) + user_id = Column(Integer, ForeignKey("users.id"), nullable=False) + merch_id = Column(Integer, ForeignKey("merch_items.id"), nullable=False) + quantity = Column(Integer, nullable=False) + total_cost = Column(Integer, nullable=False) + purchased_at = Column(DateTime, default=datetime.utcnow) + + user = relationship("User", back_populates="purchases") + merch = relationship("MerchItem", back_populates="purchases") \ No newline at end of file diff --git a/telegram_bot/database/models/User.py b/telegram_bot/database/models/User.py new file mode 100644 index 0000000..365bf82 --- /dev/null +++ b/telegram_bot/database/models/User.py @@ -0,0 +1,25 @@ +from sqlalchemy import Column, Integer, String, Boolean, ForeignKey, DateTime +from sqlalchemy.ext.asyncio import AsyncAttrs +from sqlalchemy.orm import DeclarativeBase, relationship +from datetime import datetime + +from models import Base + + +class User(Base): + __tablename__ = "users" + id = Column(Integer, primary_key=True) + telegram_id = Column(Integer, unique=True, nullable=False) + username = Column(String(255)) + rights = Column(Integer, default=0, nullable=False) # Битовая маска прав + events = Column(Integer, default=0, nullable=False) # Битовая маска мероприятий + balance = Column(Integer, default=0, nullable=False) + created_at = Column(DateTime, default=datetime.utcnow) + promoted_by = Column(Integer, ForeignKey("users.id"), nullable=True) + + promoter = relationship("User", remote_side=[id], backref="promoted_users") + promo_codes = relationship("PromoCode", back_populates="creator") + activations = relationship("PromoActivation", back_populates="user") + purchases = relationship("Purchase", back_populates="user") + requests = relationship("PrivilegeRequest", back_populates="user") + reviews = relationship("PrivilegeRequest", foreign_keys="PrivilegeRequest.reviewed_by", back_populates="reviewer") diff --git a/telegram_bot/database/models/__init__.py b/telegram_bot/database/models/__init__.py new file mode 100644 index 0000000..e8dca0c --- /dev/null +++ b/telegram_bot/database/models/__init__.py @@ -0,0 +1,22 @@ +from .Base import Base + +from .Events import Events +from .MerchCategory import MerchCategory +from .MerchItem import MerchItem +from .PKRequests import PKRequests +from .PromoActivation import PromoActivation +from .PromoCode import PromoCode +from .Purchase import Purchase +from .User import User + +__all__ = ( + "Base", + "Events", + "MerchCategory", + "MerchItem", + "PKRequests", + "PromoActivation", + "PromoCode", + "Purchase", + "User" +) From f988ebb59b463c7ab191b79675c9bd800275b35f Mon Sep 17 00:00:00 2001 From: Dafie Date: Sat, 5 Apr 2025 15:00:49 +0300 Subject: [PATCH 02/29] Add README --- README.md | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..b77eabb --- /dev/null +++ b/README.md @@ -0,0 +1,245 @@ + + + + + + + + +[![Contributors][contributors-shield]][contributors-url] +[![Forks][forks-shield]][forks-url] +[![Stargazers][stars-shield]][stars-url] +[![Issues][issues-shield]][issues-url] +[![project_license][license-shield]][license-url] +[![LinkedIn][linkedin-shield]][linkedin-url] + + + + +
+
+ + Logo + + +

project_title

+ +

+ project_description +
+ Explore the docs » +
+
+ View Demo + · + Report Bug + · + Request Feature +

+
+ + + + +
+ Table of Contents +
    +
  1. + About The Project + +
  2. +
  3. + Getting Started + +
  4. +
  5. Usage
  6. +
  7. Roadmap
  8. +
  9. Contributing
  10. +
  11. License
  12. +
  13. Contact
  14. +
  15. Acknowledgments
  16. +
+
+ + + + +## About The Project + +Here's a blank template to get started. To avoid retyping too much info, do a search and replace with your text editor for the following: `RuVl`, `MatMex-Week`, `twitter_handle`, `linkedin_username`, `email_client`, `email`, `project_title`, `project_description`, `project_license` + +

(back to top)

+ + + +### Built With + +* [![Python][Python.org]][Python-url] +* [![Docker][Docker.com]][Docker-url] +* [![Postgres][Postgres.org]][Postgres-url] +* [![Redis][Redis.io]][Redis-url] + +

(back to top)

+ + + + +## Getting Started + +This is an example of how you may give instructions on setting up your project locally. +To get a local copy up and running follow these simple example steps. + +### Prerequisites + +This is an example of how to list things you need to use the software and how to install them. +* npm + ```sh + npm install npm@latest -g + ``` + +### Installation + +1. Get a free API Key at [https://example.com](https://example.com) +2. Clone the repo + ```sh + git clone https://github.com/RuVl/MatMex-Week.git + ``` +3. Install NPM packages + ```sh + npm install + ``` +4. Enter your API in `config.js` + ```js + const API_KEY = 'ENTER YOUR API'; + ``` +5. Change git remote url to avoid accidental pushes to base project + ```sh + git remote set-url origin RuVl/MatMex-Week + git remote -v # confirm the changes + ``` + +

(back to top)

+ + + + +## Usage + +Use this space to show useful examples of how a project can be used. Additional screenshots, code examples and demos work well in this space. You may also link to more resources. + +_For more examples, please refer to the [Documentation](https://example.com)_ + +

(back to top)

+ + + + +## Roadmap + +- [ ] Feature 1 +- [ ] Feature 2 +- [ ] Feature 3 + - [ ] Nested Feature + +See the [open issues](https://github.com/RuVl/MatMex-Week/issues) for a full list of proposed features (and known issues). + +

(back to top)

+ + + + +## Contributing + +Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. + +If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". +Don't forget to give the project a star! Thanks again! + +1. Fork the Project +2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) +3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the Branch (`git push origin feature/AmazingFeature`) +5. Open a Pull Request + +

(back to top)

+ +### Top contributors: + + + contrib.rocks image + + + + + +## License + +Distributed under the project_license. See `LICENSE.txt` for more information. + +

(back to top)

+ + + + +## Contact + +Your Name - [@twitter_handle](https://twitter.com/twitter_handle) - email@email_client.com + +Project Link: [https://github.com/RuVl/MatMex-Week](https://github.com/RuVl/MatMex-Week) + +

(back to top)

+ + + + +## Acknowledgments + +* []() +* []() +* []() + +

(back to top)

+ + + + + +[contributors-shield]: https://img.shields.io/github/contributors/RuVl/MatMex-Week.svg?style=for-the-badge +[contributors-url]: https://github.com/RuVl/MatMex-Week/graphs/contributors +[forks-shield]: https://img.shields.io/github/forks/RuVl/MatMex-Week.svg?style=for-the-badge +[forks-url]: https://github.com/RuVl/MatMex-Week/network/members +[stars-shield]: https://img.shields.io/github/stars/RuVl/MatMex-Week.svg?style=for-the-badge +[stars-url]: https://github.com/RuVl/MatMex-Week/stargazers +[issues-shield]: https://img.shields.io/github/issues/RuVl/MatMex-Week.svg?style=for-the-badge +[issues-url]: https://github.com/RuVl/MatMex-Week/issues +[license-shield]: https://img.shields.io/github/license/RuVl/MatMex-Week.svg?style=for-the-badge +[license-url]: https://github.com/RuVl/MatMex-Week/blob/master/LICENSE.txt +[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555 +[linkedin-url]: https://linkedin.com/in/linkedin_username +[product-screenshot]: images/screenshot.png + +[Python.org]: https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54 +[Python-url]: https://python.org +[Docker.com]: https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white +[Docker-url]: https://docker.com +[Postgres.org]: https://img.shields.io/badge/postgres-%23316192.svg?style=for-the-badge&logo=postgresql&logoColor=white +[Postgres-url]: https://postgresql.org +[Redis.io]: https://img.shields.io/badge/redis-%23DD0031.svg?style=for-the-badge&logo=redis&logoColor=white +[Redic-url]: https://redis.io From 649b33a1d13fba9395c58b6ad5c445bbb3803e00 Mon Sep 17 00:00:00 2001 From: Dafie Date: Sat, 5 Apr 2025 15:54:58 +0300 Subject: [PATCH 03/29] README v0.1 --- README.md | 93 ++++++++++--------------------------------------------- 1 file changed, 17 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index b77eabb..8224fca 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,7 @@ [![Forks][forks-shield]][forks-url] [![Stargazers][stars-shield]][stars-url] [![Issues][issues-shield]][issues-url] -[![project_license][license-shield]][license-url] -[![LinkedIn][linkedin-shield]][linkedin-url] +[![MIT][license-shield]][license-url] @@ -34,10 +33,10 @@ Logo -

project_title

+

MatMex Week Bot

- project_description + Bot for mathmech week
Explore the docs »
@@ -69,12 +68,9 @@

  • Installation
  • -
  • Usage
  • -
  • Roadmap
  • -
  • Contributing
  • +
  • Contributing
  • License
  • Contact
  • -
  • Acknowledgments
  • @@ -83,7 +79,8 @@ ## About The Project -Here's a blank template to get started. To avoid retyping too much info, do a search and replace with your text editor for the following: `RuVl`, `MatMex-Week`, `twitter_handle`, `linkedin_username`, `email_client`, `email`, `project_title`, `project_description`, `project_license` +Данный бот предназначен для недели Матмеха. В его функции входят регистрация новых участников недели, начисление валюты за участие в событиях, магазин мерча, администрирование пользователей. +Сам бот обернут в докер и сопровождается postgres и redis.

    (back to top)

    @@ -103,80 +100,36 @@ Here's a blank template to get started. To avoid retyping too much info, do a se ## Getting Started -This is an example of how you may give instructions on setting up your project locally. -To get a local copy up and running follow these simple example steps. +How to launch bot ### Prerequisites -This is an example of how to list things you need to use the software and how to install them. -* npm - ```sh - npm install npm@latest -g - ``` +* docker [https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository](Instruction for Ubuntu) ### Installation -1. Get a free API Key at [https://example.com](https://example.com) -2. Clone the repo +1. Clone the repo ```sh git clone https://github.com/RuVl/MatMex-Week.git ``` -3. Install NPM packages - ```sh - npm install - ``` -4. Enter your API in `config.js` - ```js - const API_KEY = 'ENTER YOUR API'; - ``` -5. Change git remote url to avoid accidental pushes to base project +2. Set environment variables + +3. Run docker compose ```sh - git remote set-url origin RuVl/MatMex-Week - git remote -v # confirm the changes + docker compose up ```

    (back to top)

    - - -## Usage - -Use this space to show useful examples of how a project can be used. Additional screenshots, code examples and demos work well in this space. You may also link to more resources. - -_For more examples, please refer to the [Documentation](https://example.com)_ - -

    (back to top)

    - - - - -## Roadmap - -- [ ] Feature 1 -- [ ] Feature 2 -- [ ] Feature 3 - - [ ] Nested Feature - -See the [open issues](https://github.com/RuVl/MatMex-Week/issues) for a full list of proposed features (and known issues). - -

    (back to top)

    - - - ## Contributing -Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. - -If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". -Don't forget to give the project a star! Thanks again! - 1. Fork the Project 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 4. Push to the Branch (`git push origin feature/AmazingFeature`) -5. Open a Pull Request +5. Open a Pull Request to branch dev

    (back to top)

    @@ -191,7 +144,7 @@ Don't forget to give the project a star! Thanks again! ## License -Distributed under the project_license. See `LICENSE.txt` for more information. +Distributed under the MIT. See `LICENSE` for more information.

    (back to top)

    @@ -200,20 +153,8 @@ Distributed under the project_license. See `LICENSE.txt` for more information. ## Contact -Your Name - [@twitter_handle](https://twitter.com/twitter_handle) - email@email_client.com - Project Link: [https://github.com/RuVl/MatMex-Week](https://github.com/RuVl/MatMex-Week) - -

    (back to top)

    - - - - -## Acknowledgments - -* []() -* []() -* []() +Docker Hub: ...?

    (back to top)

    @@ -242,4 +183,4 @@ Project Link: [https://github.com/RuVl/MatMex-Week](https://github.com/RuVl/MatM [Postgres.org]: https://img.shields.io/badge/postgres-%23316192.svg?style=for-the-badge&logo=postgresql&logoColor=white [Postgres-url]: https://postgresql.org [Redis.io]: https://img.shields.io/badge/redis-%23DD0031.svg?style=for-the-badge&logo=redis&logoColor=white -[Redic-url]: https://redis.io +[Redis-url]: https://redis.io From f308a6fb99e9873415acc18f43ff72406a5d0dc6 Mon Sep 17 00:00:00 2001 From: Dafie Date: Sat, 5 Apr 2025 16:07:44 +0300 Subject: [PATCH 04/29] Add Trello link and DB scheme to README --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8224fca..cf3cc70 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,8 @@ Данный бот предназначен для недели Матмеха. В его функции входят регистрация новых участников недели, начисление валюты за участие в событиях, магазин мерча, администрирование пользователей. Сам бот обернут в докер и сопровождается postgres и redis. - +Trello: [https://trello.com/b/rDOuwQsR/matmex-week](https://trello.com/b/rDOuwQsR/matmex-week) +Схема базы данных: [https://dbdiagram.io/d/67f003894f7afba184640672](https://dbdiagram.io/d/67f003894f7afba184640672)

    (back to top)

    @@ -154,6 +155,7 @@ Distributed under the MIT. See `LICENSE` for more information. ## Contact Project Link: [https://github.com/RuVl/MatMex-Week](https://github.com/RuVl/MatMex-Week) + Docker Hub: ...?

    (back to top)

    From f33235a462bac55900fdbb5ba5de09175c662c92 Mon Sep 17 00:00:00 2001 From: Dafie Date: Sat, 5 Apr 2025 16:13:04 +0300 Subject: [PATCH 05/29] modified: README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cf3cc70..897e57b 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ How to launch bot ### Prerequisites -* docker [https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository](Instruction for Ubuntu) +* docker [https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository](Instruction_for_Ubuntu) ### Installation From a00fdd825a81e2d794cd66aacf88b0c1010d9a3f Mon Sep 17 00:00:00 2001 From: Dafie Date: Sat, 5 Apr 2025 16:23:39 +0300 Subject: [PATCH 06/29] fixed Dockerfile --- telegram_bot/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telegram_bot/Dockerfile b/telegram_bot/Dockerfile index cf5200f..1f52250 100644 --- a/telegram_bot/Dockerfile +++ b/telegram_bot/Dockerfile @@ -4,7 +4,7 @@ WORKDIR /app COPY requirements.txt . -RUN pip3 -r requirements.txt +RUN pip3 install -r requirements.txt COPY run.py . From 7748992867dd88a0d30692d06e6743f2956e09d5 Mon Sep 17 00:00:00 2001 From: Ilia Dudkin Date: Sun, 6 Apr 2025 15:34:04 +0300 Subject: [PATCH 07/29] =?UTF-8?q?feats:=20=D0=B1=D1=8B=D0=BB=D0=B8=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D1=8B=D0=B5=20=D1=81=D0=BE=D1=81=D1=82=D0=BE=D1=8F?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=B2=20=D0=BC=D0=B0=D1=88=D0=B8=D0=BD?= =?UTF-8?q?=D0=B5=20=D1=81=D0=BE=D1=81=D1=82=D0=BE=D1=8F=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.gitignore | 3 +++ .idea/MatMex-Week.iml | 8 ++++++++ .idea/inspectionProfiles/profiles_settings.xml | 6 ++++++ .idea/misc.xml | 7 +++++++ .idea/modules.xml | 8 ++++++++ .idea/vcs.xml | 6 ++++++ telegram_bot/state_machines/__init__.py | 7 +++++++ telegram_bot/state_machines/states_accrual_of_points.py | 5 +++++ telegram_bot/state_machines/states_event.py | 4 ++++ telegram_bot/state_machines/states_help.py | 5 +++++ telegram_bot/state_machines/states_moderation.py | 6 ++++++ telegram_bot/state_machines/states_promocode.py | 4 ++++ telegram_bot/state_machines/states_purchases.py | 7 +++++++ telegram_bot/state_machines/states_registration.py | 5 +++++ 14 files changed, 81 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/MatMex-Week.iml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 telegram_bot/state_machines/__init__.py create mode 100644 telegram_bot/state_machines/states_accrual_of_points.py create mode 100644 telegram_bot/state_machines/states_event.py create mode 100644 telegram_bot/state_machines/states_help.py create mode 100644 telegram_bot/state_machines/states_moderation.py create mode 100644 telegram_bot/state_machines/states_promocode.py create mode 100644 telegram_bot/state_machines/states_purchases.py create mode 100644 telegram_bot/state_machines/states_registration.py diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/MatMex-Week.iml b/.idea/MatMex-Week.iml new file mode 100644 index 0000000..d0876a7 --- /dev/null +++ b/.idea/MatMex-Week.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..a6218fe --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..e879cbe --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/telegram_bot/state_machines/__init__.py b/telegram_bot/state_machines/__init__.py new file mode 100644 index 0000000..ffd0f41 --- /dev/null +++ b/telegram_bot/state_machines/__init__.py @@ -0,0 +1,7 @@ +from .states_help import States_help +from .states_accrual_of_points import States_accrual_of_points +from .states_event import States_event +from .states_promocode import States_promocode +from .states_purchases import States_purchases +from .states_moderation import States_moderation +from .states_registration import States_registration \ No newline at end of file diff --git a/telegram_bot/state_machines/states_accrual_of_points.py b/telegram_bot/state_machines/states_accrual_of_points.py new file mode 100644 index 0000000..adc28e3 --- /dev/null +++ b/telegram_bot/state_machines/states_accrual_of_points.py @@ -0,0 +1,5 @@ +from aiogram.fsm.state import StatesGroup, State + +class States_accrual_of_points(StatesGroup): + event_waiting = State() + id_waiting = State() diff --git a/telegram_bot/state_machines/states_event.py b/telegram_bot/state_machines/states_event.py new file mode 100644 index 0000000..41939ec --- /dev/null +++ b/telegram_bot/state_machines/states_event.py @@ -0,0 +1,4 @@ +from aiogram.fsm.state import StatesGroup, State + +class States_event(StatesGroup): + enter_event_params = State() diff --git a/telegram_bot/state_machines/states_help.py b/telegram_bot/state_machines/states_help.py new file mode 100644 index 0000000..cf0fe9c --- /dev/null +++ b/telegram_bot/state_machines/states_help.py @@ -0,0 +1,5 @@ +from aiogram.fsm.state import StatesGroup, State + +class States_help(StatesGroup): + message_or_cancel = State() + diff --git a/telegram_bot/state_machines/states_moderation.py b/telegram_bot/state_machines/states_moderation.py new file mode 100644 index 0000000..8b08c32 --- /dev/null +++ b/telegram_bot/state_machines/states_moderation.py @@ -0,0 +1,6 @@ +from aiogram.fsm.state import StatesGroup, State + +class States_moderation(StatesGroup): + id_waiting = State() + rights_type_input = State() + diff --git a/telegram_bot/state_machines/states_promocode.py b/telegram_bot/state_machines/states_promocode.py new file mode 100644 index 0000000..ce89688 --- /dev/null +++ b/telegram_bot/state_machines/states_promocode.py @@ -0,0 +1,4 @@ +from aiogram.fsm.state import StatesGroup, State + +class States_promocode(StatesGroup): + enter_promocode = State() diff --git a/telegram_bot/state_machines/states_purchases.py b/telegram_bot/state_machines/states_purchases.py new file mode 100644 index 0000000..c79986f --- /dev/null +++ b/telegram_bot/state_machines/states_purchases.py @@ -0,0 +1,7 @@ +from aiogram.fsm.state import StatesGroup, State + +class States_purchases(StatesGroup): + choose_category = State() + choose_size = State() + choose_product = State() + confirm_purchase = State() diff --git a/telegram_bot/state_machines/states_registration.py b/telegram_bot/state_machines/states_registration.py new file mode 100644 index 0000000..7988c06 --- /dev/null +++ b/telegram_bot/state_machines/states_registration.py @@ -0,0 +1,5 @@ +from aiogram.fsm.state import StatesGroup, State + +class States_registration(StatesGroup): + name_waiting = State() + check_member = State() From a201bca201ad3c52ce36caf86448fe0c4c4b20e5 Mon Sep 17 00:00:00 2001 From: dim8art Date: Sun, 6 Apr 2025 16:02:41 +0300 Subject: [PATCH 08/29] deleted .idea --- .idea/.gitignore | 3 --- .idea/MatMex-Week.iml | 8 -------- .idea/inspectionProfiles/profiles_settings.xml | 6 ------ .idea/misc.xml | 7 ------- .idea/modules.xml | 8 -------- .idea/vcs.xml | 6 ------ 6 files changed, 38 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/MatMex-Week.iml delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/MatMex-Week.iml b/.idea/MatMex-Week.iml deleted file mode 100644 index d0876a7..0000000 --- a/.idea/MatMex-Week.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 105ce2d..0000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index a6218fe..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index e879cbe..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 4c4eb6f14237ad313e54019b8ef16552a1272916 Mon Sep 17 00:00:00 2001 From: Ilia Dudkin Date: Sun, 6 Apr 2025 16:15:10 +0300 Subject: [PATCH 09/29] =?UTF-8?q?feats:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20gitignore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | Bin 0 -> 67 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..f27d8388476d4314c0132b101cb8544c3ec717fe GIT binary patch literal 67 zcmY$;%S=g4)aT_=(9+Ay&Ee(JW5{GkVMt|2WYA~e Date: Sun, 6 Apr 2025 16:52:07 +0300 Subject: [PATCH 10/29] =?UTF-8?q?feats:=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B8?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=BE=D0=B2=D0=B0=D0=BD=D0=B0=20finite=20sta?= =?UTF-8?q?te=20machine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/workspace.xml | 121 ++++++++++++++++++ .../states_accrual_of_points.py | 6 +- telegram_bot/state_machines/states_event.py | 4 +- telegram_bot/state_machines/states_help.py | 4 +- .../state_machines/states_moderation.py | 6 +- .../state_machines/states_promocode.py | 4 +- .../state_machines/states_purchases.py | 10 +- .../state_machines/states_registration.py | 6 +- 8 files changed, 141 insertions(+), 20 deletions(-) create mode 100644 .idea/workspace.xml diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..310c1c1 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + "associatedIndex": 4 +} + + + + + + + + + + + + + + + + + + 1743937875669 + + + + + + + + + + + \ No newline at end of file diff --git a/telegram_bot/state_machines/states_accrual_of_points.py b/telegram_bot/state_machines/states_accrual_of_points.py index adc28e3..28297c3 100644 --- a/telegram_bot/state_machines/states_accrual_of_points.py +++ b/telegram_bot/state_machines/states_accrual_of_points.py @@ -1,5 +1,5 @@ from aiogram.fsm.state import StatesGroup, State -class States_accrual_of_points(StatesGroup): - event_waiting = State() - id_waiting = State() +class AccrualOfPointsActions(StatesGroup): + EVENT_WAITING = State() + ID_WAITING = State() diff --git a/telegram_bot/state_machines/states_event.py b/telegram_bot/state_machines/states_event.py index 41939ec..99eceee 100644 --- a/telegram_bot/state_machines/states_event.py +++ b/telegram_bot/state_machines/states_event.py @@ -1,4 +1,4 @@ from aiogram.fsm.state import StatesGroup, State -class States_event(StatesGroup): - enter_event_params = State() +class EventActions(StatesGroup): + ENTER_EVENT_PARAMS = State() diff --git a/telegram_bot/state_machines/states_help.py b/telegram_bot/state_machines/states_help.py index cf0fe9c..1101208 100644 --- a/telegram_bot/state_machines/states_help.py +++ b/telegram_bot/state_machines/states_help.py @@ -1,5 +1,5 @@ from aiogram.fsm.state import StatesGroup, State -class States_help(StatesGroup): - message_or_cancel = State() +class HelpActions(StatesGroup): + MESSAGE_OR_CANCEL = State() diff --git a/telegram_bot/state_machines/states_moderation.py b/telegram_bot/state_machines/states_moderation.py index 8b08c32..eb5a23d 100644 --- a/telegram_bot/state_machines/states_moderation.py +++ b/telegram_bot/state_machines/states_moderation.py @@ -1,6 +1,6 @@ from aiogram.fsm.state import StatesGroup, State -class States_moderation(StatesGroup): - id_waiting = State() - rights_type_input = State() +class ModerationActions(StatesGroup): + ID_WAITING = State() + RIGHTS_TYPE_INPUT = State() diff --git a/telegram_bot/state_machines/states_promocode.py b/telegram_bot/state_machines/states_promocode.py index ce89688..d0c0273 100644 --- a/telegram_bot/state_machines/states_promocode.py +++ b/telegram_bot/state_machines/states_promocode.py @@ -1,4 +1,4 @@ from aiogram.fsm.state import StatesGroup, State -class States_promocode(StatesGroup): - enter_promocode = State() +class PromocodeActions(StatesGroup): + ENTER_PROMOCODE = State() diff --git a/telegram_bot/state_machines/states_purchases.py b/telegram_bot/state_machines/states_purchases.py index c79986f..7cf606d 100644 --- a/telegram_bot/state_machines/states_purchases.py +++ b/telegram_bot/state_machines/states_purchases.py @@ -1,7 +1,7 @@ from aiogram.fsm.state import StatesGroup, State -class States_purchases(StatesGroup): - choose_category = State() - choose_size = State() - choose_product = State() - confirm_purchase = State() +class PurchasesActions(StatesGroup): + CHOOSE_CATEGORY = State() + CHOOSE_SIZE = State() + CHOOSE_PRODUCT = State() + CONFIRM_PURCHASE = State() diff --git a/telegram_bot/state_machines/states_registration.py b/telegram_bot/state_machines/states_registration.py index 7988c06..6cdbd6c 100644 --- a/telegram_bot/state_machines/states_registration.py +++ b/telegram_bot/state_machines/states_registration.py @@ -1,5 +1,5 @@ from aiogram.fsm.state import StatesGroup, State -class States_registration(StatesGroup): - name_waiting = State() - check_member = State() +class RegistrationsActions(StatesGroup): + NAME_WAITING = State() + CHECK_MEMBER = State() From b01c7a62a71bded5851d2ee34f9d2ffe8c696d62 Mon Sep 17 00:00:00 2001 From: Ilia Dudkin Date: Sun, 6 Apr 2025 16:55:36 +0300 Subject: [PATCH 11/29] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20gitignore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | Bin 67 -> 254 bytes .idea/workspace.xml | 4 +--- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index f27d8388476d4314c0132b101cb8544c3ec717fe..e406b670bccc511ac5e128b9e308c84df1c1b5a2 100644 GIT binary patch literal 254 zcmZurOA5j;5Z&h#BV7nxBxewHA$W%n(=;8JWJ((gy}gr&bmL~2_v5_|9=H2>g^_6p z%c?XE!5O7GnD;Vq24e3Byz^5~eQ&It_XezOt}CY*DGV^mU8|$li6!nRq!)Tf7#CNN za}y6_HmR$$Q71|L6RgFI#acb@b - - - +