329 lines
11 KiB
TypeScript
329 lines
11 KiB
TypeScript
import Mail from "nodemailer/lib/mailer";
|
|
import { member } from "../entity/club/member/member";
|
|
import { newsletter } from "../entity/club/newsletter/newsletter";
|
|
import { newsletterDates } from "../entity/club/newsletter/newsletterDates";
|
|
import { newsletterRecipients } from "../entity/club/newsletter/newsletterRecipients";
|
|
import MemberService from "../service/club/member/memberService";
|
|
import NewsletterDatesService from "../service/club/newsletter/newsletterDatesService";
|
|
import NewsletterRecipientsService from "../service/club/newsletter/newsletterRecipientsService";
|
|
import NewsletterService from "../service/club/newsletter/newsletterService";
|
|
import { CalendarHelper } from "./calendarHelper";
|
|
import DynamicQueryBuilder from "./dynamicQueryBuilder";
|
|
import { FileSystemHelper } from "./fileSystemHelper";
|
|
import MailHelper from "./mailHelper";
|
|
import { CLUB_NAME } from "../env.defaults";
|
|
import { TemplateHelper } from "./templateHelper";
|
|
import { PdfExport } from "./pdfExport";
|
|
import NewsletterConfigService from "../service/settings/newsletterConfigService";
|
|
import { NewsletterConfigType } from "../enums/newsletterConfigType";
|
|
import InternalException from "../exceptions/internalException";
|
|
import EventEmitter from "events";
|
|
|
|
export interface NewsletterEventType {
|
|
kind: "pdf" | "mail";
|
|
newsletterId: number;
|
|
total: number;
|
|
iteration: number;
|
|
msg: string;
|
|
}
|
|
|
|
export abstract class NewsletterHelper {
|
|
public static jobStatus = new EventEmitter();
|
|
|
|
private static formatJobEmit(
|
|
event: "progress" | "complete",
|
|
kind: "pdf" | "mail",
|
|
factor: "success" | "failed" | "info",
|
|
newsletterId: number,
|
|
total: number,
|
|
iteration: number,
|
|
msg: string
|
|
) {
|
|
this.jobStatus.emit<NewsletterEventType>(event, {
|
|
kind,
|
|
newsletterId,
|
|
factor,
|
|
total,
|
|
iteration,
|
|
msg,
|
|
date: new Date(),
|
|
});
|
|
}
|
|
|
|
public static buildData(
|
|
newsletter: newsletter,
|
|
dates: Array<newsletterDates>,
|
|
recipient?: member,
|
|
showAdress: boolean = false
|
|
) {
|
|
return {
|
|
title: newsletter.title,
|
|
description: newsletter.description,
|
|
newsletterTitle: newsletter.newsletterTitle,
|
|
newsletterText: newsletter.newsletterText,
|
|
newsletterSignatur: newsletter.newsletterSignatur,
|
|
dates: dates.map((d) => ({
|
|
title: d.diffTitle ?? d.calendar.title,
|
|
content: d.diffDescription ?? d.calendar.content,
|
|
starttime: d.calendar.starttime,
|
|
formattedStarttime: new Date(d.calendar.starttime).toLocaleDateString("de-DE", {
|
|
weekday: "long",
|
|
day: "2-digit",
|
|
month: "long",
|
|
}),
|
|
formattedFullStarttime: new Date(d.calendar.starttime).toLocaleDateString("de-DE", {
|
|
weekday: "long",
|
|
day: "2-digit",
|
|
month: "long",
|
|
year: "numeric",
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
}),
|
|
endtime: d.calendar.endtime,
|
|
formattedEndtime: new Date(d.calendar.endtime).toLocaleDateString("de-DE", {
|
|
weekday: "long",
|
|
day: "2-digit",
|
|
month: "long",
|
|
}),
|
|
formattedFullEndtime: new Date(d.calendar.endtime).toLocaleDateString("de-DE", {
|
|
weekday: "long",
|
|
day: "2-digit",
|
|
month: "long",
|
|
year: "numeric",
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
}),
|
|
location: d.calendar.location,
|
|
})),
|
|
...(recipient
|
|
? {
|
|
recipient: {
|
|
firstname: recipient.firstname,
|
|
lastname: recipient.lastname,
|
|
salutation: recipient.salutation.salutation,
|
|
nameaffix: recipient.nameaffix,
|
|
...(showAdress
|
|
? {
|
|
street: recipient.sendNewsletter.street ?? "",
|
|
streetNumber: recipient.sendNewsletter.streetNumber ?? "",
|
|
streetNumberAdd: recipient.sendNewsletter.streetNumberAddition ?? "",
|
|
}
|
|
: {}),
|
|
},
|
|
}
|
|
: {}),
|
|
};
|
|
}
|
|
|
|
public static async transformRecipientsToMembers(
|
|
newsletter: newsletter,
|
|
recipients: Array<newsletterRecipients>
|
|
): Promise<Array<member>> {
|
|
let useQuery = newsletter.recipientsByQuery?.query;
|
|
|
|
let queryMemberIds: Array<string> = [];
|
|
if (useQuery) {
|
|
let result = await DynamicQueryBuilder.executeQuery(
|
|
useQuery.startsWith("{") ? JSON.parse(useQuery) : useQuery,
|
|
0,
|
|
1000
|
|
);
|
|
if (result.stats == "success") {
|
|
let keys = Object.keys(result.rows?.[0] ?? {});
|
|
let memberKey = keys.find((k) => k.includes("member_id"));
|
|
queryMemberIds = result.rows.map((t) => (t[memberKey] ?? t.id) as string);
|
|
}
|
|
}
|
|
|
|
for (let recipient of recipients) {
|
|
if (!queryMemberIds.includes(recipient.memberId)) {
|
|
queryMemberIds.push(recipient.memberId);
|
|
}
|
|
}
|
|
|
|
let members = await MemberService.getAll({ noLimit: true });
|
|
|
|
return members[0].filter((m) => queryMemberIds.includes(m.id));
|
|
}
|
|
|
|
public static getICSFilePath(newsletter: newsletter) {
|
|
return FileSystemHelper.formatPath(
|
|
"newsletter",
|
|
`${newsletter.id}_${newsletter.title.replace(" ", "")}`,
|
|
`events.ics`
|
|
);
|
|
}
|
|
|
|
public static saveIcsToFile(newsletter: newsletter, ics: string) {
|
|
FileSystemHelper.writeFile(`newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`, "events.ics", ics);
|
|
}
|
|
|
|
public static async sendMails(newsletterId: number) {
|
|
let newsletter = await NewsletterService.getById(newsletterId);
|
|
let dates = await NewsletterDatesService.getAll(newsletterId);
|
|
let recipients = await NewsletterRecipientsService.getAll(newsletterId);
|
|
let config = await NewsletterConfigService.getAll();
|
|
|
|
const { value, error } = CalendarHelper.buildICS(dates.map((r) => r.calendar));
|
|
if (error) throw new InternalException("Failed Building ICS form Mail", error);
|
|
this.saveIcsToFile(newsletter, value);
|
|
|
|
let allowedForMail = config.filter((c) => c.config == NewsletterConfigType.mail).map((c) => c.comTypeId);
|
|
|
|
const members = await this.transformRecipientsToMembers(newsletter, recipients);
|
|
const mailRecipients = members.filter(
|
|
(m) =>
|
|
m.sendNewsletter != null &&
|
|
m.sendNewsletter?.email != null &&
|
|
allowedForMail.includes(m.sendNewsletter?.type?.id)
|
|
);
|
|
|
|
this.formatJobEmit("progress", "mail", "info", newsletterId, mailRecipients.length, 0, "starting sending");
|
|
|
|
for (const [index, rec] of mailRecipients.entries()) {
|
|
let data = this.buildData(newsletter, dates, rec);
|
|
|
|
const { body } = await TemplateHelper.renderFileForModule({
|
|
module: "newsletter",
|
|
bodyData: data,
|
|
title: `Newsletter von ${CLUB_NAME}`,
|
|
});
|
|
await MailHelper.sendMail(rec.sendNewsletter.email, `Newsletter von ${CLUB_NAME}`, body, [
|
|
{ filename: "events.ics", path: this.getICSFilePath(newsletter) },
|
|
])
|
|
.then(() => {
|
|
this.formatJobEmit(
|
|
"progress",
|
|
"mail",
|
|
"success",
|
|
newsletterId,
|
|
mailRecipients.length,
|
|
index + 1,
|
|
`successfully sent to ${rec.sendNewsletter.email}`
|
|
);
|
|
})
|
|
.catch((err) => {
|
|
this.formatJobEmit(
|
|
"progress",
|
|
"mail",
|
|
"failed",
|
|
newsletterId,
|
|
mailRecipients.length,
|
|
index + 1,
|
|
`failed to send to ${rec.sendNewsletter.email}`
|
|
);
|
|
});
|
|
}
|
|
|
|
this.formatJobEmit(
|
|
"complete",
|
|
"mail",
|
|
"info",
|
|
newsletterId,
|
|
mailRecipients.length,
|
|
mailRecipients.length,
|
|
`completed sending process`
|
|
);
|
|
}
|
|
|
|
public static async printPdfs(newsletterId: number) {
|
|
let newsletter = await NewsletterService.getById(newsletterId);
|
|
let dates = await NewsletterDatesService.getAll(newsletterId);
|
|
let recipients = await NewsletterRecipientsService.getAll(newsletterId);
|
|
let config = await NewsletterConfigService.getAll();
|
|
|
|
FileSystemHelper.clearDirectoryByFiletype(
|
|
`newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`,
|
|
".pdf"
|
|
);
|
|
|
|
const { value, error } = CalendarHelper.buildICS(dates.map((r) => r.calendar));
|
|
if (error) throw new InternalException("Failed Building ICS form Pdf", error);
|
|
this.saveIcsToFile(newsletter, value);
|
|
|
|
let notAllowedForPdf = config.filter((c) => c.config == NewsletterConfigType.mail).map((c) => c.comTypeId);
|
|
let printWithAdress = config.filter((c) => c.config == NewsletterConfigType.pdf).map((c) => c.comTypeId);
|
|
|
|
const members = await this.transformRecipientsToMembers(newsletter, recipients);
|
|
const pdfRecipients = members.filter(
|
|
(m) => !notAllowedForPdf.includes(m.sendNewsletter?.type?.id) || m.sendNewsletter == null
|
|
);
|
|
|
|
this.formatJobEmit("progress", "pdf", "info", newsletterId, pdfRecipients.length + 1, 0, "starting sending");
|
|
|
|
for (const [index, rec] of [
|
|
...pdfRecipients,
|
|
{ id: "0", firstname: "Alle Mitglieder", lastname: CLUB_NAME } as member,
|
|
].entries()) {
|
|
let data = this.buildData(newsletter, dates, rec, printWithAdress.includes(rec.sendNewsletter?.type?.id));
|
|
|
|
await PdfExport.renderFile({
|
|
template: "newsletter",
|
|
title: `Newsletter von ${CLUB_NAME}`,
|
|
filename: `${rec.lastname}_${rec.firstname}_${rec.id}`,
|
|
folder: `newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`,
|
|
data: data,
|
|
})
|
|
.then(() => {
|
|
this.formatJobEmit(
|
|
"progress",
|
|
"pdf",
|
|
"success",
|
|
newsletterId,
|
|
pdfRecipients.length + 1,
|
|
index + 1,
|
|
`successfully printed for ${rec.lastname}, ${rec.firstname}`
|
|
);
|
|
})
|
|
.catch((err) => {
|
|
this.formatJobEmit(
|
|
"progress",
|
|
"pdf",
|
|
"failed",
|
|
newsletterId,
|
|
pdfRecipients.length + 1,
|
|
index + 1,
|
|
`failed print for ${rec.lastname}, ${rec.firstname}`
|
|
);
|
|
});
|
|
}
|
|
|
|
await PdfExport.sqashToSingleFile(
|
|
`newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`,
|
|
"allPdfsTogether",
|
|
`newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`
|
|
)
|
|
.then(() => {
|
|
this.formatJobEmit(
|
|
"progress",
|
|
"pdf",
|
|
"success",
|
|
newsletterId,
|
|
pdfRecipients.length + 1,
|
|
pdfRecipients.length + 1,
|
|
"sucessfully combined pdf"
|
|
);
|
|
})
|
|
.catch((err) => {
|
|
this.formatJobEmit(
|
|
"progress",
|
|
"pdf",
|
|
"failed",
|
|
newsletterId,
|
|
pdfRecipients.length + 1,
|
|
pdfRecipients.length + 1,
|
|
"failed combining pdf"
|
|
);
|
|
});
|
|
|
|
this.formatJobEmit(
|
|
"complete",
|
|
"pdf",
|
|
"info",
|
|
newsletterId,
|
|
pdfRecipients.length + 1,
|
|
pdfRecipients.length + 1,
|
|
`completed printing process`
|
|
);
|
|
}
|
|
}
|