Erweiterungen
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
package de.assecutor.aimailassistant.mail.service;
|
||||
|
||||
import org.eclipse.angus.mail.imap.IMAPFolder;
|
||||
import org.eclipse.angus.mail.imap.IMAPStore;
|
||||
import de.assecutor.aimailassistant.mail.domain.OrderEmail;
|
||||
import de.assecutor.aimailassistant.mail.domain.OrderEmailRepository;
|
||||
import de.assecutor.aimailassistant.mail.domain.OrderSummary;
|
||||
import de.assecutor.aimailassistant.mail.event.EmailBroadcaster;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import jakarta.mail.*;
|
||||
import jakarta.mail.event.MessageCountAdapter;
|
||||
import jakarta.mail.event.MessageCountEvent;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import jakarta.mail.internet.MimeMultipart;
|
||||
import jakarta.mail.search.FlagTerm;
|
||||
@@ -35,6 +41,13 @@ public class ImapEmailService {
|
||||
private final boolean ssl;
|
||||
private final String folderName;
|
||||
|
||||
// IDLE support fields
|
||||
private volatile boolean idleSupported = false;
|
||||
private volatile boolean idleRunning = false;
|
||||
private Thread idleThread;
|
||||
private IMAPStore idleStore;
|
||||
private IMAPFolder idleFolder;
|
||||
|
||||
public ImapEmailService(
|
||||
OrderEmailRepository orderEmailRepository,
|
||||
LlmService llmService,
|
||||
@@ -54,8 +67,148 @@ public class ImapEmailService {
|
||||
this.folderName = folderName;
|
||||
}
|
||||
|
||||
// ==================== IDLE Support ====================
|
||||
|
||||
@PostConstruct
|
||||
public void initializeIdleListener() {
|
||||
log.info("Initializing IMAP IDLE listener...");
|
||||
startIdleListener();
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void shutdown() {
|
||||
log.info("Shutting down IMAP IDLE listener...");
|
||||
if (idleThread != null) {
|
||||
idleThread.interrupt();
|
||||
}
|
||||
closeIdleConnection();
|
||||
}
|
||||
|
||||
private void startIdleListener() {
|
||||
idleThread = new Thread(() -> {
|
||||
while (!Thread.interrupted()) {
|
||||
try {
|
||||
connectAndRunIdle();
|
||||
} catch (InterruptedException e) {
|
||||
log.info("IDLE thread interrupted, shutting down");
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
log.warn("IDLE connection lost: {}. Reconnecting in 10s...", e.getMessage());
|
||||
idleRunning = false;
|
||||
try {
|
||||
Thread.sleep(10000);
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "IMAP-IDLE-Thread");
|
||||
idleThread.setDaemon(true);
|
||||
idleThread.start();
|
||||
}
|
||||
|
||||
private void connectAndRunIdle() throws Exception {
|
||||
// Properties erstellen
|
||||
Properties props = new Properties();
|
||||
props.put("mail.store.protocol", "imaps");
|
||||
props.put("mail.imaps.host", host);
|
||||
props.put("mail.imaps.port", String.valueOf(port));
|
||||
props.put("mail.imaps.ssl.enable", String.valueOf(ssl));
|
||||
props.put("mail.imaps.ssl.trust", "*");
|
||||
|
||||
Session session = Session.getInstance(props);
|
||||
idleStore = (IMAPStore) session.getStore("imaps");
|
||||
idleStore.connect(host, port, username, password);
|
||||
|
||||
// IDLE-Unterstützung prüfen
|
||||
idleSupported = checkIdleSupport(idleStore);
|
||||
if (!idleSupported) {
|
||||
log.info("IMAP server does not support IDLE, using polling only");
|
||||
idleStore.close();
|
||||
idleStore = null;
|
||||
// Warte lange bevor erneut versucht wird (Polling übernimmt)
|
||||
Thread.sleep(Long.MAX_VALUE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Folder öffnen
|
||||
idleFolder = (IMAPFolder) idleStore.getFolder(folderName);
|
||||
idleFolder.open(Folder.READ_WRITE);
|
||||
|
||||
// Listener für neue Nachrichten
|
||||
idleFolder.addMessageCountListener(new MessageCountAdapter() {
|
||||
@Override
|
||||
public void messagesAdded(MessageCountEvent event) {
|
||||
log.info("IDLE: {} new message(s) received", event.getMessages().length);
|
||||
processNewMessages(event.getMessages());
|
||||
}
|
||||
});
|
||||
|
||||
log.info("IMAP IDLE started successfully on folder: {}", folderName);
|
||||
idleRunning = true;
|
||||
|
||||
// IDLE-Loop - erneuert automatisch nach Server-Timeout
|
||||
while (idleFolder.isOpen() && !Thread.interrupted()) {
|
||||
// idle() blockiert bis neue Nachricht oder Timeout (~29 min)
|
||||
idleFolder.idle();
|
||||
}
|
||||
|
||||
// Wenn wir hier ankommen, wurde die Verbindung getrennt
|
||||
idleRunning = false;
|
||||
closeIdleConnection();
|
||||
}
|
||||
|
||||
private boolean checkIdleSupport(IMAPStore store) {
|
||||
try {
|
||||
return store.hasCapability("IDLE");
|
||||
} catch (MessagingException e) {
|
||||
log.warn("Could not check IDLE capability: {}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void processNewMessages(Message[] messages) {
|
||||
for (Message message : messages) {
|
||||
try {
|
||||
processMessage(message);
|
||||
message.setFlag(Flags.Flag.SEEN, true);
|
||||
} catch (Exception e) {
|
||||
log.error("Error processing message via IDLE", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void closeIdleConnection() {
|
||||
try {
|
||||
if (idleFolder != null && idleFolder.isOpen()) {
|
||||
idleFolder.close(false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("Error closing IDLE folder: {}", e.getMessage());
|
||||
}
|
||||
try {
|
||||
if (idleStore != null && idleStore.isConnected()) {
|
||||
idleStore.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("Error closing IDLE store: {}", e.getMessage());
|
||||
}
|
||||
idleFolder = null;
|
||||
idleStore = null;
|
||||
}
|
||||
|
||||
// ==================== Polling (Fallback) ====================
|
||||
|
||||
@Scheduled(fixedDelayString = "${mail.imap.poll-interval-seconds:60}000")
|
||||
public void pollEmails() {
|
||||
// Nur pollen wenn IDLE nicht aktiv
|
||||
if (idleRunning && idleSupported) {
|
||||
log.debug("IDLE active, skipping poll");
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("Polling IMAP mailbox for new emails...");
|
||||
fetchAndProcessEmails();
|
||||
}
|
||||
|
||||
@@ -17,19 +17,19 @@ spring.jpa.hibernate.ddl-auto=update
|
||||
spring.h2.console.enabled=true
|
||||
|
||||
# IMAP Configuration
|
||||
mail.imap.host=mail.appcreation.de
|
||||
mail.imap.host=mailhub.assecutor.org
|
||||
mail.imap.port=993
|
||||
mail.imap.username=sb@appcreation.de
|
||||
mail.imap.password=SV1705CA!sb
|
||||
mail.imap.username=sb-anfrage@assecutor.org
|
||||
mail.imap.password=?,mpIpto,88
|
||||
mail.imap.ssl=true
|
||||
mail.imap.folder=INBOX
|
||||
mail.imap.poll-interval-seconds=60
|
||||
|
||||
# SMTP Configuration
|
||||
spring.mail.host=mail.appcreation.de
|
||||
spring.mail.host=mailhub.assecutor.org
|
||||
spring.mail.port=465
|
||||
spring.mail.username=sb@appcreation.de
|
||||
spring.mail.password=SV1705CA!sb
|
||||
spring.mail.username=sb-anfrage@assecutor.org
|
||||
spring.mail.password=?,mpIpto,88
|
||||
spring.mail.properties.mail.smtp.auth=true
|
||||
spring.mail.properties.mail.smtp.ssl.enable=true
|
||||
spring.mail.properties.mail.smtp.ssl.required=true
|
||||
|
||||
Reference in New Issue
Block a user