SAVE: Backup before Google Drive integration

This commit is contained in:
Admin 2026-04-20 12:13:52 +04:00
parent a28fcce19b
commit f4ac98c6f4
9 changed files with 1671 additions and 1 deletions

45
DOCS/AI_SESSION_RESUME.md Normal file
View file

@ -0,0 +1,45 @@
# 🧠 ИНСТРУКЦИЯ ПО ВОССТАНОВЛЕНИЮ СЕССИИ (19.04.2026)
> **Для AI-агента:** Прочитай этот файл полностью. Здесь зафиксированы результаты многочасового исследования и разработки связки Domovoy Bot + NotebookLM.
---
## 🎯 СУТЬ ПРОЕКТА
Создание "ИИ-Юриста" для жильцов дома.
1. **Источник**: Модуль `mutt` в `domovoy_bot` (выкачивает почту, конвертирует всё в PDF).
2. **База знаний**: Файл `domovoy_bot/services/email_auditor/exported_emails/TOTAL_ARCHIVE_2025-2026.pdf`.
3. **Мозги**: Google NotebookLM (Блокнот: "Разбор ответов строго по ЖК РФ").
---
## 🔑 ВАЖНЫЕ ДАННЫЕ
- **Notebook ID**: `00ed7d92-1ab2-4159-ac72-1d0c4be21377`
- **Source Name**: `all_mail_gmail.pdf` (в блокноте) или `TOTAL_ARCHIVE_2025-2026.pdf` (локально).
- **Session ID (прошлой сессии)**: `6dd7a10a-c921-4558-ac9e-6834c8037455`.
- **Gitea**: `http://192.168.10.105:3001` (репозиторий `domovoy_bot`).
---
## ✅ ЧТО УЖЕ СДЕЛАНО (ЭТАП 1)
1. **Sync Helper**: Создан скрипт `domovoy_bot/services/email_auditor/sync_helper.py` для пуша архива в ИИ.
2. **Web UI**: В `domovoy_bot/web/templates/emails.html` добавлена кнопка **«СИНХРОНИЗИРОВАТЬ С ИИ»**.
3. **Web API**: В `domovoy_bot/web/app.py` добавлен эндпоинт `/api/emails/sync_ai`.
4. **Telegram**: В `domovoy_bot/handlers/admin.py` добавлена команда `/legal_sync`.
5. **Proxy**: В `~/.gemini/settings.json` прописаны SOCKS5 прокси (10808) для NotebookLM MCP.
---
## ❌ ТЕКУЩАЯ ПРОБЛЕМА
**Авторизация NotebookLM**: Куки, скопированные из браузера, отклоняются Google при запросе с VPS.
**Решение**: Нужно выполнить `nlm login` через SSH-тоннель или найти способ передать свежий CSRF-токен.
---
## 📅 ПЛАН ДАЛЬШЕ
1. **Этап 1 (финал)**: Добиться успешного `source_add` через MCP.
2. **Этап 2**: Команда `/legal <вопрос>` в боте для верифицированных жильцов.
3. **Этап 3**: Полная докеризация (единый `docker-compose.yml`).
4. **Этап 4**: Публикация на GitHub и статьи.
---
**Кладбище проектов**: Проект `automaticLM` признан неудачным и перемещен в `~/archive/FAILED_PROJECTS/`. Не использовать его.

1
DOCS/GITEA_SETUP.md Normal file
View file

@ -0,0 +1 @@
# 💾 GITEA SETUP\n\n- **Host**: http://192.168.10.105:3001\n- **Repo**: domovoy_bot\n- **User**: matrixhasyou\n- **Push Command**: git push origin main\n\n**Заметка**: Всегда делай коммит перед важными изменениями!

File diff suppressed because one or more lines are too long

1
DOCS/PROJECT_ROADMAP.md Normal file
View file

@ -0,0 +1 @@
# 🚀 ROADMAP: DOMOVOY BOT + NOTEBOOKLM\n\n## 📝 ЭТАП 1: ИИ-ЮРИСТ (АДМИН-ПАНЕЛЬ)\n- [ ] Связка модуля mutt с NotebookLM.\n- [ ] Автоматическая замена источника 'all_mail_gmail.pdf' в блокноте 'Разбор ответов строго по ЖК РФ'.\n- [ ] Кнопка 'СИНХРОНИЗИРОВАТЬ С ИИ' в веб-панели.\n- [ ] Команда /legal_sync для админа.\n\n## ⚖️ ЭТАП 2: ИИ-ЮРИСТ ДЛЯ ЖИТЕЛЕЙ\n- [ ] Доступ для верифицированных жильцов.\n- [ ] Команда /legal <вопрос>.\n- [ ] Юридический анализ ответов через NotebookLM.\n\n## 🐳 ЭТАП 3: ДОКЕРИЗАЦИЯ\n- [ ] Создание единого docker-compose для деплоя.\n- [ ] Инструкции по 'борьбе' с УК для жителей.\n\n## ✍️ ЭТАП 4: ПУБЛИЧНОСТЬ\n- [ ] Статьи на Хабр/VC.\n- [ ] Open-source релиз на GitHub.\n\n--- \n## 🔐 ИНФРАСТРУКТУРА\n- **Gitea**: http://192.168.10.105:3001\n- **Notebook ID**: 00ed7d92-1ab2-4159-ac72-1d0c4be21377\n- **Source Name**: all_mail_gmail.pdf

File diff suppressed because it is too large Load diff

View file

@ -39,6 +39,38 @@ class BroadcastState(StatesGroup):
# АДМИН МЕНЮ # АДМИН МЕНЮ
# ============================================================================= # =============================================================================
@router.message(Command('legal_sync'))
@admin_only
async def cmd_legal_sync(message: Message):
"""Синхронизация архива с NotebookLM"""
await message.answer("🤖 <b>Запускаю синхронизацию с NotebookLM...</b>", parse_mode='HTML')
import os, subprocess
from pathlib import Path
# Путь к скрипту и venv
base_dir = Path(__file__).parent.parent
script_path = base_dir / "services" / "email_auditor" / "sync_helper.py"
venv_python = base_dir / "venv" / "bin" / "python3"
if not script_path.exists():
await message.answer("❌ Скрипт синхронизации не найден.")
return
try:
env = os.environ.copy()
env["PYTHONPATH"] = str(base_dir)
# Запускаем скрипт
result = subprocess.run([str(venv_python), str(script_path)], capture_output=True, text=True, env=env)
if result.returncode == 0:
await message.answer(f"✅ <b>Результат:</b>\n{result.stdout.strip()}", parse_mode='HTML')
else:
await message.answer(f"❌ <b>Ошибка:</b>\n{result.stderr}", parse_mode='HTML')
except Exception as e:
await message.answer(f"❌ Произошла ошибка: {str(e)}")
@router.message(Command('admin')) @router.message(Command('admin'))
@admin_only @admin_only
async def cmd_admin(message: Message): async def cmd_admin(message: Message):

View file

@ -0,0 +1,76 @@
#!/usr/bin/env python3
"""
Helper script to sync master PDF with NotebookLM.
Uses notebooklm-mcp (cli) internally.
"""
import os
import sys
import subprocess
import logging
from pathlib import Path
from dotenv import load_dotenv
# Path setup
BASE_DIR = Path(__file__).resolve().parent
PROJECT_ROOT = BASE_DIR.parent.parent
load_dotenv(PROJECT_ROOT / ".env")
# Logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
handlers=[logging.FileHandler(BASE_DIR / "sync.log", encoding="utf-8"), logging.StreamHandler()]
)
log = logging.getLogger("SyncAI")
# Config
MASTER_PDF = BASE_DIR / "exported_emails" / "TOTAL_ARCHIVE_2025-2026.pdf"
NOTEBOOK_ID = "00ed7d92-1ab2-4159-ac72-1d0c4be21377"
SOURCE_NAME = "all_mail_gmail.pdf"
def run_sync():
log.info("Starting AI Synchronization...")
if not MASTER_PDF.exists():
log.error(f"File not found: {MASTER_PDF}")
return False, "Файл архива не найден."
log.info(f"Using Master PDF: {MASTER_PDF} ({MASTER_PDF.stat().st_size / 1024 / 1024:.2f} MB)")
# Check for proxy
env = os.environ.copy()
if os.getenv("USE_PROXY", "").lower() == "true":
proxy = f"socks5h://{os.getenv('PROXY_HOST', '127.0.0.1')}:{os.getenv('PROXY_PORT', '10808')}"
env["HTTPS_PROXY"] = proxy
env["HTTP_PROXY"] = proxy
log.info(f"Using proxy for sync: {proxy}")
try:
# Step 1: Check if source exists and delete it (optional, to avoid duplicates)
log.info("Step 1: Checking for existing source in NotebookLM...")
# (This will be implemented when MCP auth is fixed)
# Step 2: Upload new version
log.info(f"Step 2: Uploading {MASTER_PDF} to notebook {NOTEBOOK_ID}...")
# For now, we simulate the call since auth is broken
# command = ["nlm", "source", "add", str(MASTER_PDF), "--notebook", NOTEBOOK_ID]
# result = subprocess.run(command, env=env, capture_output=True, text=True)
# if result.returncode == 0:
# log.info("Successfully synced with NotebookLM!")
# return True, "Синхронизация с ИИ успешно завершена."
# else:
# log.error(f"Sync failed: {result.stderr}")
# return False, f"Ошибка синхронизации: {result.stderr}"
log.warning("!!! SYNCHRONIZATION SIMULATED (WAITING FOR AUTH FIX) !!!")
return True, "Файл подготовлен. Ожидание восстановления авторизации NotebookLM."
except Exception as e:
log.error(f"Unexpected error: {e}")
return False, str(e)
if __name__ == "__main__":
success, msg = run_sync()
print(msg)

View file

@ -546,6 +546,23 @@ async def api_run_email_audit(username: str = Depends(get_current_admin)):
except Exception as e: except Exception as e:
return JSONResponse(status_code=500, content={"message": str(e)}) return JSONResponse(status_code=500, content={"message": str(e)})
@app.post("/api/emails/sync_ai")
async def api_sync_email_ai(username: str = Depends(get_current_admin)):
script_path = BASE_DIR / "services" / "email_auditor" / "sync_helper.py"
venv_python = BASE_DIR / "venv" / "bin" / "python3"
if not script_path.exists(): return JSONResponse(status_code=404, content={"message": "Sync script not found"})
try:
env = os.environ.copy()
env["PYTHONPATH"] = str(BASE_DIR)
# Запускаем скрипт синхронизации
result = subprocess.run([str(venv_python), str(script_path)], capture_output=True, text=True, env=env)
if result.returncode == 0:
return JSONResponse(content={"message": result.stdout.strip()})
else:
return JSONResponse(status_code=500, content={"message": f"Ошибка: {result.stderr}"})
except Exception as e:
return JSONResponse(status_code=500, content={"message": str(e)})
if __name__ == "__main__": if __name__ == "__main__":
import uvicorn import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000) uvicorn.run(app, host="0.0.0.0", port=8000)

View file

@ -25,7 +25,10 @@
<i class="bi bi-file-earmark-pdf-fill display-1 text-danger mb-3"></i> <i class="bi bi-file-earmark-pdf-fill display-1 text-danger mb-3"></i>
<h5>TOTAL_ARCHIVE_2025-2026.pdf</h5> <h5>TOTAL_ARCHIVE_2025-2026.pdf</h5>
<p class="small opacity-50">Обновлен: {{ fmt_local(last_update) }}</p> <p class="small opacity-50">Обновлен: {{ fmt_local(last_update) }}</p>
<a href="/static/email_archive/TOTAL_ARCHIVE_2025-2026.pdf" class="btn btn-success w-100" download>СКАЧАТЬ АРХИВ</a> <a href="/static/email_archive/TOTAL_ARCHIVE_2025-2026.pdf" class="btn btn-success w-100 mb-2" download>СКАЧАТЬ АРХИВ</a>
<button id="syncAiBtn" class="btn btn-matrix w-100">
<i class="bi bi-robot"></i> СИНХРОНИЗИРОВАТЬ С ИИ
</button>
{% else %} {% else %}
<i class="bi bi-file-earmark-x display-1 opacity-20 mb-3"></i> <i class="bi bi-file-earmark-x display-1 opacity-20 mb-3"></i>
<p>Архив еще не сформирован</p> <p>Архив еще не сформирован</p>
@ -130,6 +133,26 @@
</div> </div>
<script> <script>
document.getElementById('syncAiBtn').addEventListener('click', async function() {
const btn = this;
if (!confirm('Отправить архив в NotebookLM для анализа?')) return;
btn.disabled = true;
const oldHtml = btn.innerHTML;
btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span> СИНХРОНИЗИРУЮ...';
try {
const response = await fetch('/api/emails/sync_ai', { method: 'POST' });
const result = await response.json();
alert(result.message);
} catch (e) {
alert('Ошибка при синхронизации: ' + e);
} finally {
btn.disabled = false;
btn.innerHTML = oldHtml;
}
});
document.getElementById('runAuditBtn').addEventListener('click', async function() { document.getElementById('runAuditBtn').addEventListener('click', async function() {
const btn = this; const btn = this;
if (!confirm('Запустить процесс синхронизации с Gmail? Это может занять пару минут.')) return; if (!confirm('Запустить процесс синхронизации с Gmail? Это может занять пару минут.')) return;