domovoy_bot/services/chat_exporter.py

119 lines
5.1 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.

"""
Надёжный сервис экспорта всей истории чата в JSON и Git.
"""
import json
import logging
import subprocess
import os
from pathlib import Path
from datetime import datetime, timezone, timedelta
from sqlalchemy import select, func
from database.models import User, Message as DBMessage
logger = logging.getLogger("ChatExporter")
class ChatExporter:
def __init__(self, session):
self.session = session
self.base_dir = Path(__file__).parent.parent
self.export_dir = self.base_dir / "exports"
self.export_dir.mkdir(exist_ok=True)
self.uly_tz = timezone(timedelta(hours=4))
async def run_full_export(self):
"""Полный цикл: Генерация -> Запись в БД -> Push в Git"""
try:
now_local = datetime.now(self.uly_tz)
timestamp = now_local.strftime("%Y-%m-%d_%H-%M")
filename = f"chat_history_{timestamp}.json"
filepath = self.export_dir / filename
# 1. Собираем данные
users_res = await self.session.execute(select(User))
users = users_res.scalars().all()
msg_res = await self.session.execute(select(DBMessage).order_by(DBMessage.timestamp.asc()))
messages = msg_res.scalars().all()
export_data = {
"info": {
"exported_at": now_local.isoformat(),
"total_users": len(list(users)),
"total_messages": len(list(messages))
},
"users": [
{
"id": u.user_id, "username": u.username, "name": u.full_name,
"apt": u.apartment, "verified": u.verified
} for u in users
],
"messages": [
{
"id": m.id, "user_id": m.user_id, "text": m.text,
"date": m.timestamp.isoformat() if m.timestamp else None,
"topic": m.topic
} for m in messages
]
}
# 2. Сохраняем файл
with open(filepath, "w", encoding="utf-8") as f:
json.dump(export_data, f, ensure_ascii=False, indent=2)
# 3. Пушим в Gitea
git_success = self._push_to_gitea(filepath)
# 4. Логируем в БД
await self._log_to_db(filename, len(messages), git_success)
return {"success": True, "filename": filename, "messages": len(messages), "git": git_success}
except Exception as e:
logger.error(f"❌ Критическая ошибка экспорта: {e}", exc_info=True)
return {"success": False, "error": str(e)}
def _push_to_gitea(self, filepath):
"""Отправка файла в Gitea через системный git"""
try:
# Напрямую, без прокси
env = os.environ.copy()
for var in ['http_proxy', 'https_proxy', 'all_proxy', 'HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY']:
env.pop(var, None)
# Переходим в корень проекта для git команд
os.chdir(str(self.base_dir))
subprocess.run(["git", "add", "-f", str(filepath)], check=True, env=env)
subprocess.run(["git", "commit", "-m", f"📦 Авто-экспорт чата: {filepath.name}"], check=True, env=env)
subprocess.run(["git", "push", "origin", "main"], check=True, env=env)
logger.info(f"✅ Файл {filepath.name} успешно отправлен в Gitea")
return True
except Exception as e:
logger.error(f"❌ Ошибка Git Push: {e}")
return False
async def _log_to_db(self, filename, count, git_pushed):
from sqlalchemy import text
try:
# Используем текстовый запрос, так как у нас нет модели для этой таблицы
sql = text("INSERT INTO chat_exports (filename, total_messages, git_pushed) VALUES (:f, :c, :g)")
await self.session.execute(sql, {"f": filename, "c": count, "g": 1 if git_pushed else 0})
await self.session.commit()
except Exception as e:
logger.error(f"❌ Ошибка записи в таблицу экспортов: {e}")
async def get_stats(self):
"""Получить статистику для веб-панели"""
from sqlalchemy import text
try:
sql = text("SELECT COUNT(*), MAX(export_date) FROM chat_exports")
res = await self.session.execute(sql)
row = res.first()
if row:
count, last_date = row
return {"total": count or 0, "last_date": last_date}
return {"total": 0, "last_date": None}
except Exception as e:
logger.error(f"Error getting stats: {e}")
return {"total": 0, "last_date": None}