Модели для базы данных
This commit is contained in:
362
database/models.py
Normal file
362
database/models.py
Normal file
@@ -0,0 +1,362 @@
|
||||
"""
|
||||
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",
|
||||
"Report",
|
||||
)
|
||||
|
||||
|
||||
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"<BanWord(word='{self.word}', type={self.type})>"
|
||||
|
||||
|
||||
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"<TempBanWord(word='{self.word}', expires={self.expires_at})>"
|
||||
|
||||
|
||||
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"<WhitelistWord(word='{self.word}')>"
|
||||
|
||||
|
||||
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"<Admin(user_id={self.user_id})>"
|
||||
|
||||
|
||||
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"<Setting(key='{self.key}', value='{self.value}')>"
|
||||
|
||||
|
||||
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"<SpamStat(user_id={self.user_id}, word='{self.matched_word}')>"
|
||||
|
||||
|
||||
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"<AutoComment(channel_id={self.channel_id}, enabled={self.is_enabled})>"
|
||||
|
||||
|
||||
class Report(Base):
|
||||
"""
|
||||
Модель для хранения статистики репортов.
|
||||
|
||||
Attributes:
|
||||
id: Уникальный ID репорта
|
||||
report_id: Строковый ID репорта (timestamp)
|
||||
reporter_id: ID пользователя, который пожаловался
|
||||
reporter_username: Username жалобщика
|
||||
reported_user_id: ID пользователя, на которого пожаловались
|
||||
reported_username: Username нарушителя
|
||||
chat_id: ID чата, где произошло нарушение
|
||||
chat_title: Название чата
|
||||
message_id: ID сообщения-нарушения
|
||||
message_thread_id: ID топика (если есть)
|
||||
message_text: Текст сообщения (до 500 символов)
|
||||
reason: Причина жалобы
|
||||
status: Статус репорта (pending, closed, banned, deleted)
|
||||
processed_by: ID админа, который обработал
|
||||
created_at: Дата создания репорта
|
||||
processed_at: Дата обработки
|
||||
"""
|
||||
__tablename__ = "reports"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
report_id: Mapped[str] = mapped_column(String(50), nullable=False, unique=True, index=True)
|
||||
|
||||
# Информация о жалобщике
|
||||
reporter_id: Mapped[int] = mapped_column(BigInteger, nullable=False, index=True)
|
||||
reporter_username: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
||||
|
||||
# Информация о нарушителе
|
||||
reported_user_id: Mapped[int] = mapped_column(BigInteger, nullable=False, index=True)
|
||||
reported_username: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
||||
|
||||
# Информация о чате и сообщении
|
||||
chat_id: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
||||
chat_title: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
||||
message_id: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
message_thread_id: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
|
||||
message_text: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
|
||||
# Причина и статус
|
||||
reason: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
status: Mapped[str] = mapped_column(
|
||||
String(20),
|
||||
default="pending",
|
||||
nullable=False,
|
||||
index=True
|
||||
) # pending, closed, banned, deleted
|
||||
|
||||
# Обработка
|
||||
processed_by: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime,
|
||||
default=lambda: datetime.now(timezone.utc),
|
||||
nullable=False,
|
||||
index=True
|
||||
)
|
||||
processed_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Report(id={self.report_id}, reporter={self.reporter_id}, reported={self.reported_user_id})>"
|
||||
Reference in New Issue
Block a user