This commit is contained in:
2026-02-20 03:12:47 +07:00
parent 5d350d0885
commit 5aca4e8438
23 changed files with 2291 additions and 1330 deletions

View File

@@ -8,7 +8,7 @@ from datetime import datetime, timezone
from middleware.loggers import logger
from .database import Database, get_db
from .repository import BanWordsRepository
from .models import BanWordType, SpamStat, SpamLog, TempBanWord
from .models import BanWordType, SpamStat, SpamLog, TempBanWord, AutoComment
from sqlalchemy import select, delete, func, desc
@@ -43,6 +43,7 @@ class BanWordsManager:
async def init(self) -> None:
"""Инициализирует базу данных и загружает кэш"""
await self.db.init()
await self.init_default_bot_settings() # ← добавлено
await self.refresh_cache()
logger.info("BanWordsManager инициализирован", log_type="DATABASE")
@@ -335,7 +336,6 @@ class BanWordsManager:
now = datetime.now().timestamp()
if now >= silence_until:
# Время истекло - удаляем настройку
await self.disable_silence_mode()
return False
@@ -381,7 +381,6 @@ class BanWordsManager:
now = datetime.now().timestamp()
if now >= conflict_until:
# Время истекло
await self.disable_conflict_mode()
return False
@@ -433,7 +432,6 @@ class BanWordsManager:
"""Получает общую статистику"""
db_stats = await self.repo.get_stats()
# Добавляем информацию о кэше
cache_info = {
'cache_active': self._cache_banwords is not None,
'cache_updated_at': self._cache_updated_at.isoformat() if self._cache_updated_at else None
@@ -480,7 +478,6 @@ class BanWordsManager:
"""
async with self.db.get_session() as session:
try:
# Группируем по matched_word и считаем количество
query = select(
SpamLog.matched_word,
SpamLog.match_type,
@@ -497,7 +494,6 @@ class BanWordsManager:
result = await session.execute(query)
rows = result.all()
# Форматируем результат
top_words = []
for row in rows:
top_words.append({
@@ -531,7 +527,6 @@ class BanWordsManager:
try:
now = datetime.now(timezone.utc)
# Ищем истёкшие временные слова
query = select(TempBanWord).where(
TempBanWord.expires_at < now
)
@@ -541,23 +536,18 @@ class BanWordsManager:
if not expired_words:
return 0
# Собираем информацию для логирования
expired_info = []
for word in expired_words:
expired_info.append({
'word': word.word,
'type': word.word_type.value,
'type': word.type.value, # ← ИСПРАВЛЕНО: было word.word_type.value
'expires_at': word.expires_at
})
await session.delete(word)
# Сохраняем изменения
await session.commit()
# Обновляем кеш
await self.refresh_cache()
# Логируем подробности
logger.info(
f"Удалено {len(expired_words)} истёкших временных банвордов",
log_type="DATABASE"
@@ -608,7 +598,6 @@ class BanWordsManager:
"""
async with self.db.get_session() as session:
try:
# Удаляем все записи
await session.execute(delete(SpamLog))
await session.commit()
@@ -629,32 +618,30 @@ class BanWordsManager:
"""
Получает настройки автокомментариев для канала.
Args:
channel_id: ID канала
Returns:
dict: Настройки или значения по умолчанию
ВАЖНО: возвращает сохранённые поля даже когда is_enabled=False,
чтобы UI/preview показывали реальную конфигурацию.
"""
from configs import settings
auto_comment = await self.repo.get_auto_comment(channel_id)
if auto_comment and auto_comment.is_enabled:
return {
'text': auto_comment.text,
'button_text': auto_comment.button_text,
'button_url': auto_comment.button_url,
'photo_url': auto_comment.photo_url,
'is_enabled': auto_comment.is_enabled,
}
# Возвращаем настройки по умолчанию из .env
return {
defaults = {
'text': settings.AUTO_COMMENT_TEXT,
'button_text': settings.AUTO_COMMENT_BUTTON_TEXT,
'button_url': settings.AUTO_COMMENT_BUTTON_URL,
'photo_url': settings.AUTO_COMMENT_PHOTO_URL,
'is_enabled': False, # По умолчанию выключено
'is_enabled': False,
}
if not auto_comment:
return defaults
return {
'text': auto_comment.text if auto_comment.text is not None else defaults['text'],
'button_text': auto_comment.button_text if auto_comment.button_text is not None else defaults['button_text'],
'button_url': auto_comment.button_url if auto_comment.button_url is not None else defaults['button_url'],
'photo_url': auto_comment.photo_url if auto_comment.photo_url is not None else defaults['photo_url'],
'is_enabled': bool(auto_comment.is_enabled),
}
async def save_auto_comment_settings(
@@ -715,6 +702,136 @@ class BanWordsManager:
channel_id, 'photo_url', photo_url, updated_by
)
async def log_report(
self,
report_id: str,
reporter_id: int,
reporter_username: Optional[str],
reported_user_id: int,
reported_username: Optional[str],
chat_id: int,
chat_title: Optional[str],
message_id: int,
message_thread_id: Optional[int],
message_text: Optional[str],
reason: str
) -> bool:
"""Логирует репорт в БД"""
return await self.repo.log_report(
report_id=report_id,
reporter_id=reporter_id,
reporter_username=reporter_username,
reported_user_id=reported_user_id,
reported_username=reported_username,
chat_id=chat_id,
chat_title=chat_title,
message_id=message_id,
message_thread_id=message_thread_id,
message_text=message_text,
reason=reason
)
# ==================== ✅ КАНАЛЫ АВТОКОММЕНТАРИЕВ ====================
async def add_auto_comment_channel(self, channel_id: int, added_by: int) -> bool:
"""✅ Добавляет новый канал в БД"""
async with self.db.get_session() as session:
try:
new_channel = AutoComment(
channel_id=channel_id,
text="",
button_text="",
button_url="",
photo_url="",
is_enabled=False,
updated_by=added_by,
)
session.add(new_channel)
await session.commit()
await session.refresh(new_channel)
logger.info(f"✅ Канал добавлен: {channel_id}", log_type="CHANNEL")
return True
except Exception as e:
await session.rollback()
logger.error(f"Ошибка добавления канала {channel_id}: {e}", log_type="CHANNEL")
return False
async def get_auto_comment_channels(self) -> List[int]:
"""✅ Возвращает все channel_id из БД"""
async with self.db.get_session() as session:
result = await session.execute(select(AutoComment.channel_id).distinct())
return [row[0] for row in result.fetchall()]
async def delete_auto_comment(self, channel_id: int) -> bool:
"""✅ Удаляет настройки канала"""
async with self.db.get_session() as session:
try:
result = await session.execute(
delete(AutoComment).where(AutoComment.channel_id == channel_id)
)
if result.rowcount > 0:
await session.commit()
logger.info(f"✅ Канал удален: {channel_id}", log_type="CHANNEL")
return True
await session.rollback()
return False
except Exception as e:
await session.rollback()
logger.error(f"Ошибка удаления канала {channel_id}: {e}", log_type="CHANNEL")
return False
# ==================== BOT SETTINGS (замена .env) ====================
async def get_bot_settings(self) -> dict:
"""Получает все настройки бота из БД"""
settings = {
'admin_chat_id': await self.repo.get_setting("admin_chat_id"),
'admin_thread_id': await self.repo.get_setting("admin_thread_id"),
'report_chat_id': await self.repo.get_setting("report_chat_id"),
'report_thread_id': await self.repo.get_setting("report_thread_id"),
}
return {k: v for k, v in settings.items() if v is not None}
async def set_bot_setting(self, key: str, value: Optional[str]) -> bool:
"""
Сохраняет настройку бота в БД
Args:
key: admin_chat_id, admin_thread_id, report_chat_id, report_thread_id
value: str или None/null
Returns:
bool: True если сохранено
"""
if value is None:
return await self.repo.delete_setting(key)
else:
return await self.repo.set_setting(key, value)
async def get_bot_setting(self, key: str) -> Optional[str]:
"""Получает ОДНУ настройку бота"""
settings = await self.get_bot_settings()
return settings.get(key)
async def init_default_bot_settings(self) -> None:
"""Инициализирует настройки по умолчанию из .env"""
try:
from configs import settings
defaults = {
"admin_chat_id": getattr(settings, 'ADMIN_CHAT_ID', None),
"admin_thread_id": str(getattr(settings, 'ADMIN_THREAD_ID', None)) if getattr(settings, 'ADMIN_THREAD_ID', None) else None,
"report_chat_id": getattr(settings, 'REPORT_CHAT_ID', None),
"report_thread_id": str(getattr(settings, 'REPORT_THREAD_ID', None)) if getattr(settings, 'REPORT_THREAD_ID', None) else None,
}
for key, value in defaults.items():
if value: # Не null
await self.set_bot_setting(key, str(value))
logger.info("✅ Настройки бота инициализированы из .env", log_type="SETTINGS")
except Exception as e:
logger.warning(f"Не удалось инициализировать настройки из .env: {e}", log_type="SETTINGS")
# Глобальный экземпляр менеджера
_manager_instance: Optional[BanWordsManager] = None