Модели для базы данных
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