This commit is contained in:
2026-01-30 15:22:44 +03:00
parent f313ba944a
commit 6fc6ae833b

View File

@@ -5,107 +5,114 @@ import { ScraperService, ScrapedGame } from '../scraper/scraper.service';
@Injectable() @Injectable()
export class SyncService { 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 // Define target URLs - could be moved to Config/DB
private readonly TARGET_URLS = [ private readonly TARGET_URLS = [
'https://insider-gaming.com/calendar/', 'https://insider-gaming.com/calendar/',
// Add other URLs here as needed // Add other URLs here as needed
]; ];
constructor( constructor(
private readonly prisma: PrismaService, private readonly prisma: PrismaService,
private readonly scraper: ScraperService, private readonly scraper: ScraperService,
) { } ) {}
@Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT) @Cron(CronExpression.EVERY_HOUR)
async handleDailySync() { async handleDailySync() {
this.logger.log('Starting daily game synchronization job...'); this.logger.log('Starting daily game synchronization job...');
for (const url of this.TARGET_URLS) { for (const url of this.TARGET_URLS) {
await this.syncUrl(url); await this.syncUrl(url);
} }
this.logger.log('Daily game synchronization job completed.'); 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 parsing standard JS Date
try { const parsed = new Date(clean);
const games = await this.scraper.scrapeUrl(url); if (!isNaN(parsed.getTime())) {
this.logger.log(`Syncing ${games.length} games from ${url}`); date = parsed;
} else {
for (const gameData of games) { // Check for Month Year (e.g. "October 2026") -> Default to 1st of month
await this.upsertGame(gameData); // Check for "Q3 2026"
} isTBD = true; // If we can't get a specific day, marked as TBD-ish or just keep dateText
} catch (error) {
this.logger.error(`Failed to sync URL ${url}: ${error.message}`, error.stack);
}
} }
private async upsertGame(data: ScrapedGame) { return {
const slug = this.generateSlug(data.title); releaseDate: date,
const { releaseDate, isTBD, dateText } = this.parseDate(data.releaseDate); isTBD: date === null,
dateText: clean,
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
};
}
} }