feat: классификация писем Qwen3:8b + обработчики (urgent→TG, task→VTODO, meeting→VEVENT)
- mail_archive.py: фикс вложений himalaya --dir → --downloads-dir; attachments/ только при has_attachment; идемпотентно - email_classifier.py: Qwen3:8b (Ollama) → теги info/urgent/task/meeting в frontmatter email.md - email_handlers.py: urgent→Telegram (Bot API+SOCKS5), task→Radicale VTODO, meeting→Radicale VEVENT; handled_* идемпотентность; --dry-run - .gitignore: игнор *.env.bak* - openspec: чейндж email-classification-handlers (в работе) - STATUS/TODO/WALKTHROUGH: прогресс сессии, подводные камни Radicale (http.client, Depth:1, формат дат)
This commit is contained in:
+30
-8
@@ -398,18 +398,34 @@ def make_email_md(meta, extra_headers, body, folder_name):
|
||||
|
||||
|
||||
def get_attachments(uid, folder, dest_dir):
|
||||
"""Скачать вложения письма в dest_dir."""
|
||||
"""
|
||||
Скачать вложения письма в dest_dir.
|
||||
|
||||
Спек email-attachments: правильный флаг — `--downloads-dir` (не `--dir`).
|
||||
Идемпотентность: если в dest_dir уже есть файлы — не качаем повторно.
|
||||
"""
|
||||
try:
|
||||
existing = list(dest_dir.iterdir()) if dest_dir.exists() else []
|
||||
if existing:
|
||||
print(f" вложения уже скачаны ({len(existing)} ф.) — пропускаю")
|
||||
return
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
try:
|
||||
run_cmd(
|
||||
HIMALAYA_CMD + [
|
||||
"attachment", "download", str(uid),
|
||||
"--folder", folder,
|
||||
"--dir", str(dest_dir),
|
||||
"--downloads-dir", str(dest_dir),
|
||||
],
|
||||
timeout=60,
|
||||
)
|
||||
except RuntimeError:
|
||||
pass # нет вложений — норм
|
||||
except RuntimeError as e:
|
||||
# Нет вложений / письмо не имеет вложений — норм для has_attachment=false.
|
||||
# Но если письмо помечено has_attachment=true, а скачать не вышло —
|
||||
# оставляем пустую папку и пишем warning (письмо не теряется).
|
||||
print(f" [WARN] вложения не скачаны: {e}", file=sys.stderr)
|
||||
|
||||
|
||||
def archive_folder(folder, limit=100):
|
||||
@@ -454,10 +470,9 @@ def archive_folder(folder, limit=100):
|
||||
date_str = env.get("date") or env.get("internal_date") or ""
|
||||
year, month = parse_date(date_str)
|
||||
|
||||
# Путь: /mnt/yandex-disk/hermes/email/<folder>/YYYY/MM/UID/
|
||||
# Путь: /opt/hermes/email/<folder>/YYYY/MM/UID/
|
||||
msg_dir = ARCHIVE_ROOT / folder / f"{year:04d}" / f"{month:02d}" / str(uid)
|
||||
email_path = msg_dir / "email.md"
|
||||
attachments_dir = msg_dir / "attachments"
|
||||
|
||||
# Проверка — уже сохранено
|
||||
if email_path.exists():
|
||||
@@ -467,7 +482,13 @@ def archive_folder(folder, limit=100):
|
||||
continue
|
||||
|
||||
msg_dir.mkdir(parents=True, exist_ok=True)
|
||||
attachments_dir.mkdir(parents=True, exist_ok=True)
|
||||
has_attachment = bool(env.get("has_attachment", False))
|
||||
# Папку attachments/ создаём ТОЛЬКО если у письма есть вложения
|
||||
# (спек email-attachments: без вложений пустую папку не создаём).
|
||||
attachments_dir = None
|
||||
if has_attachment:
|
||||
attachments_dir = msg_dir / "attachments"
|
||||
attachments_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Получаем заголовки и тело
|
||||
extra_headers, body = get_email_content(uid, folder)
|
||||
@@ -477,7 +498,8 @@ def archive_folder(folder, limit=100):
|
||||
email_path.write_text(content, encoding="utf-8")
|
||||
|
||||
# Вложения
|
||||
get_attachments(uid, folder, attachments_dir)
|
||||
if attachments_dir is not None:
|
||||
get_attachments(uid, folder, attachments_dir)
|
||||
|
||||
subj = (env.get("subject") or "")[:60]
|
||||
print(f" ✓ UID {uid} ({subj})")
|
||||
|
||||
Reference in New Issue
Block a user