import asyncio
from datetime import datetime, timezone, timedelta
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
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
async def auto_unban_users(self):
"""
Основная функция автоматического разбана пользователей.
Получает список пользователей, у которых истекает срок блокировки сегодня,
и удаляет их из черного списка.
"""
try:
logger.info("Запуск автоматического разбана пользователей")
# Получаем сегодняшнюю дату в формате YYYY-MM-DD
moscow_tz = timezone(timedelta(hours=3)) # UTC+3 для Москвы
today = datetime.now(moscow_tz).strftime("%Y-%m-%d")
logger.info(f"Поиск пользователей для разблокировки на дату: {today}")
# Получаем список пользователей для разблокировки
users_to_unban = self.bot_db.get_users_for_unblock_today(today)
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, username in users_to_unban.items():
try:
result = self.bot_db.delete_user_blacklist(user_id)
if result:
success_count += 1
logger.info(f"Пользователь {user_id} ({username}) успешно разблокирован")
else:
failed_count += 1
failed_users.append(f"{user_id} ({username})")
logger.error(f"Ошибка при разблокировке пользователя {user_id} ({username})")
except Exception as e:
failed_count += 1
failed_users.append(f"{user_id} ({username})")
logger.error(f"Исключение при разблокировке пользователя {user_id} ({username}): {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"🤖 Отчет об автоматическом разбане\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 += "✅ Разблокированные пользователи:\n"
for user_id, username in all_users.items():
if f"{user_id} ({username})" not in failed_users:
safe_username = username if username else "Неизвестный пользователь"
report += f"• ID: {user_id}, Имя: {safe_username}\n"
report += "\n"
if failed_users:
report += "❌ Ошибки при разблокировке:\n"
for user in failed_users:
report += f"• {user}\n"
return 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}")
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"🚨 Ошибка автоматического разбана\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