#!/usr/bin/env python3
import subprocess
import os
import sys
import logging
import requests
import json
import re
import psycopg2
from pathlib import Path
from datetime import datetime
# === CONFIG ===
BASE_DIR = Path("/home/matrixhasyou/mutt")
PYTHON_BIN = "/usr/bin/python3"
GOOGLE_PYTHON_BIN = "/home/matrixhasyou/swarm-services/domovoy-bot/venv-google/bin/python"
EXPORT_SCRIPT = BASE_DIR / "export_emails.py"
CONSOLIDATE_SCRIPT = BASE_DIR / "consolidate_to_mega_pdf.py"
DRIVE_SYNC_SCRIPT = Path("/home/matrixhasyou/domovoy_drive_sync.py")
MASTER_PDF = BASE_DIR / "ALL_EMAILS_CONSOLIDATED.pdf"
BOT_TOKEN = "8725618164:AAH1tGalq-pw1l0t4P5c0sdLkCVJdh7IE7M"
CHAT_ID = "197957361"
STATUS_FILE = BASE_DIR / "pipeline_status.json"
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
log = logging.getLogger("ChameleonSyncMaster")
def send_tg(text):
url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage"
try:
requests.post(url, json={"chat_id": CHAT_ID, "text": text, "parse_mode": "HTML"},
timeout=20)
except Exception as e:
log.error(f"TG notification failed: {e}")
def get_db_url():
env_path = Path("/home/matrixhasyou/swarm-services/domovoy-bot/.env")
if env_path.exists():
try:
with open(env_path) as f:
for line in f:
if line.startswith("DATABASE_URL_SYNC="):
return line.split("=", 1)[1].strip()
except:
pass
return "postgresql://gemini_admin:secure_swarm_pass_2026@192.168.10.105:5433/domovoy_db"
def update_status(status, step, new_emails=0, total_emails=0, pdf_size_mb=0.0):
try:
data = {
"status": status,
"step": step,
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"new_emails": new_emails,
"total_emails": total_emails,
"pdf_size_mb": round(pdf_size_mb, 2)
}
with open(STATUS_FILE, "w") as f:
json.dump(data, f)
except Exception as e:
log.error(f"Failed to write pipeline status: {e}")
def update_db_start():
try:
conn = psycopg2.connect(get_db_url())
cur = conn.cursor()
cur.execute("INSERT INTO email_export_logs (status, start_time, new_emails_count) VALUES (%s, CURRENT_TIMESTAMP, 0) RETURNING id", ('running',))
log_id = cur.fetchone()[0]
conn.commit()
cur.close()
conn.close()
return log_id
except Exception as e:
log.error(f"Failed to log start in DB: {e}")
return None
def update_db_finish(log_id, status, new_emails_count):
if not log_id: return
try:
conn = psycopg2.connect(get_db_url())
cur = conn.cursor()
cur.execute("""
UPDATE email_export_logs
SET status = %s, end_time = CURRENT_TIMESTAMP, new_emails_count = %s
WHERE id = %s
""", (status, new_emails_count, log_id))
conn.commit()
cur.close()
conn.close()
except Exception as e:
log.error(f"Failed to update log status in DB: {e}")
def update_email_audit_stats():
try:
all_folder = Path("/home/matrixhasyou/mutt/exported_emails/all")
if not all_folder.exists():
return
stats = {}
for f in all_folder.glob("*.pdf"):
match = re.match(r'([a-zA-Z0-9.-]+)_(\d{4}-\d{2}-\d{2})_(.*)', f.stem)
if match:
domain, dt_str, subject = match.group(1), match.group(2), match.group(3)
if domain not in stats:
stats[domain] = {"inbound": 0, "outbound": 0, "last_date": dt_str}
else:
if dt_str > stats[domain]["last_date"]:
stats[domain]["last_date"] = dt_str
# Простейшая эвристика направления
stats[domain]["inbound"] += 1
conn = psycopg2.connect(get_db_url())
cur = conn.cursor()
# Сначала очистим старое для актуализации
cur.execute("DELETE FROM email_audit")
for domain, s in stats.items():
cur.execute("""
INSERT INTO email_audit (domain, description, inbound_count, outbound_count, last_email_date, updated_at)
VALUES (%s, %s, %s, %s, %s, CURRENT_TIMESTAMP)
""", (domain, f"Ведомство {domain}", s["inbound"], s["outbound"], datetime.strptime(s["last_date"], "%Y-%m-%d")))
conn.commit()
cur.close()
conn.close()
log.info("Email audit stats updated successfully in PostgreSQL.")
except Exception as e:
log.error(f"Failed to update email_audit stats: {e}")
def run_step(name, cmd):
log.info(f"--- Step: {name} ---")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
log.error(f"Step {name} FAILED!")
log.error(result.stderr)
return False, result.stderr
log.info(f"Step {name} completed.")
return True, result.stdout
def main():
start_time = datetime.now()
log.info(f"Starting Master Sync at {start_time}")
log_id = update_db_start()
# 1. Export Emails
update_status("running", "Сбор почты (Gmail Export)")
ok, out_export = run_step("Export Emails", [PYTHON_BIN, str(EXPORT_SCRIPT)])
if not ok:
update_status("error", "Ошибка на шаге: Сбор почты")
update_db_finish(log_id, "error", 0)
send_tg(f"❌ Mutt Master Sync: FAILED\nStep: Export Emails\nError: {out_export[:200]}")
return
# 2. Consolidate PDF
update_status("running", "Консолидация и разбиение PDF")
ok, out_consolidate = run_step("Consolidate PDF", [PYTHON_BIN, str(CONSOLIDATE_SCRIPT)])
if not ok:
update_status("error", "Ошибка на шаге: Консолидация PDF")
update_db_finish(log_id, "error", 0)
send_tg(f"❌ Mutt Master Sync: FAILED\nStep: Consolidate PDF\nError: {out_consolidate[:200]}")
return
# 3. Sync to G-Drive
update_status("running", "Синхронизация с Google Drive")
ok, out_drive = run_step("G-Drive Sync", [GOOGLE_PYTHON_BIN, str(DRIVE_SYNC_SCRIPT), str(MASTER_PDF)])
if not ok:
update_status("error", "Ошибка на шаге: Синхронизация Google Drive")
update_db_finish(log_id, "error", 0)
send_tg(f"❌ Mutt Master Sync: FAILED\nStep: G-Drive Sync\nError: {out_drive[:200]}")
return
end_time = datetime.now()
# Parsing Stats
new_emails = 0
total_emails = 0
for line in out_export.split('\n'):
if "Processed this run:" in line:
try: new_emails = int(line.split(':')[-1].strip())
except: pass
for line in out_consolidate.split('\n'):
if "Found" in line and "PDFs" in line:
try: total_emails = int(line.split('Found')[-1].split('PDFs')[0].strip())
except: pass
# Get total size of all parts or master pdf
pdf_size_mb = 0.0
if MASTER_PDF.exists():
pdf_size_mb = os.path.getsize(MASTER_PDF) / (1024 * 1024)
else:
parts = list(BASE_DIR.glob("ALL_EMAILS_CONSOLIDATED_part*.pdf"))
if parts:
pdf_size_mb = sum(p.stat().st_size for p in parts) / (1024 * 1024)
# Обновляем БД
update_db_finish(log_id, "success", new_emails)
update_email_audit_stats()
update_status("success", "Синхронизация завершена успешно", new_emails, total_emails, pdf_size_mb)
summary = f"""
🚀 AI ПОЧТОВЫЙ ТУРБО-СИНХРОН: OK 🚀
📥 Новых писем: {new_emails}
📚 Всего в архиве: {total_emails}
🐘 Размер PDF: {pdf_size_mb:.2f} МБ
☁️ G-Drive: Обновлено ✅
📅 {end_time.strftime("%Y-%m-%d %H:%M")}
Робот закончил работу. NotebookLM готов к анализу.
"""
send_tg(summary)
log.info("Master Sync completed successfully.")
# Save last sync time for web UI
sync_time_file = Path("/home/matrixhasyou/mutt/last_ai_sync.txt")
with open(sync_time_file, "w") as f:
f.write(end_time.strftime("%Y-%m-%d %H:%M:%S"))
if __name__ == "__main__":
main()