""" SQLAlchemy модели для банвордов. """ from datetime import datetime, timezone from enum import Enum as PyEnum from typing import Optional from sqlalchemy import String, Integer, DateTime, Text, Enum, BigInteger from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column __all__ = ( "Base", "BanWordType", "SpamMode", "BanWord", "TempBanWord", "WhitelistWord", "Admin", "Setting", "SpamStat", "SpamLog", "AutoComment", ) class Base(DeclarativeBase): """Базовый класс для всех моделей""" pass class BanWordType(str, PyEnum): """Типы банвордов""" SUBSTRING = "substring" LEMMA = "lemma" PART = "part" CONFLICT_SUBSTRING = "conflict_substring" CONFLICT_LEMMA = "conflict_lemma" class SpamMode(str, PyEnum): """Режимы работы спам-фильтра""" NORMAL = "normal" SILENCE = "silence" CONFLICT = "conflict" class BanWord(Base): """ Постоянные банворды. Attributes: id: Уникальный ID word: Само слово (lowercase) type: Тип банворда added_by: Telegram ID добавившего админа added_at: Дата добавления reason: Причина добавления """ __tablename__ = "banwords" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) word: Mapped[str] = mapped_column(String(255), nullable=False, index=True) type: Mapped[BanWordType] = mapped_column( Enum(BanWordType, native_enum=False), nullable=False, index=True ) added_by: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) added_at: Mapped[datetime] = mapped_column( DateTime, default=datetime.now, nullable=False ) reason: Mapped[Optional[str]] = mapped_column(Text, nullable=True) def __repr__(self) -> str: return f"" class TempBanWord(Base): """ Временные банворды (с автоудалением). Attributes: id: Уникальный ID word: Само слово type: Тип банворда added_by: ID админа added_at: Дата добавления expires_at: Дата истечения """ __tablename__ = "temp_banwords" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) word: Mapped[str] = mapped_column(String(255), nullable=False, index=True) type: Mapped[BanWordType] = mapped_column( Enum(BanWordType, native_enum=False), nullable=False ) added_by: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) added_at: Mapped[datetime] = mapped_column( DateTime, default=datetime.now, nullable=False ) expires_at: Mapped[datetime] = mapped_column( DateTime, nullable=False, index=True ) def is_expired(self) -> bool: """Проверяет, истёк ли срок""" return datetime.now() >= self.expires_at def __repr__(self) -> str: return f"" class WhitelistWord(Base): """ Белый список (исключения из проверки). Attributes: id: Уникальный ID word: Слово-исключение added_by: ID админа added_at: Дата добавления reason: Причина (например, "ложное срабатывание") """ __tablename__ = "whitelist" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) word: Mapped[str] = mapped_column(String(255), nullable=False, unique=True, index=True) added_by: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) added_at: Mapped[datetime] = mapped_column( DateTime, default=datetime.now, nullable=False ) reason: Mapped[Optional[str]] = mapped_column(Text, nullable=True) def __repr__(self) -> str: return f"" class Admin(Base): """ Дополнительные администраторы бота. Attributes: id: Уникальный ID записи user_id: Telegram ID пользователя (уникальный) added_by: ID суперадмина, который добавил added_at: Дата добавления permissions: JSON со списком прав (для будущего) """ __tablename__ = "admins" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) user_id: Mapped[int] = mapped_column(Integer, nullable=False, unique=True, index=True) added_by: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) added_at: Mapped[datetime] = mapped_column( DateTime, default=datetime.now, nullable=False ) permissions: Mapped[Optional[str]] = mapped_column( Text, default="[]", nullable=True ) def __repr__(self) -> str: return f"" class Setting(Base): """ Настройки и состояния бота. Attributes: key: Ключ настройки (primary key) value: Значение (JSON string) updated_at: Дата обновления Examples: - silence_until: datetime ISO string - conflict_until: datetime ISO string - spam_mode: "normal"/"silence"/"conflict" """ __tablename__ = "settings" key: Mapped[str] = mapped_column(String(100), primary_key=True) value: Mapped[Optional[str]] = mapped_column(Text, nullable=True) updated_at: Mapped[datetime] = mapped_column( DateTime, default=datetime.now, onupdate=datetime.now, nullable=False ) def __repr__(self) -> str: return f"" class SpamStat(Base): """ Статистика удалённых спам-сообщений. Attributes: id: Уникальный ID user_id: Telegram ID отправителя username: Username отправителя chat_id: ID чата message_text: Текст сообщения (до 500 символов) matched_word: Слово, по которому сработал фильтр match_type: Тип проверки (substring/lemma/part) deleted_at: Дата удаления """ __tablename__ = "spam_stats" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) user_id: Mapped[int] = mapped_column(Integer, nullable=False, index=True) username: Mapped[Optional[str]] = mapped_column(String(100), nullable=True) chat_id: Mapped[int] = mapped_column(Integer, nullable=False) message_text: Mapped[str] = mapped_column(Text, nullable=False) matched_word: Mapped[str] = mapped_column(String(255), nullable=False) match_type: Mapped[str] = mapped_column(String(50), nullable=False) deleted_at: Mapped[datetime] = mapped_column( DateTime, default=datetime.now, nullable=False, index=True ) def __repr__(self) -> str: return f"" class SpamLog(Base): """Модель для логирования срабатываний спам-фильтра""" __tablename__ = "spam_logs" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) user_id: Mapped[int] = mapped_column(BigInteger, nullable=False) username: Mapped[str] = mapped_column(String(255), nullable=True) chat_id: Mapped[int] = mapped_column(BigInteger, nullable=False) message_text: Mapped[str] = mapped_column(Text, nullable=True) matched_word: Mapped[str] = mapped_column(String(255), nullable=True) # <-- Должно быть! match_type: Mapped[str] = mapped_column(String(50), nullable=True) # <-- Должно быть! timestamp: Mapped[datetime] = mapped_column( DateTime, default=lambda: datetime.now(timezone.utc) ) class AutoComment(Base): """ Настройки автокомментариев для каналов. Attributes: id: Уникальный ID channel_id: ID канала (-100...) text: Текст комментария (HTML) button_text: Текст кнопки button_url: URL кнопки photo_url: URL фото для preview is_enabled: Включены ли автокомментарии для этого канала created_at: Дата создания updated_at: Дата последнего обновления updated_by: ID админа, который последним изменил """ __tablename__ = "auto_comments" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) channel_id: Mapped[int] = mapped_column(BigInteger, nullable=False, unique=True, index=True) text: Mapped[str] = mapped_column(Text, nullable=False) button_text: Mapped[str] = mapped_column(String(100), nullable=False) button_url: Mapped[str] = mapped_column(String(500), nullable=False) photo_url: Mapped[str] = mapped_column(String(500), nullable=False) is_enabled: Mapped[bool] = mapped_column(Integer, default=1, nullable=False) created_at: Mapped[datetime] = mapped_column( DateTime, default=lambda: datetime.now(timezone.utc), nullable=False ) updated_at: Mapped[datetime] = mapped_column( DateTime, default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False ) updated_by: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) def __repr__(self) -> str: return f""