From 4c6dd07c310410baa6347698a9deb0e736358921 Mon Sep 17 00:00:00 2001 From: rene Date: Thu, 30 Apr 2026 20:03:23 +0200 Subject: [PATCH] =?UTF-8?q?Fix:=20IMAP=20Sent-Ordner=20=E2=80=94=20echten?= =?UTF-8?q?=20Namen=20aus=20LIST-Antwort=20extrahieren=20(INBOX.Sent)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/routes/outreach.py | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/backend/routes/outreach.py b/backend/routes/outreach.py index ab4e7fb..6ec066c 100644 --- a/backend/routes/outreach.py +++ b/backend/routes/outreach.py @@ -10,13 +10,16 @@ from email.utils import formataddr from datetime import datetime from typing import List, Optional +import logging + from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from auth import require_admin from database import db -router = APIRouter() +router = APIRouter() +_log = logging.getLogger(__name__) _SMTP_HOST = os.getenv("SMTP_HOST", "mail.your-server.de") _SMTP_PORT = int(os.getenv("SMTP_PORT", "587")) @@ -45,29 +48,40 @@ _SENT_CANDIDATES = ["Sent", "Sent Messages", "Sent Items", "INBOX.Sent", "Gesend def _imap_save_sent(msg_bytes: bytes, account: str): acc = _ACCOUNTS.get(account) or _ACCOUNTS["partner"] if not acc["user"] or not acc["pass"]: + _log.warning("IMAP: Account '%s' nicht konfiguriert, überspringe.", account) return try: ctx = ssl.create_default_context() with imaplib.IMAP4_SSL(_IMAP_HOST, _IMAP_PORT, ssl_context=ctx) as imap: imap.login(acc["user"], acc["pass"]) - # Sent-Ordner finden + _, raw_folders = imap.list() + available = [f.decode(errors="replace") for f in (raw_folders or [])] + _log.info("IMAP Ordner (%s): %s", account, available) + + # Echten Ordnernamen aus LIST-Antwort extrahieren + # Format: '(\Flags) "." INBOX.Sent' → letztes Token folder = None - _, folders = imap.list() - available = [f.decode() for f in (folders or [])] - for candidate in _SENT_CANDIDATES: - if any(candidate.lower() in f.lower() for f in available): - folder = candidate + for line in available: + name = line.rsplit('"." ', 1)[-1].strip().strip('"') + for candidate in _SENT_CANDIDATES: + if candidate.lower() in name.lower(): + folder = name + break + if folder: break if not folder: - folder = "Sent" # Fallback: anlegen lassen - imap.append( + folder = "INBOX.Sent" + _log.info("IMAP: speichere in Ordner '%s' (%s)", folder, account) + + typ, data = imap.append( folder, r"\Seen", imaplib.Time2Internaldate(datetime.now().timestamp()), msg_bytes, ) - except Exception: - pass # Nicht blockieren wenn IMAP fehlschlägt + _log.info("IMAP append: %s %s", typ, data) + except Exception as e: + _log.error("IMAP Sent-Speicherung fehlgeschlagen (%s): %s", account, e) def _build_message(to: str, subject: str, body: str, account: str) -> MIMEMultipart: