Skip to content

Commit cc03f35

Browse files
Switch to polling for emails (#111)
* Switch to polling for emails * Remove direnv * Delete direnv * Adjust some mailing * Fix dockerfile
1 parent 105c10b commit cc03f35

File tree

6 files changed

+82
-102
lines changed

6 files changed

+82
-102
lines changed

.envrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
use nix

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ buildNumber.properties
3434
/config/
3535
/logs/
3636
/out/
37+
.direnv

Dockerfile

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
FROM eclipse-temurin:21-alpine as build
1+
FROM gradle:jdk21-alpine as build
22

33
COPY . .
4-
RUN ./gradlew clean build
4+
RUN gradle clean build --no-daemon
55

66
FROM eclipse-temurin:21-alpine as runtime
77

88
WORKDIR /app
99

10-
COPY --from=build /build/libs/lyna-*-all.jar bot.jar
10+
COPY --from=build /home/gradle/build/libs/lyna-*-all.jar bot.jar
1111

1212
ENTRYPOINT ["java", "-Dbot.config=config/config.json", "-Dlog4j.configurationFile=config/log4j2.xml", "-jar" , "bot.jar"]

docker-compose.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ services:
2424
postgres:
2525
networks:
2626
- lyna
27-
image: postgres:16.1
27+
image: postgres:latest
2828
expose:
2929
- 5432
30+
ports:
31+
- 5432:5432
3032
volumes:
3133
- db_data:/var/lib/postgres/data
3234
environment:

shell.nix

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{ pkgs ? import <nixpkgs> {}, ... }:
2+
3+
let
4+
jdk = pkgs.jdk21;
5+
gradle= pkgs.gradle.override { java = jdk; };
6+
in
7+
pkgs.mkShell
8+
{
9+
packages = with pkgs; [jdk gradle];
10+
}
11+

src/main/java/de/chojo/lyna/mail/MailingService.java

+63-98
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,27 @@
1111
import jakarta.activation.DataHandler;
1212
import jakarta.mail.Address;
1313
import jakarta.mail.Authenticator;
14+
import jakarta.mail.Flags;
1415
import jakarta.mail.Folder;
15-
import jakarta.mail.FolderClosedException;
1616
import jakarta.mail.Message;
1717
import jakarta.mail.MessagingException;
18+
import jakarta.mail.NoSuchProviderException;
1819
import jakarta.mail.PasswordAuthentication;
1920
import jakarta.mail.Session;
20-
import jakarta.mail.Store;
2121
import jakarta.mail.Transport;
2222
import jakarta.mail.event.MessageCountAdapter;
23-
import jakarta.mail.event.MessageCountEvent;
2423
import jakarta.mail.internet.InternetAddress;
2524
import jakarta.mail.internet.MimeMessage;
25+
import jakarta.mail.search.FlagTerm;
2626
import org.eclipse.angus.mail.imap.IMAPFolder;
27+
import org.eclipse.angus.mail.imap.IMAPStore;
2728
import org.slf4j.Logger;
2829

2930
import java.util.ArrayList;
3031
import java.util.Date;
3132
import java.util.List;
3233
import java.util.Optional;
3334
import java.util.Properties;
34-
import java.util.concurrent.CompletableFuture;
35-
import java.util.concurrent.ScheduledThreadPoolExecutor;
3635
import java.util.concurrent.TimeUnit;
3736

3837
import static org.slf4j.LoggerFactory.getLogger;
@@ -42,10 +41,7 @@ public class MailingService {
4241
private final Data data;
4342
private final Configuration<ConfigFile> configuration;
4443
private static final Logger log = getLogger(MailingService.class);
45-
private volatile Session session;
46-
private Store imapStore;
4744
private final List<ThrowingConsumer<Message, Exception>> receivedListener = new ArrayList<>();
48-
private MessageCountAdapter countAdapter;
4945

5046
public MailingService(Threading threading, Data data, Configuration<ConfigFile> configuration) {
5147
this.threading = threading;
@@ -68,106 +64,65 @@ public static MailingService create(Threading threading, Data data, Configuratio
6864
}
6965

7066
private void init() throws MessagingException {
71-
createSession();
72-
createImapStore();
73-
createMailListener();
74-
startMailMonitor();
67+
threading.botWorker().scheduleAtFixedRate(this::loop, 10, 300, TimeUnit.SECONDS);
7568
registerMessageListener(new MessageHandler(data, this, configuration));
7669
}
7770

78-
private void createMailListener() {
79-
countAdapter = new MessageCountAdapter() {
80-
@Override
81-
public void messagesAdded(MessageCountEvent e) {
82-
if (e.getType() == MessageCountEvent.REMOVED) return;
83-
for (Message message : e.getMessages()) {
84-
try {
85-
log.info("Received new message from {}", ((InternetAddress) message.getFrom()[0]).getAddress());
86-
} catch (MessagingException ex) {
87-
// ignore
88-
}
89-
for (var messageConsumer : receivedListener) {
90-
try {
91-
messageConsumer.accept(message);
92-
} catch (Exception ex) {
93-
log.error("Error when handling mail", ex);
94-
}
95-
}
71+
private void loop(){
72+
try {
73+
check();
74+
} catch (Exception e){
75+
log.error("Could not check emails",e);
76+
// c:
77+
}
78+
}
79+
80+
private void check() throws Exception {
81+
log.debug("Performing mail check");
82+
Session session = createSession();
83+
var store = (IMAPStore) createImapStore(session);
84+
IMAPFolder inbox = getInbox(store);
85+
Message[] search = inbox.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false));
86+
87+
for (Message message : search) {
88+
log.info("Received new message from {}", ((InternetAddress) message.getFrom()[0]).getAddress());
89+
for (ThrowingConsumer<Message, Exception> consumer : receivedListener) {
90+
try {
91+
consumer.accept(message);
92+
} catch (Exception ex) {
93+
log.error("Error when handling mail", ex);
9694
}
9795
}
98-
};
96+
message.setFlag(Flags.Flag.SEEN, true);
97+
}
98+
99+
log.debug("Mail check done");
99100
}
100101

101-
private void createImapStore() throws MessagingException {
102+
private IMAPStore createImapStore(Session session) {
102103
log.info(LogNotify.STATUS, "Creating imap store");
103-
imapStore = session.getStore("imap");
104+
IMAPStore imapStore = null;
105+
try {
106+
imapStore = (IMAPStore) session.getStore("imap");
104107
imapStore.connect();
108+
} catch (MessagingException e) {
109+
throw new RuntimeException(e);
110+
}
111+
return imapStore;
105112
}
106113

107-
private void createSession() {
114+
private Session createSession() {
108115
log.info(LogNotify.STATUS, "Creating new mail session");
109116
Properties props = System.getProperties();
110117
Mailing mailing = configuration.config().mailing();
111118
props.put("mail.smtp.host", mailing.host());
112119
props.put("mail.imap.host", mailing.host());
113-
session = Session.getInstance(props, new Authenticator() {
120+
return Session.getInstance(props, new Authenticator() {
114121
@Override
115122
protected PasswordAuthentication getPasswordAuthentication() {
116123
return new PasswordAuthentication(mailing.user(), mailing.password());
117124
}
118125
});
119-
try {
120-
createImapStore();
121-
} catch (MessagingException e) {
122-
log.error(LogNotify.NOTIFY_ADMIN, "Could not recreate imap store");
123-
}
124-
}
125-
126-
private void startMailMonitor() {
127-
log.info(LogNotify.DISCORD, "Starting monitoring");
128-
IMAPFolder inbox = getFolder("Inbox");
129-
130-
try {
131-
inbox.open(Folder.READ_WRITE);
132-
} catch (MessagingException e) {
133-
// c:
134-
throw new RuntimeException(e);
135-
}
136-
137-
inbox.removeMessageCountListener(countAdapter);
138-
inbox.addMessageCountListener(countAdapter);
139-
log.info("Registered mail listener");
140-
waitForMail(inbox);
141-
}
142-
143-
private void waitForMail(IMAPFolder folder) {
144-
log.info("Waiting for mail");
145-
if(threading.botWorker() instanceof ScheduledThreadPoolExecutor ex){
146-
log.debug("Executor status: {}/{} are running", ex.getActiveCount(), ex.getPoolSize());
147-
}
148-
CompletableFuture.runAsync(() -> {
149-
var inbox = folder;
150-
while (true) {
151-
try {
152-
try {
153-
inbox.idle(true);
154-
} catch (FolderClosedException e) {
155-
log.error(LogNotify.NOTIFY_ADMIN, "Folder closed. Attempting to restart monitoring.");
156-
startMailMonitor();
157-
break;
158-
} catch (MessagingException e) {
159-
log.error(LogNotify.NOTIFY_ADMIN, "Could not start connection idling", e);
160-
startMailMonitor();
161-
break;
162-
}
163-
} catch (Exception e) {
164-
log.error(LogNotify.NOTIFY_ADMIN, "Connection to folder failed.", e);
165-
continue;
166-
}
167-
}
168-
}, threading.botWorker())
169-
.completeOnTimeout(null, 2, TimeUnit.HOURS)
170-
.thenRunAsync(this::startMailMonitor, threading.botWorker());
171126
}
172127

173128
public void registerMessageListener(ThrowingConsumer<Message, Exception> listener) {
@@ -176,9 +131,10 @@ public void registerMessageListener(ThrowingConsumer<Message, Exception> listene
176131

177132

178133
public void sendMail(Mail mail) {
134+
Session session = createSession();
179135
MimeMessage mimeMessage;
180136
try {
181-
mimeMessage = buildMessage(mail);
137+
mimeMessage = buildMessage(session, mail);
182138
} catch (MessagingException e) {
183139
log.error(LogNotify.NOTIFY_ADMIN, "Could not build mail", e);
184140
return;
@@ -188,19 +144,21 @@ public void sendMail(Mail mail) {
188144
() -> sendMessage(mimeMessage),
189145
err -> {
190146
log.error(LogNotify.NOTIFY_ADMIN, "Could not sent mail", err);
191-
createSession();
147+
sendMail(mail);
192148
});
193149

194150
if (sendResult.isEmpty()) {
195151
log.error(LogNotify.NOTIFY_ADMIN, "Retries exceeded. Aborting.");
196152
return;
197153
}
198154

155+
IMAPStore imapStore = createImapStore(session);
156+
199157
Optional<Boolean> result = Retry.retryAndReturn(3,
200-
() -> storeMessage(mimeMessage),
158+
() -> storeMessage(imapStore,mimeMessage),
201159
err -> {
202160
log.error(LogNotify.NOTIFY_ADMIN, "Could not store mail");
203-
createSession();
161+
sendMail(mail);
204162
});
205163

206164
if (result.isPresent() && result.get()) {
@@ -217,26 +175,33 @@ private boolean sendMessage(MimeMessage message) throws MessagingException {
217175
return true;
218176
}
219177

220-
private boolean storeMessage(MimeMessage message) throws MessagingException {
221-
Folder sent = getFolder("inbox").getFolder("Sent");
178+
private boolean storeMessage(IMAPStore store, MimeMessage message) throws MessagingException {
179+
store.getFolder("inbox");
180+
Folder sent = getInbox(store).getFolder("Sent");
222181
if (!sent.exists()) {
223182
sent.create(Folder.HOLDS_MESSAGES);
224183
}
225184
sent.appendMessages(new Message[]{message});
226185
return true;
227186
}
228187

229-
private IMAPFolder getFolder(String name) {
188+
private IMAPFolder getInbox(IMAPStore store) {
189+
return getFolder(store, "inbox");
190+
}
191+
192+
private IMAPFolder getFolder(IMAPStore store, String name) {
230193
return Retry.retryAndReturn(3, () -> {
231194
log.info(LogNotify.STATUS, "Connecting to folder {}", name);
232-
return (IMAPFolder) imapStore.getFolder(name);
195+
IMAPFolder folder = (IMAPFolder) store.getFolder(name);
196+
folder.open(Folder.READ_WRITE);
197+
return folder;
233198
}, err -> {
234-
log.error(LogNotify.NOTIFY_ADMIN, "Could not connect to folder. Rebuilding session.");
235-
createSession();
199+
log.error(LogNotify.NOTIFY_ADMIN, "Could not connect to folder. Retrying.");
200+
getFolder(store, name);
236201
}).orElseThrow(() -> new RuntimeException("Reconnecting to folder failed."));
237202
}
238203

239-
private MimeMessage buildMessage(Mail mail) throws MessagingException {
204+
private MimeMessage buildMessage(Session session, Mail mail) throws MessagingException {
240205
var message = new MimeMessage(session);
241206
message.addFrom(new Address[]{new InternetAddress(configuration.config().mailing().user())});
242207
message.setRecipient(Message.RecipientType.TO, new InternetAddress(mail.address(), false));

0 commit comments

Comments
 (0)