feat(automation): add timezone-safe daily briefing dedupe
This commit is contained in:
@@ -17,6 +17,7 @@ export class CronScheduler implements ChannelAdapter {
|
||||
private messageHandler?: (msg: InboundMessage) => void;
|
||||
private cronInstances: Map<string, Cron> = new Map();
|
||||
private jobs: Map<string, CronJobConfig> = new Map();
|
||||
private lastTriggeredLocalDateByJob: Map<string, string> = new Map();
|
||||
|
||||
constructor(
|
||||
private readonly jobConfigs: CronJobConfig[],
|
||||
@@ -89,6 +90,14 @@ export class CronScheduler implements ChannelAdapter {
|
||||
triggerJob(jobName: string): void {
|
||||
const job = this.jobs.get(jobName);
|
||||
if (!job) {return;}
|
||||
if (job.once_per_local_day) {
|
||||
const dayKey = this.localDayKey(Date.now(), job.timezone);
|
||||
const lastDayKey = this.lastTriggeredLocalDateByJob.get(jobName);
|
||||
if (lastDayKey === dayKey) {
|
||||
return;
|
||||
}
|
||||
this.lastTriggeredLocalDateByJob.set(jobName, dayKey);
|
||||
}
|
||||
const runId = `run-${randomUUID()}`;
|
||||
const senderId = this.deliveryMode === 'isolated_job' ? `${jobName}:${runId}` : jobName;
|
||||
|
||||
@@ -174,9 +183,37 @@ export class CronScheduler implements ChannelAdapter {
|
||||
}
|
||||
|
||||
this.jobs.delete(name);
|
||||
this.lastTriggeredLocalDateByJob.delete(name);
|
||||
|
||||
auditLogger?.cronRemove(name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private localDayKey(timestamp: number, timezone?: string): string {
|
||||
const date = new Date(timestamp);
|
||||
const format = (timeZoneValue?: string): string => {
|
||||
const parts = new Intl.DateTimeFormat('en-US', {
|
||||
timeZone: timeZoneValue,
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
}).formatToParts(date);
|
||||
const year = parts.find((part) => part.type === 'year')?.value ?? '0000';
|
||||
const month = parts.find((part) => part.type === 'month')?.value ?? '00';
|
||||
const day = parts.find((part) => part.type === 'day')?.value ?? '00';
|
||||
return `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
if (!timezone) {
|
||||
return format();
|
||||
}
|
||||
|
||||
try {
|
||||
return format(timezone);
|
||||
} catch {
|
||||
console.warn(`CronScheduler: invalid timezone '${timezone}' for job local-day dedupe; falling back to system timezone`);
|
||||
return format();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user