diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f99d40 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +# Python +__pycache__/ +*.pyc +*.pyo +*.pyd +.Python +.env +venv/ +.venv/ diff --git a/create_database.py b/create_database.py new file mode 100644 index 0000000..30ddf29 --- /dev/null +++ b/create_database.py @@ -0,0 +1,93 @@ +import os +import sys +from sqlalchemy import create_engine, inspect +from sqlalchemy.orm import declarative_base, Session +from sqlalchemy import Column, String, DateTime, Boolean, Integer, Text +import uuid +from datetime import datetime + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +DB_PATH = os.path.join(BASE_DIR, 'data', 'bot.db') +DATABASE_URL = f"sqlite:///{DB_PATH}" + +os.makedirs(os.path.dirname(DB_PATH), exist_ok=True) + +if os.path.exists(DB_PATH): + try: + temp_engine = create_engine(DATABASE_URL) + inspector = inspect(temp_engine) + tables = inspector.get_table_names() + if tables: + sys.exit(0) + except: + pass + + choice = input("Перезаписать БД? (y/N): ") + if choice.lower() != 'y': + sys.exit(0) + +engine = create_engine(DATABASE_URL, echo=False) +Base = declarative_base() + +class UserModel(Base): + __tablename__ = "users" + + user_id = Column("user_id", String(36), primary_key=True, default=lambda: str(uuid.uuid4())) + telegram_id = Column("telegram_id", String(100), nullable=False, unique=True) + created_at = Column("created_at", DateTime, default=datetime.utcnow, nullable=False) + role = Column("role", String(20), default="user", nullable=False) + is_premium = Column(Boolean, default=False, nullable=False) + premium_until = Column(DateTime, nullable=True) + questions_used = Column(Integer, default=0, nullable=False) + username = Column(String(100), nullable=True) + first_name = Column(String(100), nullable=True) + last_name = Column(String(100), nullable=True) + +class PaymentModel(Base): + __tablename__ = "payments" + + id = Column(Integer, primary_key=True, autoincrement=True) + payment_id = Column(String(36), default=lambda: str(uuid.uuid4()), nullable=False, unique=True) + user_id = Column(Integer, nullable=False) + amount = Column(String(20), nullable=False) + currency = Column(String(3), default="RUB", nullable=False) + status = Column(String(20), default="pending", nullable=False) + created_at = Column(DateTime, default=datetime.utcnow, nullable=False) + yookassa_payment_id = Column(String(100), unique=True, nullable=True) + description = Column(Text, nullable=True) + +try: + Base.metadata.create_all(bind=engine) + + session = Session(bind=engine) + + existing = session.query(UserModel).filter_by(telegram_id="123456789").first() + if not existing: + test_user = UserModel( + telegram_id="123456789", + username="test_user", + first_name="Test", + last_name="User", + is_premium=True + ) + session.add(test_user) + + existing_payment = session.query(PaymentModel).filter_by(yookassa_payment_id="test_yoo_001").first() + if not existing_payment: + test_payment = PaymentModel( + user_id=123456789, + amount="500.00", + status="succeeded", + description="Test payment", + yookassa_payment_id="test_yoo_001" + ) + session.add(test_payment) + + session.commit() + session.close() + +except Exception as e: + print(f"Ошибка: {e}") + import traceback + traceback.print_exc() + diff --git a/create_tables.py b/create_tables.py new file mode 100755 index 0000000..0bca2f4 --- /dev/null +++ b/create_tables.py @@ -0,0 +1,31 @@ +import sys +import os + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +try: + from tg_bot.infrastructure.database.database import engine, Base + from tg_bot.infrastructure.database import models + + print("СОЗДАНИЕ ТАБЛИЦ БАЗЫ ДАННЫХ") + Base.metadata.create_all(bind=engine) + + print("Таблицы успешно созданы!") + print(" • users") + print(" • payments") + print() + print(f"База данных: {engine.url}") + + db_path = "data/bot.db" + if os.path.exists(db_path): + size = os.path.getsize(db_path) + print(f"Размер файла: {size} байт") + else: + print("Файл БД не найден, но таблицы созданы") + +except Exception as e: + print(f"Ошибка: {e}") + import traceback + traceback.print_exc() + +print("=" * 50) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0c4aad2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,9 @@ +pydantic>=2.5.0 +pydantic-settings>=2.1.0 +python-dotenv>=1.0.0 +aiogram>=3.10.0 +sqlalchemy>=2.0.0 +yookassa>=2.4.0 +fastapi>=0.104.0 +uvicorn>=0.24.0 +python-multipart>=0.0.6 \ No newline at end of file diff --git a/tg_bot/config/__init__.py b/tg_bot/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tg_bot/config/constants.py b/tg_bot/config/constants.py new file mode 100644 index 0000000..e69de29 diff --git a/tg_bot/config/settings.py b/tg_bot/config/settings.py new file mode 100644 index 0000000..9bacdbf --- /dev/null +++ b/tg_bot/config/settings.py @@ -0,0 +1,41 @@ +import os +from typing import List, Optional +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class Settings(BaseSettings): + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + case_sensitive=True, + extra="allow" + ) + + APP_NAME: str = "VibeLawyerBot" + VERSION: str = "0.1.0" + DEBUG: bool = True + TELEGRAM_BOT_TOKEN: str = "" + FREE_QUESTIONS_LIMIT: int = 5 + PAYMENT_AMOUNT: float = 500.0 + DATABASE_URL: str = "sqlite:///data/bot.db" + LOG_LEVEL: str = "INFO" + LOG_FILE: str = "logs/bot.log" + + YOOKASSA_SHOP_ID: str = "1230200" + YOOKASSA_SECRET_KEY: str = "test_GVoixmlp0FqohXcyFzFHbRlAUoA3B1I2aMtAkAE_ubw" + YOOKASSA_RETURN_URL: str = "https://t.me/vibelawyer_bot" + YOOKASSA_WEBHOOK_SECRET: Optional[str] = None + + ADMIN_IDS_STR: str = "" + + @property + def ADMIN_IDS(self) -> List[int]: + if not self.ADMIN_IDS_STR: + return [] + try: + return [int(x.strip()) for x in self.ADMIN_IDS_STR.split(",")] + except: + return [] + + +settings = Settings() \ No newline at end of file diff --git a/tg_bot/infrastructure/database/__init__.py b/tg_bot/infrastructure/database/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tg_bot/infrastructure/database/database.py b/tg_bot/infrastructure/database/database.py new file mode 100644 index 0000000..8044e09 --- /dev/null +++ b/tg_bot/infrastructure/database/database.py @@ -0,0 +1,15 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from tg_bot.config.settings import settings + +engine = create_engine( + settings.DATABASE_URL, + echo=settings.DEBUG +) + +SessionLocal = sessionmaker(bind=engine) + +def create_tables(): + from .models import Base + Base.metadata.create_all(bind=engine) + print(f"Таблицы созданы: {settings.DATABASE_URL}") \ No newline at end of file diff --git a/tg_bot/infrastructure/database/models.py b/tg_bot/infrastructure/database/models.py new file mode 100644 index 0000000..f681729 --- /dev/null +++ b/tg_bot/infrastructure/database/models.py @@ -0,0 +1,39 @@ +import uuid +from datetime import datetime +from sqlalchemy import Column, String, DateTime, Boolean, Integer, Text +from sqlalchemy.ext.declarative import declarative_base + +Base = declarative_base() + + +class UserModel(Base): + __tablename__ = "users" + user_id = Column("user_id", String(36), primary_key=True, default=lambda: str(uuid.uuid4())) + telegram_id = Column("telegram_id", String(100), nullable=False, unique=True) + created_at = Column("created_at", DateTime, default=datetime.utcnow, nullable=False) + role = Column("role", String(20), default="user", nullable=False) + + is_premium = Column(Boolean, default=False, nullable=False) + premium_until = Column(DateTime, nullable=True) + questions_used = Column(Integer, default=0, nullable=False) + + username = Column(String(100), nullable=True) + first_name = Column(String(100), nullable=True) + last_name = Column(String(100), nullable=True) + + +class PaymentModel(Base): + __tablename__ = "payments" + + id = Column(Integer, primary_key=True, autoincrement=True) + payment_id = Column(String(36), default=lambda: str(uuid.uuid4()), nullable=False, unique=True) + user_id = Column(Integer, nullable=False) + amount = Column(String(20), nullable=False) + currency = Column(String(3), default="RUB", nullable=False) + status = Column(String(20), default="pending", nullable=False) + created_at = Column(DateTime, default=datetime.utcnow, nullable=False) + yookassa_payment_id = Column(String(100), unique=True, nullable=True) + description = Column(Text, nullable=True) + + def __repr__(self): + return f"" \ No newline at end of file