import { Worker } from "bullmq"; import { redisConnectionOpts } from "../connection.js"; import { awardPoints, revokePoints, checkAchievements } from "../../lib/gamification.js"; import { db } from "../../db/connection.js"; import { logger } from "../../lib/logger.js"; import type { POINT_VALUES } from "../../lib/gamification.js"; const log = logger.child({ component: "gamification-worker" }); export type GamificationJobData = | { job: "awardPoints"; userId: string; action: keyof typeof POINT_VALUES; recordingId?: string } | { job: "revokePoints"; userId: string; action: keyof typeof POINT_VALUES; recordingId: string } | { job: "checkAchievements"; userId: string; category?: string }; export function startGamificationWorker(): Worker { const worker = new Worker( "gamification", async (bullJob) => { const data = bullJob.data as GamificationJobData; log.info({ jobId: bullJob.id, job: data.job, userId: data.userId }, "Processing gamification job"); switch (data.job) { case "awardPoints": await awardPoints(db, data.userId, data.action, data.recordingId); break; case "revokePoints": await revokePoints(db, data.userId, data.action, data.recordingId); break; case "checkAchievements": await checkAchievements(db, data.userId, data.category); break; default: throw new Error(`Unknown gamification job: ${(data as GamificationJobData).job}`); } log.info({ jobId: bullJob.id, job: data.job }, "Gamification job completed"); }, { connection: redisConnectionOpts }, ); worker.on("failed", (bullJob, err) => { log.error( { jobId: bullJob?.id, job: (bullJob?.data as GamificationJobData)?.job, err: err.message }, "Gamification job failed", ); }); return worker; }