Fix email poll: search all recent emails (not just UNSEEN) on manual trigger

The manual "Jetzt abrufen" button now runs synchronously and searches all
emails from the last 30 days instead of only unread ones. This fixes the
issue where already-read emails in IMAP were invisible to the poll task.
Duplicate detection (by sender+date+subject) prevents re-imports.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
SysAdmin Agent
2026-03-11 21:00:50 +00:00
parent c3c6755027
commit 96204c04dd
2 changed files with 37 additions and 20 deletions

View File

@@ -156,21 +156,27 @@ def _upload_to_paperless(content: bytes, filename: str, destinataer=None, betref
@shared_task(bind=True, max_retries=3, default_retry_delay=300, name="stiftung.tasks.poll_destinataer_emails")
def poll_destinataer_emails(self):
def poll_destinataer_emails(self, search_all_recent_days=0):
"""
Liest ungelesene E-Mails aus dem IMAP-Postfach und verarbeitet sie.
Liest E-Mails aus dem IMAP-Postfach und verarbeitet sie.
Wird durch Celery Beat alle 15 Minuten ausgeführt.
Args:
search_all_recent_days: Wenn > 0, werden alle E-Mails der letzten N Tage
durchsucht (nicht nur ungelesene). Nützlich für manuellen Abruf.
"""
from stiftung.models import Destinataer, DestinataerEmailEingang, DokumentLink
# IMAP-Konfiguration aus Settings
imap_host = getattr(settings, "IMAP_HOST", None)
imap_port = int(getattr(settings, "IMAP_PORT", 993))
imap_user = getattr(settings, "IMAP_USER", None)
imap_password = getattr(settings, "IMAP_PASSWORD", None)
imap_folder = getattr(settings, "IMAP_FOLDER", "INBOX")
imap_use_ssl = getattr(settings, "IMAP_USE_SSL", True)
# IMAP-Konfiguration: DB (AppConfiguration) mit Fallback auf env/settings
from stiftung.utils.config import get_config
imap_host = get_config("imap_host")
imap_port = int(get_config("imap_port", 993))
imap_user = get_config("imap_user")
imap_password = get_config("imap_password")
imap_folder = get_config("imap_folder", "INBOX")
imap_use_ssl = get_config("imap_use_ssl", True)
if not all([imap_host, imap_user, imap_password]):
logger.warning(
@@ -199,11 +205,18 @@ def poll_destinataer_emails(self):
mail.login(imap_user, imap_password)
mail.select(imap_folder)
# Ungelesene Nachrichten suchen
_, message_ids_raw = mail.search(None, "UNSEEN")
# Nachrichten suchen
if search_all_recent_days and search_all_recent_days > 0:
from datetime import timedelta
since_date = (datetime.now(dt_timezone.utc) - timedelta(days=search_all_recent_days)).strftime("%d-%b-%Y")
_, message_ids_raw = mail.search(None, "SINCE", since_date)
search_mode = f"ALL seit {since_date}"
else:
_, message_ids_raw = mail.search(None, "UNSEEN")
search_mode = "UNSEEN"
message_ids = message_ids_raw[0].split()
logger.info("Postfach '%s': %d ungelesene Nachricht(en) gefunden.", imap_folder, len(message_ids))
logger.info("Postfach '%s' (%s): %d Nachricht(en) gefunden.", imap_folder, search_mode, len(message_ids))
for msg_id in message_ids:
try: