import { Injectable, Logger } from '@nestjs/common'; import { Cron, CronExpression } from '@nestjs/schedule'; import { JobQueue, OneDay, OnJob } from '../../base'; import { Models } from '../../models'; import { CopilotTranscriptionRetryService } from './transcript/retry'; const BACKGROUND_COPILOT_JOB_PRIORITY = 100; declare global { interface Jobs { 'copilot.session.cleanupEmptySessions': {}; 'copilot.session.generateMissingTitles': {}; } } @Injectable() export class CopilotCronJobs { private readonly logger = new Logger(CopilotCronJobs.name); constructor( private readonly models: Models, private readonly jobs: JobQueue, private readonly transcriptRetry: CopilotTranscriptionRetryService ) {} @Cron(CronExpression.EVERY_MINUTE) async reconcileTranscriptDispatches() { await this.transcriptRetry.reconcileDispatches(); } @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT) async dailyCleanupJob() { await this.jobs.add( 'copilot.session.cleanupEmptySessions', {}, { jobId: 'daily-copilot-cleanup-empty-sessions' } ); await this.jobs.add( 'copilot.session.generateMissingTitles', {}, { jobId: 'daily-copilot-generate-missing-titles' } ); } async triggerGenerateMissingTitles() { await this.jobs.add( 'copilot.session.generateMissingTitles', {}, { jobId: 'trigger-copilot-generate-missing-titles' } ); } @OnJob('copilot.session.cleanupEmptySessions') async cleanupEmptySessions() { const { removed, cleaned } = await this.models.copilotSession.cleanupEmptySessions( new Date(Date.now() - OneDay) ); this.logger.log( `Cleanup completed: ${removed} sessions deleted, ${cleaned} sessions marked as deleted` ); } @OnJob('copilot.session.generateMissingTitles') async generateMissingTitles() { const sessions = await this.models.copilotSession.toBeGenerateTitle(); for (const session of sessions) { await this.jobs.add( 'copilot.session.generateTitle', { sessionId: session.id }, { priority: BACKGROUND_COPILOT_JOB_PRIORITY } ); } this.logger.log( `Scheduled title generation for ${sessions.length} sessions` ); } }