Files
AuroraBot-discord/src/modules/quest/quest.service.ts

88 lines
3.1 KiB
TypeScript

import { quests, userQuests, users } from "@/db/schema";
import { eq, and, sql } from "drizzle-orm";
import { DrizzleClient } from "@/lib/DrizzleClient";
import { economyService } from "@/modules/economy/economy.service";
import { levelingService } from "@/modules/leveling/leveling.service";
import { withTransaction } from "@/lib/db";
import type { Transaction } from "@/lib/types";
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 Error("Quest not assigned");
if (userQuest.completedAt) throw new Error("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, '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,
}
});
}
};