from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, DateTime, ForeignKey, JSON, Text from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, relationship from datetime import datetime import json from pathlib import Path DATABASE_URL = "sqlite:///./cs2_simulator.db" engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False}) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) username = Column(String(50), unique=True, nullable=False) hashed_password = Column(String(200), nullable=False) balance = Column(Float, default=0.0) is_admin = Column(Boolean, default=False) is_banned = Column(Boolean, default=False) created_at = Column(DateTime, default=datetime.utcnow) last_login = Column(DateTime, nullable=True) inventory_items = relationship("InventoryItem", back_populates="user", cascade="all, delete-orphan") case_openings = relationship("CaseOpening", back_populates="user", cascade="all, delete-orphan") contracts = relationship("Contract", back_populates="user", cascade="all, delete-orphan") rpu_settings = relationship("UserRPU", back_populates="user", uselist=False, cascade="all, delete-orphan") upgrades = relationship("Upgrade", back_populates="user", cascade="all, delete-orphan") upgrade_rpu = relationship("UpgradeRPU", back_populates="user", uselist=False, cascade="all, delete-orphan") class UpgradeRPU(Base): __tablename__ = "upgrade_rpu" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False, unique=True) # Множитель шанса апгрейда (1.0 = стандартный) upgrade_multiplier = Column(Float, default=1.0) # Автоматическая подкрутка auto_adjust = Column(Boolean, default=False) # Статистика total_attempts = Column(Integer, default=0) total_success = Column(Integer, default=0) total_spent_value = Column(Float, default=0.0) # Стоимость потерянных предметов created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) user = relationship("User", back_populates="upgrade_rpu") class Upgrade(Base): __tablename__ = "upgrades" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False) input_item_id = Column(Integer, nullable=False) input_item_name = Column(String(200)) input_item_image_url = Column(String(500), default="") target_item_id = Column(Integer, nullable=False) target_item_name = Column(String(200)) target_item_image_url = Column(String(500), default="") success = Column(Boolean, default=False) probability = Column(Float) # Базовый шанс rpu_adjusted_probability = Column(Float) # Шанс с учетом РПУ created_at = Column(DateTime, default=datetime.utcnow) user = relationship("User", back_populates="upgrades") class UserRPU(Base): __tablename__ = "user_rpu" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False, unique=True) # Множители шансов для каждой редкости (1.0 = стандартный шанс) consumer_multiplier = Column(Float, default=1.0) industrial_multiplier = Column(Float, default=1.0) mil_spec_multiplier = Column(Float, default=1.0) restricted_multiplier = Column(Float, default=1.0) classified_multiplier = Column(Float, default=1.0) covert_multiplier = Column(Float, default=1.0) rare_special_multiplier = Column(Float, default=1.0) # Общий множитель удачи luck_multiplier = Column(Float, default=1.0) # Автоматическая подкрутка auto_adjust = Column(Boolean, default=False) # Статистика для авто-подкрутки total_spent = Column(Float, default=0.0) # Всего потрачено total_opened = Column(Integer, default=0) # Всего открыто кейсов last_adjustment = Column(DateTime, nullable=True) # Мета-данные created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) user = relationship("User", back_populates="rpu_settings") class InventoryItem(Base): __tablename__ = "inventory_items" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False) item_id = Column(Integer, nullable=False) # ID из ALL_ITEMS market_hash_name = Column(String(200), nullable=False) rarity = Column(String(50)) wear = Column(String(50)) float_value = Column(Float, default=0.0) type = Column(String(20), default="Normal") obtained_from = Column(String(100)) # "case: название" или "contract" obtained_at = Column(DateTime, default=datetime.utcnow) is_equipped = Column(Boolean, default=False) user = relationship("User", back_populates="inventory_items") class CaseOpening(Base): __tablename__ = "case_openings" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False) case_name = Column(String(100), nullable=False) item_id = Column(Integer, nullable=False) item_name = Column(String(200), nullable=False) rarity = Column(String(50)) float_value = Column(Float) opened_at = Column(DateTime, default=datetime.utcnow) user = relationship("User", back_populates="case_openings") class Contract(Base): __tablename__ = "contracts" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False) input_item_ids = Column(Text, nullable=False) # JSON список ID из инвентаря output_item_id = Column(Integer, nullable=False) output_item_name = Column(String(200)) output_float = Column(Float) probability = Column(Float) created_at = Column(DateTime, default=datetime.utcnow) user = relationship("User", back_populates="contracts") class Achievement(Base): __tablename__ = "achievements" id = Column(Integer, primary_key=True, index=True) name = Column(String(100), unique=True, nullable=False) title = Column(String(200), nullable=False) description = Column(String(500)) icon = Column(String(50), default="🏆") category = Column(String(50), default="general") # cases, contracts, upgrade, social, general requirement_type = Column(String(50), nullable=False) # open_cases, contracts, upgrade_wins, total_spent, valuable_item, etc. requirement_value = Column(Integer, nullable=False) # how many reward_amount = Column(Float, default=0.0) # bonus rubles sort_order = Column(Integer, default=0) hidden = Column(Boolean, default=False) # секретные достижения user_achievements = relationship("UserAchievement", back_populates="achievement") class UserAchievement(Base): __tablename__ = "user_achievements" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False) achievement_id = Column(Integer, ForeignKey("achievements.id"), nullable=False) progress = Column(Integer, default=0) # текущий прогресс unlocked_at = Column(DateTime, nullable=True) created_at = Column(DateTime, default=datetime.utcnow) user = relationship("User", backref="user_achievements") achievement = relationship("Achievement", back_populates="user_achievements") class ActivityFeed(Base): __tablename__ = "activity_feed" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id"), nullable=False) username = Column(String(50), nullable=False) activity_type = Column(String(50), nullable=False) # case_open, contract, upgrade, achievement, trade, crash message = Column(String(500), nullable=False) data_json = Column(Text, default="{}") # дополнительные данные (item_name, price, etc.) created_at = Column(DateTime, default=datetime.utcnow) user = relationship("User", backref="activities") Base.metadata.create_all(bind=engine) # Миграция: добавляем колонку hidden если её нет try: from sqlalchemy import inspect inspector = inspect(engine) cols = [c["name"] for c in inspector.get_columns("achievements")] if "hidden" not in cols: from sqlalchemy import text with engine.connect() as conn: conn.execute(text("ALTER TABLE achievements ADD COLUMN hidden BOOLEAN DEFAULT 0")) conn.commit() except Exception as e: print(f"[DB] Миграция hidden: {e}") def get_db(): db = SessionLocal() try: yield db finally: db.close()