feat: introduce weekly bonus for daily rewards, updating calculations, configuration, and command UI.

This commit is contained in:
syntaxbullet
2025-12-22 13:16:44 +01:00
parent b7b1dd87b8
commit f859618367
4 changed files with 34 additions and 3 deletions

View File

@@ -15,9 +15,10 @@ export const daily = createCommand({
const embed = new EmbedBuilder()
.setTitle("💰 Daily Reward Claimed!")
.setDescription(`You claimed ** ${result.amount}** Astral Units!`)
.setDescription(`You claimed ** ${result.amount}** Astral Units!${result.isWeekly ? `\n🎉 **Weekly Bonus!** +${result.weeklyBonus} extra!` : ''}`)
.addFields(
{ name: "Streak", value: `🔥 ${result.streak} days`, inline: true },
{ name: "Weekly Progress", value: `${"🟩".repeat(result.streak % 7 || 7)}${"⬜".repeat(7 - (result.streak % 7 || 7))} (${result.streak % 7 || 7}/7)`, inline: true },
{ name: "Next Reward", value: `< t:${Math.floor(result.nextReadyAt.getTime() / 1000)}: R > `, inline: true }
)
.setColor("Gold")

View File

@@ -18,6 +18,7 @@ export interface GameConfigType {
daily: {
amount: bigint;
streakBonus: bigint;
weeklyBonus: bigint;
cooldownMs: number;
},
transfers: {
@@ -79,6 +80,7 @@ const configSchema = z.object({
daily: z.object({
amount: bigIntSchema,
streakBonus: bigIntSchema,
weeklyBonus: bigIntSchema.default(50n),
cooldownMs: z.number(),
}),
transfers: z.object({

View File

@@ -62,6 +62,7 @@ mock.module("@/lib/config", () => ({
daily: {
amount: 100n,
streakBonus: 10n,
weeklyBonus: 50n,
cooldownMs: 86400000, // 24 hours
}
}
@@ -139,6 +140,7 @@ describe("economyService", () => {
expect(result.streak).toBe(6);
// Base 100 + (6-1)*10 = 150
expect(result.amount).toBe(150n);
expect(result.isWeekly).toBe(false);
// Check updates
expect(mockUpdate).toHaveBeenCalledWith(users);
@@ -146,6 +148,28 @@ describe("economyService", () => {
expect(mockInsert).toHaveBeenCalledWith(transactions);
});
it("should claim weekly bonus correctly on 7th day", async () => {
const recentPast = new Date("2023-01-01T11:00:00Z");
mockFindFirst
.mockResolvedValueOnce({ expiresAt: recentPast })
.mockResolvedValueOnce({ id: 1n, dailyStreak: 6, balance: 1000n }); // User currently at 6 days
const result = await economyService.claimDaily("1");
expect(result.claimed).toBe(true);
// Streak should increase: 6 + 1 = 7
expect(result.streak).toBe(7);
// Base: 100
// Streak Bonus: (7-1)*10 = 60
// Weekly Bonus: 50
// Total: 210
expect(result.amount).toBe(210n);
expect(result.isWeekly).toBe(true);
expect(result.weeklyBonus).toBe(50n);
});
it("should throw if cooldown is active", async () => {
const future = new Date("2023-01-02T12:00:00Z"); // +24h
mockFindFirst.mockResolvedValue({ expiresAt: future });

View File

@@ -106,7 +106,11 @@ export const economyService = {
const bonus = (BigInt(streak) - 1n) * config.economy.daily.streakBonus;
const totalReward = config.economy.daily.amount + bonus;
// Weekly bonus check
const isWeeklyCurrent = streak > 0 && streak % 7 === 0;
const weeklyBonusAmount = isWeeklyCurrent ? config.economy.daily.weeklyBonus : 0n;
const totalReward = config.economy.daily.amount + bonus + weeklyBonusAmount;
await txFn.update(users)
.set({
balance: sql`${users.balance} + ${totalReward}`,
@@ -138,7 +142,7 @@ export const economyService = {
description: `Daily reward (Streak: ${streak})`,
});
return { claimed: true, amount: totalReward, streak, nextReadyAt };
return { claimed: true, amount: totalReward, streak, nextReadyAt, isWeekly: isWeeklyCurrent, weeklyBonus: weeklyBonusAmount };
}, tx);
},