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

@@ -157,12 +157,6 @@ class BanWordsRepository:
return set()
async def get_all_banwords(self) -> dict[BanWordType, Set[str]]:
"""
Получает все банворды, сгруппированные по типам.
Returns:
dict: {BanWordType: Set[str]}
"""
result = {
BanWordType.SUBSTRING: set(),
BanWordType.LEMMA: set(),
@@ -170,19 +164,28 @@ class BanWordsRepository:
BanWordType.CONFLICT_SUBSTRING: set(),
BanWordType.CONFLICT_LEMMA: set(),
}
try:
async with self.db.get_session() as session:
banwords = await session.execute(select(BanWord))
loaded = 0
for banword in banwords.scalars():
result[banword.type].add(banword.word)
try:
word_type = (
banword.type
if isinstance(banword.type, BanWordType)
else BanWordType(banword.type.casefold())
)
if word_type in result:
result[word_type].add(banword.word)
loaded += 1
except ValueError:
logger.warning(
f"Неизвестный тип: '{banword.type}' для '{banword.word}'",
log_type="DATABASE"
)
logger.info(f"✅ Кэш загружен: {loaded} банвордов", log_type="DATABASE")
except Exception as e:
logger.error(
f"Ошибка получения всех банвордов: {e}",
log_type="DATABASE"
)
logger.error(f"❌ get_all_banwords: {e}", log_type="DATABASE")
return result
async def search_banwords(self, query: str, limit: int = 50) -> List[BanWord]:
@@ -344,8 +347,14 @@ class BanWordsRepository:
)
)
for temp_banword in temp_banwords.scalars():
if temp_banword.type in result:
result[temp_banword.type].add(temp_banword.word)
word_type = (
temp_banword.type
if isinstance(temp_banword.type, BanWordType)
else BanWordType(temp_banword.type.casefold())
)
if word_type in result:
result[word_type].add(temp_banword.word)
except Exception as e:
logger.error(
@@ -1046,3 +1055,336 @@ class BanWordsRepository:
log_type="DATABASE"
)
return False
# === REPORTS ===
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:
"""
Сохраняет репорт в БД.
Args:
report_id: Уникальный ID репорта
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: Текст сообщения
reason: Причина жалобы
Returns:
bool: True если успешно
"""
try:
from .models import Report # Импорт здесь, чтобы избежать циклических импортов
async with self.db.get_session() as session:
report = 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[:500] if message_text else None,
reason=reason
)
session.add(report)
await session.commit()
logger.info(
f"Репорт #{report_id} сохранён в БД",
log_type="DATABASE"
)
return True
except Exception as e:
logger.error(
f"Ошибка сохранения репорта: {e}",
log_type="DATABASE"
)
return False
async def update_report_status(
self,
report_id: str,
status: str,
processed_by: int
) -> bool:
"""
Обновляет статус репорта.
Args:
report_id: ID репорта
status: Новый статус (closed, banned, deleted)
processed_by: ID админа
Returns:
bool: True если успешно
"""
try:
from .models import Report
async with self.db.get_session() as session:
result = await session.execute(
select(Report).where(Report.report_id == report_id)
)
report = result.scalar_one_or_none()
if not report:
return False
report.status = status
report.processed_by = processed_by
report.processed_at = datetime.now(timezone.utc)
await session.commit()
logger.info(
f"Репорт #{report_id} обновлён: статус={status}",
log_type="DATABASE"
)
return True
except Exception as e:
logger.error(
f"Ошибка обновления статуса репорта: {e}",
log_type="DATABASE"
)
return False
async def get_report_stats(self) -> dict:
"""
Получает общую статистику по репортам.
Returns:
dict: Статистика
"""
try:
from .models import Report
async with self.db.get_session() as session:
# Всего репортов
total_reports = await session.execute(
select(func.count(Report.id))
)
# По статусам
pending_reports = await session.execute(
select(func.count(Report.id)).where(Report.status == "pending")
)
closed_reports = await session.execute(
select(func.count(Report.id)).where(Report.status == "closed")
)
banned_reports = await session.execute(
select(func.count(Report.id)).where(Report.status == "banned")
)
deleted_reports = await session.execute(
select(func.count(Report.id)).where(Report.status == "deleted")
)
return {
'total': total_reports.scalar_one(),
'pending': pending_reports.scalar_one(),
'closed': closed_reports.scalar_one(),
'banned': banned_reports.scalar_one(),
'deleted': deleted_reports.scalar_one(),
}
except Exception as e:
logger.error(
f"Ошибка получения статистики репортов: {e}",
log_type="DATABASE"
)
return {}
async def get_top_reporters(self, limit: int = 10) -> List[tuple[int, str, int]]:
"""
Получает топ жалобщиков.
Args:
limit: Количество записей
Returns:
List[tuple[int, str, int]]: [(user_id, username, count), ...]
"""
try:
from .models import Report
async with self.db.get_session() as session:
result = await session.execute(
select(
Report.reporter_id,
Report.reporter_username,
func.count(Report.id).label('count')
)
.group_by(Report.reporter_id, Report.reporter_username)
.order_by(func.count(Report.id).desc())
.limit(limit)
)
return [
(row.reporter_id, row.reporter_username or f"id{row.reporter_id}", row.count)
for row in result
]
except Exception as e:
logger.error(
f"Ошибка получения топ жалобщиков: {e}",
log_type="DATABASE"
)
return []
async def get_top_reported_users(self, limit: int = 10) -> List[tuple[int, str, int]]:
"""
Получает топ нарушителей.
Args:
limit: Количество записей
Returns:
List[tuple[int, str, int]]: [(user_id, username, count), ...]
"""
try:
from .models import Report
async with self.db.get_session() as session:
result = await session.execute(
select(
Report.reported_user_id,
Report.reported_username,
func.count(Report.id).label('count')
)
.group_by(Report.reported_user_id, Report.reported_username)
.order_by(func.count(Report.id).desc())
.limit(limit)
)
return [
(row.reported_user_id, row.reported_username or f"id{row.reported_user_id}", row.count)
for row in result
]
except Exception as e:
logger.error(
f"Ошибка получения топ нарушителей: {e}",
log_type="DATABASE"
)
return []
async def get_recent_reports(self, limit: int = 20) -> List:
"""
Получает последние репорты.
Args:
limit: Количество записей
Returns:
List[Report]: Список репортов
"""
try:
from .models import Report
async with self.db.get_session() as session:
result = await session.execute(
select(Report)
.order_by(Report.created_at.desc())
.limit(limit)
)
return list(result.scalars().all())
except Exception as e:
logger.error(
f"Ошибка получения последних репортов: {e}",
log_type="DATABASE"
)
return []
async def get_user_report_count(self, user_id: int, as_reporter: bool = True) -> int:
"""
Получает количество репортов пользователя.
Args:
user_id: ID пользователя
as_reporter: True - как жалобщик, False - как нарушитель
Returns:
int: Количество репортов
"""
try:
from .models import Report
async with self.db.get_session() as session:
if as_reporter:
result = await session.execute(
select(func.count(Report.id)).where(
Report.reporter_id == user_id
)
)
else:
result = await session.execute(
select(func.count(Report.id)).where(
Report.reported_user_id == user_id
)
)
return result.scalar_one()
except Exception as e:
logger.error(
f"Ошибка подсчёта репортов пользователя: {e}",
log_type="DATABASE"
)
return 0
async def get_setting(self, key: str) -> Optional[str]:
"""Получает значение настройки"""
async with self.db.get_session() as session:
result = await session.get(Setting, key)
return result.value if result else None
async def set_setting(self, key: str, value: str) -> bool:
"""Устанавливает значение настройки"""
async with self.db.get_session() as session:
try:
setting = await session.get(Setting, key)
if setting:
setting.value = value
setting.updated_at = datetime.now()
else:
setting = Setting(key=key, value=value)
session.add(setting)
await session.commit()
return True
except Exception as e:
await session.rollback()
logger.error(f"set_setting {key} failed: {e}", log_type="DATABASE")
return False
async def delete_setting(self, key: str) -> bool:
"""Удаляет настройку"""
async with self.db.get_session() as session:
try:
result = await session.execute(delete(Setting).where(Setting.key == key))
await session.commit()
return result.rowcount > 0
except Exception as e:
await session.rollback()
logger.error(f"delete_setting {key} failed: {e}", log_type="DATABASE")
return False