ff-admin-server/src/helpers/newsletterHelper.ts

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`
);
}
}