Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6fc6ae833b |
@@ -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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user