11
11
import jakarta .activation .DataHandler ;
12
12
import jakarta .mail .Address ;
13
13
import jakarta .mail .Authenticator ;
14
+ import jakarta .mail .Flags ;
14
15
import jakarta .mail .Folder ;
15
- import jakarta .mail .FolderClosedException ;
16
16
import jakarta .mail .Message ;
17
17
import jakarta .mail .MessagingException ;
18
+ import jakarta .mail .NoSuchProviderException ;
18
19
import jakarta .mail .PasswordAuthentication ;
19
20
import jakarta .mail .Session ;
20
- import jakarta .mail .Store ;
21
21
import jakarta .mail .Transport ;
22
22
import jakarta .mail .event .MessageCountAdapter ;
23
- import jakarta .mail .event .MessageCountEvent ;
24
23
import jakarta .mail .internet .InternetAddress ;
25
24
import jakarta .mail .internet .MimeMessage ;
25
+ import jakarta .mail .search .FlagTerm ;
26
26
import org .eclipse .angus .mail .imap .IMAPFolder ;
27
+ import org .eclipse .angus .mail .imap .IMAPStore ;
27
28
import org .slf4j .Logger ;
28
29
29
30
import java .util .ArrayList ;
30
31
import java .util .Date ;
31
32
import java .util .List ;
32
33
import java .util .Optional ;
33
34
import java .util .Properties ;
34
- import java .util .concurrent .CompletableFuture ;
35
- import java .util .concurrent .ScheduledThreadPoolExecutor ;
36
35
import java .util .concurrent .TimeUnit ;
37
36
38
37
import static org .slf4j .LoggerFactory .getLogger ;
@@ -42,10 +41,7 @@ public class MailingService {
42
41
private final Data data ;
43
42
private final Configuration <ConfigFile > configuration ;
44
43
private static final Logger log = getLogger (MailingService .class );
45
- private volatile Session session ;
46
- private Store imapStore ;
47
44
private final List <ThrowingConsumer <Message , Exception >> receivedListener = new ArrayList <>();
48
- private MessageCountAdapter countAdapter ;
49
45
50
46
public MailingService (Threading threading , Data data , Configuration <ConfigFile > configuration ) {
51
47
this .threading = threading ;
@@ -68,106 +64,65 @@ public static MailingService create(Threading threading, Data data, Configuratio
68
64
}
69
65
70
66
private void init () throws MessagingException {
71
- createSession ();
72
- createImapStore ();
73
- createMailListener ();
74
- startMailMonitor ();
67
+ threading .botWorker ().scheduleAtFixedRate (this ::loop , 10 , 300 , TimeUnit .SECONDS );
75
68
registerMessageListener (new MessageHandler (data , this , configuration ));
76
69
}
77
70
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 );
96
94
}
97
95
}
98
- };
96
+ message .setFlag (Flags .Flag .SEEN , true );
97
+ }
98
+
99
+ log .debug ("Mail check done" );
99
100
}
100
101
101
- private void createImapStore () throws MessagingException {
102
+ private IMAPStore createImapStore (Session session ) {
102
103
log .info (LogNotify .STATUS , "Creating imap store" );
103
- imapStore = session .getStore ("imap" );
104
+ IMAPStore imapStore = null ;
105
+ try {
106
+ imapStore = (IMAPStore ) session .getStore ("imap" );
104
107
imapStore .connect ();
108
+ } catch (MessagingException e ) {
109
+ throw new RuntimeException (e );
110
+ }
111
+ return imapStore ;
105
112
}
106
113
107
- private void createSession () {
114
+ private Session createSession () {
108
115
log .info (LogNotify .STATUS , "Creating new mail session" );
109
116
Properties props = System .getProperties ();
110
117
Mailing mailing = configuration .config ().mailing ();
111
118
props .put ("mail.smtp.host" , mailing .host ());
112
119
props .put ("mail.imap.host" , mailing .host ());
113
- session = Session .getInstance (props , new Authenticator () {
120
+ return Session .getInstance (props , new Authenticator () {
114
121
@ Override
115
122
protected PasswordAuthentication getPasswordAuthentication () {
116
123
return new PasswordAuthentication (mailing .user (), mailing .password ());
117
124
}
118
125
});
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 ());
171
126
}
172
127
173
128
public void registerMessageListener (ThrowingConsumer <Message , Exception > listener ) {
@@ -176,9 +131,10 @@ public void registerMessageListener(ThrowingConsumer<Message, Exception> listene
176
131
177
132
178
133
public void sendMail (Mail mail ) {
134
+ Session session = createSession ();
179
135
MimeMessage mimeMessage ;
180
136
try {
181
- mimeMessage = buildMessage (mail );
137
+ mimeMessage = buildMessage (session , mail );
182
138
} catch (MessagingException e ) {
183
139
log .error (LogNotify .NOTIFY_ADMIN , "Could not build mail" , e );
184
140
return ;
@@ -188,19 +144,21 @@ public void sendMail(Mail mail) {
188
144
() -> sendMessage (mimeMessage ),
189
145
err -> {
190
146
log .error (LogNotify .NOTIFY_ADMIN , "Could not sent mail" , err );
191
- createSession ( );
147
+ sendMail ( mail );
192
148
});
193
149
194
150
if (sendResult .isEmpty ()) {
195
151
log .error (LogNotify .NOTIFY_ADMIN , "Retries exceeded. Aborting." );
196
152
return ;
197
153
}
198
154
155
+ IMAPStore imapStore = createImapStore (session );
156
+
199
157
Optional <Boolean > result = Retry .retryAndReturn (3 ,
200
- () -> storeMessage (mimeMessage ),
158
+ () -> storeMessage (imapStore , mimeMessage ),
201
159
err -> {
202
160
log .error (LogNotify .NOTIFY_ADMIN , "Could not store mail" );
203
- createSession ( );
161
+ sendMail ( mail );
204
162
});
205
163
206
164
if (result .isPresent () && result .get ()) {
@@ -217,26 +175,33 @@ private boolean sendMessage(MimeMessage message) throws MessagingException {
217
175
return true ;
218
176
}
219
177
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" );
222
181
if (!sent .exists ()) {
223
182
sent .create (Folder .HOLDS_MESSAGES );
224
183
}
225
184
sent .appendMessages (new Message []{message });
226
185
return true ;
227
186
}
228
187
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 ) {
230
193
return Retry .retryAndReturn (3 , () -> {
231
194
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 ;
233
198
}, 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 );
236
201
}).orElseThrow (() -> new RuntimeException ("Reconnecting to folder failed." ));
237
202
}
238
203
239
- private MimeMessage buildMessage (Mail mail ) throws MessagingException {
204
+ private MimeMessage buildMessage (Session session , Mail mail ) throws MessagingException {
240
205
var message = new MimeMessage (session );
241
206
message .addFrom (new Address []{new InternetAddress (configuration .config ().mailing ().user ())});
242
207
message .setRecipient (Message .RecipientType .TO , new InternetAddress (mail .address (), false ));
0 commit comments