All checks were successful
CI pipeline / Test & Code Quality (push) Successful in 34s
196 lines
8.7 KiB
Python
196 lines
8.7 KiB
Python
import asyncio
|
||
from datetime import datetime, timedelta, timezone
|
||
from typing import Optional
|
||
|
||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||
from apscheduler.triggers.cron import CronTrigger
|
||
|
||
from helper_bot.utils.base_dependency_factory import get_global_instance
|
||
from logs.custom_logger import logger
|
||
|
||
from .metrics import db_query_time, track_errors, track_time
|
||
|
||
|
||
class AutoUnbanScheduler:
|
||
"""
|
||
Класс для автоматического разбана пользователей по истечении срока блокировки.
|
||
Запускается ежедневно в 5:00 по московскому времени.
|
||
"""
|
||
|
||
def __init__(self):
|
||
self.bdf = get_global_instance()
|
||
self.bot_db = self.bdf.get_db()
|
||
self.scheduler = AsyncIOScheduler()
|
||
self.bot = None # Будет установлен позже
|
||
|
||
def set_bot(self, bot):
|
||
"""Устанавливает экземпляр бота для отправки уведомлений"""
|
||
self.bot = bot
|
||
|
||
@track_time("auto_unban_users", "auto_unban_scheduler")
|
||
@track_errors("auto_unban_scheduler", "auto_unban_users")
|
||
@db_query_time("auto_unban_users", "users", "mixed")
|
||
async def auto_unban_users(self):
|
||
"""
|
||
Основная функция автоматического разбана пользователей.
|
||
Получает список пользователей, у которых истекает срок блокировки сегодня,
|
||
и удаляет их из черного списка.
|
||
"""
|
||
try:
|
||
logger.info("Запуск автоматического разбана пользователей")
|
||
|
||
# Получаем текущий UNIX timestamp
|
||
current_timestamp = int(datetime.now().timestamp())
|
||
|
||
logger.info(
|
||
f"Поиск пользователей для разблокировки на timestamp: {current_timestamp}"
|
||
)
|
||
|
||
# Получаем список пользователей для разблокировки
|
||
users_to_unban = await self.bot_db.get_users_for_unblock_today(
|
||
current_timestamp
|
||
)
|
||
|
||
if not users_to_unban:
|
||
logger.info("Нет пользователей для разблокировки сегодня")
|
||
return
|
||
|
||
logger.info(
|
||
f"Найдено {len(users_to_unban)} пользователей для разблокировки"
|
||
)
|
||
|
||
# Список для отслеживания результатов
|
||
success_count = 0
|
||
failed_count = 0
|
||
failed_users = []
|
||
|
||
# Разблокируем каждого пользователя
|
||
for user_id in users_to_unban:
|
||
try:
|
||
result = await self.bot_db.delete_user_blacklist(user_id)
|
||
if result:
|
||
success_count += 1
|
||
logger.info(f"Пользователь {user_id} успешно разблокирован")
|
||
else:
|
||
failed_count += 1
|
||
failed_users.append(f"{user_id}")
|
||
logger.error(f"Ошибка при разблокировке пользователя {user_id}")
|
||
except Exception as e:
|
||
failed_count += 1
|
||
failed_users.append(f"{user_id}")
|
||
logger.error(
|
||
f"Исключение при разблокировке пользователя {user_id}: {e}"
|
||
)
|
||
|
||
# Формируем отчет
|
||
report = self._generate_report(
|
||
success_count, failed_count, failed_users, users_to_unban
|
||
)
|
||
|
||
# Отправляем отчет в лог-канал
|
||
await self._send_report(report)
|
||
|
||
logger.info(
|
||
f"Автоматический разбан завершен. Успешно: {success_count}, Ошибок: {failed_count}"
|
||
)
|
||
|
||
except Exception as e:
|
||
error_msg = f"Критическая ошибка в автоматическом разбане: {e}"
|
||
logger.error(error_msg)
|
||
await self._send_error_report(error_msg)
|
||
|
||
def _generate_report(
|
||
self, success_count: int, failed_count: int, failed_users: list, all_users: dict
|
||
) -> str:
|
||
"""Генерирует отчет о результатах автоматического разбана"""
|
||
report = f"🤖 <b>Отчет об автоматическом разбане</b>\n\n"
|
||
report += f"📅 Дата: {datetime.now().strftime('%d.%m.%Y %H:%M')}\n"
|
||
report += f"✅ Успешно разблокировано: {success_count}\n"
|
||
report += f"❌ Ошибок: {failed_count}\n\n"
|
||
|
||
if success_count > 0:
|
||
report += "✅ <b>Разблокированные пользователи:</b>\n"
|
||
for user_id in all_users:
|
||
if str(user_id) not in failed_users:
|
||
report += f"• ID: {user_id}\n"
|
||
report += "\n"
|
||
|
||
if failed_users:
|
||
report += "❌ <b>Ошибки при разблокировке:</b>\n"
|
||
for user in failed_users:
|
||
report += f"• {user}\n"
|
||
|
||
return report
|
||
|
||
@track_time("send_report", "auto_unban_scheduler")
|
||
@track_errors("auto_unban_scheduler", "send_report")
|
||
async def _send_report(self, report: str):
|
||
"""Отправляет отчет в лог-канал"""
|
||
try:
|
||
if self.bot:
|
||
group_for_logs = self.bdf.settings["Telegram"]["group_for_logs"]
|
||
await self.bot.send_message(
|
||
chat_id=group_for_logs, text=report, parse_mode="HTML"
|
||
)
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при отправке отчета: {e}")
|
||
|
||
@track_time("send_error_report", "auto_unban_scheduler")
|
||
@track_errors("auto_unban_scheduler", "send_error_report")
|
||
async def _send_error_report(self, error_msg: str):
|
||
"""Отправляет отчет об ошибке в важный лог-канал"""
|
||
try:
|
||
if self.bot:
|
||
important_logs = self.bdf.settings["Telegram"]["important_logs"]
|
||
await self.bot.send_message(
|
||
chat_id=important_logs,
|
||
text=f"🚨 <b>Ошибка автоматического разбана</b>\n\n{error_msg}",
|
||
parse_mode="HTML",
|
||
)
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при отправке отчета об ошибке: {e}")
|
||
|
||
def start_scheduler(self):
|
||
"""Запускает планировщик задач"""
|
||
try:
|
||
# Добавляем задачу на ежедневное выполнение в 5:00 по Москве
|
||
self.scheduler.add_job(
|
||
self.auto_unban_users,
|
||
CronTrigger(hour=5, minute=0, timezone="Europe/Moscow"),
|
||
id="auto_unban_users",
|
||
name="Автоматический разбан пользователей",
|
||
replace_existing=True,
|
||
)
|
||
|
||
# Запускаем планировщик
|
||
self.scheduler.start()
|
||
logger.info(
|
||
"Планировщик автоматического разбана запущен. Задача запланирована на 5:00 по Москве"
|
||
)
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при запуске планировщика: {e}")
|
||
|
||
def stop_scheduler(self):
|
||
"""Останавливает планировщик задач"""
|
||
try:
|
||
if self.scheduler.running:
|
||
self.scheduler.shutdown()
|
||
logger.info("Планировщик автоматического разбана остановлен")
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при остановке планировщика: {e}")
|
||
|
||
async def run_manual_unban(self):
|
||
"""Запускает разбан вручную (для тестирования)"""
|
||
logger.info("Запуск ручного разбана пользователей")
|
||
await self.auto_unban_users()
|
||
|
||
|
||
# Глобальный экземпляр планировщика
|
||
auto_unban_scheduler = AutoUnbanScheduler()
|
||
|
||
|
||
def get_auto_unban_scheduler() -> AutoUnbanScheduler:
|
||
"""Возвращает глобальный экземпляр планировщика"""
|
||
return auto_unban_scheduler
|