blob: 5f7937febcdfb46b3abc2ce0504f81e764fd9d0f [file] [log] [blame]
Matthias Andreas Benkardba3e58c2020-11-01 12:58:35 +01001package eu.mulk.mulkcms2.benki.newsletter;
2
3import static java.util.stream.Collectors.partitioningBy;
4import static java.util.stream.Collectors.toList;
5
6import eu.mulk.mulkcms2.benki.bookmarks.Bookmark;
7import eu.mulk.mulkcms2.benki.lazychat.LazychatMessage;
8import eu.mulk.mulkcms2.benki.posts.Post;
9import io.quarkus.mailer.MailTemplate.MailTemplateInstance;
10import io.quarkus.panache.common.Sort;
Matthias Andreas Benkard49b01512021-07-05 06:45:54 +020011import io.quarkus.qute.CheckedTemplate;
Matthias Andreas Benkardba3e58c2020-11-01 12:58:35 +010012import io.quarkus.qute.TemplateExtension;
Matthias Andreas Benkardba3e58c2020-11-01 12:58:35 +010013import io.quarkus.scheduler.Scheduled;
Matthias Andreas Benkarde3bc3ee2023-08-06 16:21:11 +020014import jakarta.enterprise.context.Dependent;
15import jakarta.persistence.EntityManager;
16import jakarta.persistence.PersistenceContext;
17import jakarta.transaction.Transactional;
Matthias Andreas Benkardba3e58c2020-11-01 12:58:35 +010018import java.time.LocalDate;
19import java.time.OffsetDateTime;
20import java.time.ZoneId;
21import java.time.format.DateTimeFormatter;
22import java.time.format.FormatStyle;
23import java.util.List;
24import java.util.concurrent.ExecutionException;
25import java.util.concurrent.TimeUnit;
26import java.util.concurrent.TimeoutException;
27import javax.annotation.CheckForNull;
Matthias Andreas Benkardba3e58c2020-11-01 12:58:35 +010028import org.eclipse.microprofile.config.inject.ConfigProperty;
29import org.hibernate.Session;
30
31@Dependent
32public class NewsletterSender {
33
34 private static final DateTimeFormatter humanDateFormatter =
35 DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG);
36
37 @ConfigProperty(name = "mulkcms.newsletter.time-zone")
38 ZoneId newsletterTimeZone;
39
Matthias Andreas Benkardd4df22d2021-02-06 11:31:27 +010040 @ConfigProperty(name = "quarkus.mailer.from")
41 String senderAddress;
42
Matthias Andreas Benkardba3e58c2020-11-01 12:58:35 +010043 @PersistenceContext EntityManager em;
44
45 @CheckedTemplate
46 static class Templates {
47 public static native MailTemplateInstance newsletter(
48 Newsletter newsletter, List<Bookmark> bookmarks, List<LazychatMessage> lazychatMessages);
49 }
50
51 @Scheduled(cron = "0 0 0 ? * Mon")
52 @Transactional
53 void run() throws InterruptedException, TimeoutException, ExecutionException {
54 var session = em.unwrap(Session.class);
55
Matthias Andreas Benkard475bf002023-08-06 20:56:30 +020056 List<Post> posts =
Matthias Andreas Benkard601508f2022-11-08 21:42:59 +010057 Post.find(
58 """
59 SELECT DISTINCT p FROM Post p
60 JOIN p.targets r
61 JOIN r.tags tag
62 WHERE p.newsletter IS NULL
63 AND p.scope = 'top_level'
64 AND tag = 'world'
65 """,
66 Sort.ascending("date"))
Matthias Andreas Benkard601508f2022-11-08 21:42:59 +010067 .list();
Matthias Andreas Benkardba3e58c2020-11-01 12:58:35 +010068 Post.fetchTexts(posts);
69
70 if (posts.isEmpty()) {
71 return;
72 }
73
74 var postsByClass = posts.stream().collect(partitioningBy(Post::isBookmark));
75 var bookmarks =
76 postsByClass.getOrDefault(Boolean.TRUE, List.of()).stream()
77 .map(x -> (Bookmark) x)
78 .collect(toList());
79 var lazychatMessages =
80 postsByClass.getOrDefault(Boolean.FALSE, List.of()).stream()
81 .map(x -> (LazychatMessage) x)
82 .collect(toList());
83
84 var date = OffsetDateTime.now(newsletterTimeZone);
85 var newsletterNumber =
86 (int)
87 session
88 .createQuery("SELECT max(id) FROM Newsletter", Integer.class)
89 .uniqueResultOptional()
90 .map(x -> x + 1)
91 .orElse(1);
92
93 var newsletter = new Newsletter();
94 newsletter.id = newsletterNumber;
95 newsletter.date = date;
96 newsletter.persist();
97
98 posts.forEach(post -> post.newsletter = newsletter);
99
100 var subscriberEmails =
Matthias Andreas Benkardff358942020-11-03 06:16:17 +0100101 NewsletterSubscription.<NewsletterSubscription>stream("registrationKey IS NULL")
Matthias Andreas Benkardba3e58c2020-11-01 12:58:35 +0100102 .map(x -> x.email)
103 .toArray(String[]::new);
104
105 var mailText = Templates.newsletter(newsletter, bookmarks, lazychatMessages);
106 var sendJob =
107 mailText
108 .subject(String.format("MulkCMS newsletter #%d", newsletterNumber))
Matthias Andreas Benkardd4df22d2021-02-06 11:31:27 +0100109 .to(senderAddress)
Matthias Andreas Benkardba3e58c2020-11-01 12:58:35 +0100110 .bcc(subscriberEmails)
111 .send();
Matthias Andreas Benkarda50ac8d2021-06-28 19:58:36 +0200112 sendJob.subscribe().asCompletionStage().get(10000, TimeUnit.SECONDS);
Matthias Andreas Benkardba3e58c2020-11-01 12:58:35 +0100113 }
114
115 @TemplateExtension
116 @CheckForNull
117 static String humanDate(@CheckForNull LocalDate x) {
118 if (x == null) {
119 return null;
120 }
121 return humanDateFormatter.format(x);
122 }
123
124 @TemplateExtension
125 @CheckForNull
126 static String humanDate(@CheckForNull OffsetDateTime x) {
127 if (x == null) {
128 return null;
129 }
130 return humanDateFormatter.format(x);
131 }
132}