diff --git a/src/modules/sync/sync.service.ts b/src/modules/sync/sync.service.ts index 5643044..e1354fe 100644 --- a/src/modules/sync/sync.service.ts +++ b/src/modules/sync/sync.service.ts @@ -5,107 +5,114 @@ import { ScraperService, ScrapedGame } from '../scraper/scraper.service'; @Injectable() export class SyncService { - private readonly logger = new Logger(SyncService.name); + private readonly logger = new Logger(SyncService.name); - // Define target URLs - could be moved to Config/DB - private readonly TARGET_URLS = [ - 'https://insider-gaming.com/calendar/', - // Add other URLs here as needed - ]; + // Define target URLs - could be moved to Config/DB + private readonly TARGET_URLS = [ + 'https://insider-gaming.com/calendar/', + // Add other URLs here as needed + ]; - constructor( - private readonly prisma: PrismaService, - private readonly scraper: ScraperService, - ) { } + constructor( + private readonly prisma: PrismaService, + private readonly scraper: ScraperService, + ) {} - @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT) - async handleDailySync() { - this.logger.log('Starting daily game synchronization job...'); - for (const url of this.TARGET_URLS) { - await this.syncUrl(url); - } - this.logger.log('Daily game synchronization job completed.'); + @Cron(CronExpression.EVERY_HOUR) + async handleDailySync() { + this.logger.log('Starting daily game synchronization job...'); + for (const url of this.TARGET_URLS) { + await this.syncUrl(url); + } + this.logger.log('Daily game synchronization job completed.'); + } + + async syncUrl(url: string) { + try { + const games = await this.scraper.scrapeUrl(url); + this.logger.log(`Syncing ${games.length} games from ${url}`); + + for (const gameData of games) { + await this.upsertGame(gameData); + } + } catch (error) { + this.logger.error( + `Failed to sync URL ${url}: ${error.message}`, + error.stack, + ); + } + } + + private async upsertGame(data: ScrapedGame) { + const slug = this.generateSlug(data.title); + const { releaseDate, isTBD, dateText } = this.parseDate(data.releaseDate); + + try { + // Simplistic Upsert + await this.prisma.game.upsert({ + where: { slug: slug }, + update: { + title: data.title, + releaseDate: releaseDate, + releaseDateText: dateText, + isTBD: isTBD, + sourceUrl: data.sourceUrl, + // TODO: Handle platform mapping if needed + }, + create: { + title: data.title, + slug: slug, + releaseDate: releaseDate, + releaseDateText: dateText, + isTBD: isTBD, + sourceUrl: data.sourceUrl, + }, + }); + // this.logger.debug(`Upserted game: ${data.title}`); + } catch (error) { + this.logger.error(`Error saving game ${data.title}: ${error.message}`); + } + } + + private generateSlug(title: string): string { + return title + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/(^-|-$)+/g, ''); + } + + private parseDate(rawDate?: string): { + releaseDate: Date | null; + isTBD: boolean; + dateText: string | null; + } { + if (!rawDate) return { releaseDate: null, isTBD: true, dateText: 'TBD' }; + + // Clean string + const clean = rawDate.trim(); + let date: Date | null = null; + let isTBD = false; + + // Check for TBD/TBA + if (clean.match(/tbd|tba|to be announced/i)) { + isTBD = true; + return { releaseDate: null, isTBD, dateText: clean }; } - async syncUrl(url: string) { - try { - const games = await this.scraper.scrapeUrl(url); - this.logger.log(`Syncing ${games.length} games from ${url}`); - - for (const gameData of games) { - await this.upsertGame(gameData); - } - } catch (error) { - this.logger.error(`Failed to sync URL ${url}: ${error.message}`, error.stack); - } + // Try parsing standard JS Date + const parsed = new Date(clean); + if (!isNaN(parsed.getTime())) { + date = parsed; + } else { + // Check for Month Year (e.g. "October 2026") -> Default to 1st of month + // Check for "Q3 2026" + isTBD = true; // If we can't get a specific day, marked as TBD-ish or just keep dateText } - private async upsertGame(data: ScrapedGame) { - const slug = this.generateSlug(data.title); - const { releaseDate, isTBD, dateText } = this.parseDate(data.releaseDate); - - try { - // Simplistic Upsert - await this.prisma.game.upsert({ - where: { slug: slug }, - update: { - title: data.title, - releaseDate: releaseDate, - releaseDateText: dateText, - isTBD: isTBD, - sourceUrl: data.sourceUrl, - // TODO: Handle platform mapping if needed - }, - create: { - title: data.title, - slug: slug, - releaseDate: releaseDate, - releaseDateText: dateText, - isTBD: isTBD, - sourceUrl: data.sourceUrl, - } - }); - // this.logger.debug(`Upserted game: ${data.title}`); - } catch (error) { - this.logger.error(`Error saving game ${data.title}: ${error.message}`); - } - } - - private generateSlug(title: string): string { - return title - .toLowerCase() - .replace(/[^a-z0-9]+/g, '-') - .replace(/(^-|-$)+/g, ''); - } - - private parseDate(rawDate?: string): { releaseDate: Date | null, isTBD: boolean, dateText: string | null } { - if (!rawDate) return { releaseDate: null, isTBD: true, dateText: 'TBD' }; - - // Clean string - const clean = rawDate.trim(); - let date: Date | null = null; - let isTBD = false; - - // Check for TBD/TBA - if (clean.match(/tbd|tba|to be announced/i)) { - isTBD = true; - return { releaseDate: null, isTBD, dateText: clean }; - } - - // Try parsing standard JS Date - const parsed = new Date(clean); - if (!isNaN(parsed.getTime())) { - date = parsed; - } else { - // Check for Month Year (e.g. "October 2026") -> Default to 1st of month - // Check for "Q3 2026" - isTBD = true; // If we can't get a specific day, marked as TBD-ish or just keep dateText - } - - return { - releaseDate: date, - isTBD: date === null, - dateText: clean - }; - } + return { + releaseDate: date, + isTBD: date === null, + dateText: clean, + }; + } }