Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 19 additions & 12 deletions backend/src/cronjobs/ghostmode-reminder.cron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ export class GhostModeReminderCronJob {
template: "ghostmode-reminder",
context: {
firstName: user.firstName,
intervalHour: this.i18n.t(intervalHour.translationKey),
intervalHour: this.i18n.t(intervalHour.translationKey, {
lang,
}),
t: (key: string, params?: Record<string, any>) =>
this.i18n.translate(
`main.email.ghostmode-reminder.${key}`,
Expand All @@ -59,16 +61,20 @@ export class GhostModeReminderCronJob {
const now = new Date();
const chunks = 100; // Process users in chunks to prevent memory overload

// Track different reminder intervals
const intervalHours: IntervalHour[] = [
{ hours: 24, translationKey: "main.cron.intervalHours.h24" },
{ hours: 72, translationKey: "main.cron.intervalHours.h72" },
{ hours: 336, translationKey: "main.cron.intervalHours.h336" }, // 14 days * 24 hours
];

const notificationTicketsToSend: OfflineryNotification[] = [];
for (const intervalHour of intervalHours) {

// Process one interval at a time, from shortest to longest
for (let i = 0; i < intervalHours.length; i++) {
const intervalHour = intervalHours[i];
const previousInterval = i > 0 ? intervalHours[i - 1].hours : 0;
let skip = 0;

this.logger.debug(
`Checking users for interval ${intervalHour.hours}h.`,
);
Expand All @@ -78,33 +84,34 @@ export class GhostModeReminderCronJob {
.createQueryBuilder("user")
.where("user.dateMode = :mode", { mode: EDateMode.GHOST })
.andWhere(
"user.lastDateModeChange IS NULL OR user.lastDateModeChange <= :timestamp",
"user.lastDateModeChange IS NULL OR user.lastDateModeChange <= :currentInterval",
{
timestamp: new Date(
currentInterval: new Date(
now.getTime() -
intervalHour.hours * 60 * 60 * 1000,
),
},
)
// Only get users who haven't been reminded yet or were last reminded before the previous interval
.andWhere(
"user.lastDateModeReminderSent IS NULL OR user.lastDateModeReminderSent < :reminderTimestamp",
"(user.lastDateModeReminderSent IS NULL OR user.lastDateModeReminderSent <= :previousInterval)",
{
reminderTimestamp: new Date(
previousInterval: new Date(
now.getTime() -
intervalHour.hours * 60 * 60 * 1000,
previousInterval * 60 * 60 * 1000,
),
},
)
.take(chunks)
.skip(skip)
.getMany();

if (users.length === 0) break;

this.logger.debug(
`Found ${users.length} users that are to be reminded for interval ${intervalHour.hours}h.`,
`Found ${users.length} users to remind for interval ${intervalHour.hours}h.`,
);
if (users.length === 0) break;

// Process users in parallel but with concurrency control
await Promise.all(
users.map(async (user) => {
try {
Expand Down Expand Up @@ -145,7 +152,6 @@ export class GhostModeReminderCronJob {
);
}

// Update last reminder timestamp
await this.userRepository.update(user.id, {
lastDateModeReminderSent: now,
});
Expand All @@ -164,6 +170,7 @@ export class GhostModeReminderCronJob {
`Ghostmode reminders sent for ${intervalHour.hours}h.`,
);
}

const tickets = await this.notificationService.sendPushNotifications(
notificationTicketsToSend,
);
Expand Down
Loading