Files
PrimoGuardBot-/bot/tasks/cleanup.py

264 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Фоновые задачи для автоматической очистки и обслуживания бота
"""
import asyncio
from datetime import datetime
from pathlib import Path
import shutil
from database import get_manager
from middleware.loggers import logger
from configs import settings
__all__ = ('cleanup_expired_banwords', 'cleanup_spam_stats', 'auto_backup_database', 'start_background_tasks', 'check_system_health')
async def cleanup_expired_banwords() -> None:
"""
Периодически удаляет истёкшие временные банворды.
Запускается каждый час и проверяет все временные слова.
Удаляет те, у которых истёк срок действия.
"""
logger.info("🔄 Запущена задача автоочистки временных банвордов", log_type="CLEANUP")
manager = get_manager()
while True:
try:
# Ждём 1 час между проверками
await asyncio.sleep(3600)
# Выполняем очистку
deleted = await manager.cleanup_expired_temp_words()
if deleted > 0:
logger.info(
f"✅ Очищено {deleted} истёкших временных банвордов",
log_type="CLEANUP"
)
else:
logger.debug(
"✓ Проверка временных банвордов: истёкших не найдено",
log_type="CLEANUP"
)
except Exception as e:
logger.error(
f"❌ Ошибка в задаче очистки банвордов: {e}",
log_type="CLEANUP"
)
# При ошибке ждём 5 минут перед следующей попыткой
await asyncio.sleep(300)
async def cleanup_spam_stats(max_age_days: int = 30) -> None:
"""
Очищает старую статистику антиспама.
Запускается каждые 24 часа и удаляет записи старше max_age_days дней.
Args:
max_age_days: Возраст записей для удаления (по умолчанию 30 дней)
"""
logger.info(
f"🔄 Запущена задача очистки статистики (возраст > {max_age_days} дней)",
log_type="CLEANUP"
)
from bot.middlewares.spam_mdw import spam_stats
while True:
try:
# Ждём 24 часа между проверками
await asyncio.sleep(86400)
# Очищаем старую статистику
max_age_seconds = max_age_days * 86400
deleted = spam_stats.cleanup(max_age=max_age_seconds)
if deleted > 0:
logger.info(
f"✅ Очищена статистика: {deleted} неактивных пользователей",
log_type="CLEANUP"
)
else:
logger.debug(
"✓ Проверка статистики: старых записей не найдено",
log_type="CLEANUP"
)
except Exception as e:
logger.error(
f"❌ Ошибка в задаче очистки статистики: {e}",
log_type="CLEANUP"
)
await asyncio.sleep(3600) # При ошибке ждём 1 час
async def auto_backup_database(backup_interval_hours: int = 24, keep_backups: int = 7) -> None:
"""
Автоматически создаёт резервные копии базы данных.
Args:
backup_interval_hours: Интервал между бэкапами (по умолчанию 24 часа)
keep_backups: Количество хранимых копий (по умолчанию 7)
"""
logger.info(
f"🔄 Запущена задача автобэкапа (каждые {backup_interval_hours}ч, хранить {keep_backups})",
log_type="CLEANUP"
)
while True:
try:
# Ждём указанное время
await asyncio.sleep(backup_interval_hours * 3600)
# Путь к базе данных
db_path = Path(settings.DATABASE_PATH)
if not db_path.exists():
logger.warning(
f"⚠️ База данных не найдена: {db_path}",
log_type="CLEANUP"
)
continue
# Создаём папку для бэкапов
backup_dir = Path("backups")
backup_dir.mkdir(exist_ok=True)
# Имя файла бэкапа с датой и временем
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = backup_dir / f"banwords_backup_{timestamp}.db"
# Копируем базу данных
shutil.copy2(db_path, backup_path)
# Получаем размер файла
backup_size = backup_path.stat().st_size / 1024 # KB
logger.info(
f"✅ Создан backup: {backup_path.name} ({backup_size:.1f} KB)",
log_type="CLEANUP"
)
# Удаляем старые бэкапы (оставляем только последние N)
backups = sorted(backup_dir.glob("banwords_backup_*.db"))
if len(backups) > keep_backups:
old_backups = backups[:-keep_backups]
for old_backup in old_backups:
old_backup.unlink()
logger.debug(
f"🗑️ Удалён старый backup: {old_backup.name}",
log_type="CLEANUP"
)
logger.info(
f"✓ Удалено старых бэкапов: {len(old_backups)}",
log_type="CLEANUP"
)
except Exception as e:
logger.error(
f"❌ Ошибка создания backup: {e}",
log_type="CLEANUP"
)
await asyncio.sleep(3600) # При ошибке ждём 1 час
async def check_system_health() -> None:
"""
Мониторинг здоровья системы.
Проверяет каждые 5 минут:
- Размер базы данных
- Количество записей в БД
- Использование памяти (статистика антиспама)
"""
logger.info("🔄 Запущена задача мониторинга системы", log_type="CLEANUP")
manager = get_manager()
while True:
try:
await asyncio.sleep(300) # 5 минут
# Проверяем размер БД
db_path = Path(settings.DATABASE_PATH)
if db_path.exists():
db_size_mb = db_path.stat().st_size / (1024 * 1024)
if db_size_mb > 100: # Если больше 100 MB
logger.warning(
f"⚠️ Большой размер БД: {db_size_mb:.2f} MB",
log_type="CLEANUP"
)
# Проверяем количество временных слов
stats = await manager.get_stats()
temp_count = stats.get('temp_total', 0)
if temp_count > 100: # Если больше 100 временных слов
logger.warning(
f"⚠️ Много временных банвордов: {temp_count}",
log_type="CLEANUP"
)
# Проверяем статистику антиспама
from bot.middlewares.spam_mdw import spam_stats
spam_summary = spam_stats.get_stats_summary()
active_blocks = spam_summary.get('active_blocks', 0)
if active_blocks > 10:
logger.warning(
f"⚠️ Много активных блокировок: {active_blocks}",
log_type="CLEANUP"
)
except Exception as e:
logger.error(
f"❌ Ошибка в задаче мониторинга: {e}",
log_type="CLEANUP"
)
await asyncio.sleep(600) # При ошибке ждём 10 минут
def start_background_tasks() -> list[asyncio.Task]:
"""
Запускает все фоновые задачи.
Returns:
List[asyncio.Task]: Список запущенных задач
"""
tasks = []
# 1. Автоочистка временных банвордов (каждый час)
task1 = asyncio.create_task(cleanup_expired_banwords())
tasks.append(task1)
logger.info("✅ Задача 'cleanup_expired_banwords' запущена", log_type="STARTUP")
# 2. Очистка старой статистики (каждые 24 часа)
task2 = asyncio.create_task(cleanup_spam_stats(max_age_days=30))
tasks.append(task2)
logger.info("✅ Задача 'cleanup_spam_stats' запущена", log_type="STARTUP")
# 3. Автобэкап базы данных (каждые 24 часа, хранить 7 копий)
task3 = asyncio.create_task(auto_backup_database(backup_interval_hours=24, keep_backups=7))
tasks.append(task3)
logger.info("✅ Задача 'auto_backup_database' запущена", log_type="STARTUP")
# 4. Мониторинг здоровья системы (каждые 5 минут)
task4 = asyncio.create_task(check_system_health())
tasks.append(task4)
logger.info("✅ Задача 'check_system_health' запущена", log_type="STARTUP")
logger.success(
f"🚀 Все фоновые задачи запущены: {len(tasks)} шт.",
log_type="STARTUP"
)
return tasks