Files
AuroraBot-discord/shared/modules/quest/quest.service.ts
2026-01-08 16:09:26 +01:00

89 lines
3.3 KiB
TypeScript

import { userQuests } from "@db/schema";
import { eq, and } from "drizzle-orm";
import { UserError } from "@/lib/errors";
import { DrizzleClient } from "@shared/db/DrizzleClient";
import { economyService } from "@shared/modules/economy/economy.service";
import { levelingService } from "@shared/modules/leveling/leveling.service";
import { withTransaction } from "@/lib/db";
import type { Transaction } from "@shared/lib/types";
import { TransactionType } from "@shared/lib/constants";
export const questService = {
assignQuest: async (userId: string, questId: number, tx?: Transaction) => {
return await withTransaction(async (txFn) => {
return await txFn.insert(userQuests)
.values({
userId: BigInt(userId),
questId: questId,
progress: 0,
})
.onConflictDoNothing() // Ignore if already assigned
.returning();
}, tx);
},
updateProgress: async (userId: string, questId: number, progress: number, tx?: Transaction) => {
return await withTransaction(async (txFn) => {
return await txFn.update(userQuests)
.set({ progress: progress })
.where(and(
eq(userQuests.userId, BigInt(userId)),
eq(userQuests.questId, questId)
))
.returning();
}, tx);
},
completeQuest: async (userId: string, questId: number, tx?: Transaction) => {
return await withTransaction(async (txFn) => {
const userQuest = await txFn.query.userQuests.findFirst({
where: and(
eq(userQuests.userId, BigInt(userId)),
eq(userQuests.questId, questId)
),
with: {
quest: true,
}
});
if (!userQuest) throw new UserError("Quest not assigned");
if (userQuest.completedAt) throw new UserError("Quest already completed");
// Mark completed
await txFn.update(userQuests)
.set({ completedAt: new Date() })
.where(and(
eq(userQuests.userId, BigInt(userId)),
eq(userQuests.questId, questId)
));
// Distribute Rewards
const rewards = userQuest.quest.rewards as { xp?: number, balance?: number };
const results = { xp: 0n, balance: 0n };
if (rewards?.balance) {
const bal = BigInt(rewards.balance);
await economyService.modifyUserBalance(userId, bal, TransactionType.QUEST_REWARD, `Reward for quest ${questId}`, null, txFn);
results.balance = bal;
}
if (rewards?.xp) {
const xp = BigInt(rewards.xp);
await levelingService.addXp(userId, xp, txFn);
results.xp = xp;
}
return { success: true, rewards: results };
}, tx);
},
getUserQuests: async (userId: string) => {
return await DrizzleClient.query.userQuests.findMany({
where: eq(userQuests.userId, BigInt(userId)),
with: {
quest: true,
}
});
}
};