176 lines
8.6 KiB
Python
176 lines
8.6 KiB
Python
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"🤖 <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, 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 += "❌ <b>Ошибки при разблокировке:</b>\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"🚨 <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
|