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

357 lines
12 KiB
TypeScript
Raw Normal View History

2025-01-05 14:14:00 +01:00
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";
2025-02-15 10:59:54 +01:00
import NewsletterConfigService from "../service/configuration/newsletterConfigService";
import { NewsletterConfigEnum } from "../enums/newsletterConfigEnum";
import InternalException from "../exceptions/internalException";
2024-12-31 14:23:14 +01:00
import EventEmitter from "events";
export interface NewsletterEventType {
kind: "pdf" | "mail";
newsletterId: number;
total: number;
iteration: number;
msg: string;
}
export abstract class NewsletterHelper {
2024-12-31 14:23:14 +01:00
public static jobStatus = new EventEmitter();
private static formatJobEmit(
event: "progress" | "complete",
kind: "pdf" | "mail",
factor: "success" | "failed" | "info",
2024-12-31 14:23:14 +01:00
newsletterId: number,
total: number,
iteration: number,
msg: string
) {
this.jobStatus.emit<NewsletterEventType>(event, {
kind,
newsletterId,
factor,
total,
iteration,
msg,
date: new Date(),
});
2024-12-31 14:23:14 +01:00
}
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,
2025-04-10 12:28:41 +02:00
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,
}))
.sort((a, b) => a.starttime.getTime() - b.starttime.getTime()),
...(recipient
? {
recipient: {
firstname: recipient.firstname,
lastname: recipient.lastname,
2025-01-25 10:20:57 +01:00
salutation: recipient.salutation.salutation,
nameaffix: recipient.nameaffix,
...(showAdress
? {
2025-04-07 16:27:47 +02:00
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;
2025-01-29 08:53:49 +01:00
let queryMemberIds: Array<string> = [];
if (useQuery) {
2025-02-16 13:32:10 +01:00
let result = await DynamicQueryBuilder.executeQuery({
query: useQuery.startsWith("{") ? JSON.parse(useQuery) : useQuery,
noLimit: true,
});
if (result.stats == "success") {
let keys = Object.keys(result.rows?.[0] ?? {});
let memberKey = keys.find((k) => k.includes("member_id"));
2025-01-29 08:53:49 +01:00
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);
}
}
if (queryMemberIds.length == 0) {
return [];
} else {
let members = await MemberService.getAll({ noLimit: true, ids: queryMemberIds });
2025-04-19 09:29:05 +02:00
return members[0].filter((m) => m.sendNewsletter != null);
}
}
public static async getMailRecipients(newsletterId: number) {
let newsletter = await NewsletterService.getById(newsletterId);
let recipients = await NewsletterRecipientsService.getAll(newsletterId);
let config = await NewsletterConfigService.getAll();
let allowedForMail = config.filter((c) => c.config == NewsletterConfigEnum.mail).map((c) => c.comTypeId);
const members = await this.transformRecipientsToMembers(newsletter, recipients);
const mailRecipients = members.filter(
(m) => m.sendNewsletter?.email != "" && allowedForMail.includes(m.sendNewsletter?.type?.id)
);
return mailRecipients;
}
public static async getPrintRecipients(newsletterId: number) {
let newsletter = await NewsletterService.getById(newsletterId);
let recipients = await NewsletterRecipientsService.getAll(newsletterId);
let config = await NewsletterConfigService.getAll();
let notAllowedForPdf = config
.filter((c) => c.config == NewsletterConfigEnum.none || c.config == NewsletterConfigEnum.mail)
.map((c) => c.comTypeId);
const members = await this.transformRecipientsToMembers(newsletter, recipients);
const pdfRecipients = members.filter((m) => !notAllowedForPdf.includes(m.sendNewsletter?.type?.id));
pdfRecipients.unshift({
id: "0",
firstname: "Alle Mitglieder",
lastname: CLUB_NAME,
nameaffix: "",
salutation: { salutation: "" },
} as member);
return pdfRecipients;
}
public static getICSFilePath(newsletter: newsletter) {
return FileSystemHelper.formatPath(
"newsletter",
`${newsletter.id}_${newsletter.title.replaceAll(" ", "")}`,
`events.ics`
);
}
public static saveIcsToFile(newsletter: newsletter, ics: string) {
FileSystemHelper.writeFile(
`newsletter/${newsletter.id}_${newsletter.title.replaceAll(" ", "")}`,
"events.ics",
ics
);
}
public static async sendMails(newsletterId: number) {
let newsletter = await NewsletterService.getById(newsletterId);
let dates = await NewsletterDatesService.getAll(newsletterId);
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);
const mailRecipients = await this.getMailRecipients(newsletterId);
this.formatJobEmit("progress", "mail", "info", newsletterId, mailRecipients.length, 0, "starting sending");
2024-12-31 14:23:14 +01:00
for (const [index, rec] of mailRecipients.entries()) {
let data = this.buildData(newsletter, dates, rec);
2024-12-31 14:23:14 +01:00
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) },
])
2024-12-31 14:23:14 +01:00
.then(() => {
this.formatJobEmit(
"progress",
"mail",
"success",
2024-12-31 14:23:14 +01:00
newsletterId,
mailRecipients.length,
index + 1,
2024-12-31 14:23:14 +01:00
`successfully sent to ${rec.sendNewsletter.email}`
);
})
.catch((err) => {
2024-12-31 14:23:14 +01:00
this.formatJobEmit(
"progress",
"mail",
"failed",
2024-12-31 14:23:14 +01:00
newsletterId,
mailRecipients.length,
index + 1,
2024-12-31 14:23:14 +01:00
`failed to send to ${rec.sendNewsletter.email}`
);
});
}
2024-12-31 14:23:14 +01:00
this.formatJobEmit(
"complete",
"mail",
"info",
2024-12-31 14:23:14 +01:00
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 config = await NewsletterConfigService.getAll();
FileSystemHelper.clearDirectoryByFiletype(
`newsletter/${newsletter.id}_${newsletter.title.replaceAll(" ", "")}`,
".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 printWithAdress = config.filter((c) => c.config == NewsletterConfigEnum.pdf).map((c) => c.comTypeId);
const pdfRecipients = await this.getPrintRecipients(newsletterId);
2025-04-10 12:32:08 +02:00
this.formatJobEmit("progress", "pdf", "info", newsletterId, pdfRecipients.length, 0, "starting printing");
for (const [index, rec] of pdfRecipients.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}`.replaceAll(" ", "-"),
folder: `newsletter/${newsletter.id}_${newsletter.title.replaceAll(" ", "")}`,
data: data,
})
2024-12-31 14:23:14 +01:00
.then(() => {
this.formatJobEmit(
"progress",
"pdf",
"success",
2024-12-31 14:23:14 +01:00
newsletterId,
2025-04-10 12:45:29 +02:00
pdfRecipients.length + 1,
index + 1,
2024-12-31 14:23:14 +01:00
`successfully printed for ${rec.lastname}, ${rec.firstname}`
);
})
.catch((err) => {
2024-12-31 14:23:14 +01:00
this.formatJobEmit(
"progress",
"pdf",
"failed",
2024-12-31 14:23:14 +01:00
newsletterId,
2025-04-10 12:45:29 +02:00
pdfRecipients.length + 1,
index + 1,
2024-12-31 14:23:14 +01:00
`failed print for ${rec.lastname}, ${rec.firstname}`
);
});
}
await PdfExport.sqashToSingleFile(
`newsletter/${newsletter.id}_${newsletter.title.replaceAll(" ", "")}`,
"allPdfsTogether",
`newsletter/${newsletter.id}_${newsletter.title.replaceAll(" ", "")}`
)
2024-12-31 14:23:14 +01:00
.then(() => {
this.formatJobEmit(
"progress",
"pdf",
"success",
2024-12-31 14:23:14 +01:00
newsletterId,
2025-04-10 12:45:29 +02:00
pdfRecipients.length + 1,
pdfRecipients.length + 1,
2024-12-31 14:23:14 +01:00
"sucessfully combined pdf"
);
})
.catch((err) => {
2024-12-31 14:23:14 +01:00
this.formatJobEmit(
"progress",
"pdf",
"failed",
2024-12-31 14:23:14 +01:00
newsletterId,
2025-04-10 12:45:29 +02:00
pdfRecipients.length + 1,
pdfRecipients.length + 1,
2024-12-31 14:23:14 +01:00
"failed combining pdf"
);
});
2024-12-31 14:23:14 +01:00
this.formatJobEmit(
"complete",
"pdf",
"info",
2024-12-31 14:23:14 +01:00
newsletterId,
2025-04-10 12:45:29 +02:00
pdfRecipients.length + 1,
pdfRecipients.length + 1,
2024-12-31 14:23:14 +01:00
`completed printing process`
);
}
}