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() const embed = new EmbedBuilder()
.setTitle("💰 Daily Reward Claimed!") .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( .addFields(
{ name: "Streak", value: `🔥 ${result.streak} days`, inline: true }, { 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 } { name: "Next Reward", value: `< t:${Math.floor(result.nextReadyAt.getTime() / 1000)}: R > `, inline: true }
) )
.setColor("Gold") .setColor("Gold")

View File

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

View File

@@ -62,6 +62,7 @@ mock.module("@/lib/config", () => ({
daily: { daily: {
amount: 100n, amount: 100n,
streakBonus: 10n, streakBonus: 10n,
weeklyBonus: 50n,
cooldownMs: 86400000, // 24 hours cooldownMs: 86400000, // 24 hours
} }
} }
@@ -139,6 +140,7 @@ describe("economyService", () => {
expect(result.streak).toBe(6); expect(result.streak).toBe(6);
// Base 100 + (6-1)*10 = 150 // Base 100 + (6-1)*10 = 150
expect(result.amount).toBe(150n); expect(result.amount).toBe(150n);
expect(result.isWeekly).toBe(false);
// Check updates // Check updates
expect(mockUpdate).toHaveBeenCalledWith(users); expect(mockUpdate).toHaveBeenCalledWith(users);
@@ -146,6 +148,28 @@ describe("economyService", () => {
expect(mockInsert).toHaveBeenCalledWith(transactions); 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 () => { it("should throw if cooldown is active", async () => {
const future = new Date("2023-01-02T12:00:00Z"); // +24h const future = new Date("2023-01-02T12:00:00Z"); // +24h
mockFindFirst.mockResolvedValue({ expiresAt: future }); mockFindFirst.mockResolvedValue({ expiresAt: future });

View File

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