feat: Allow item effects to specify durations in hours, minutes, or seconds.

This commit is contained in:
syntaxbullet
2025-12-15 23:26:51 +01:00
parent d3ade218ec
commit 3a96b67e89
2 changed files with 16 additions and 8 deletions

View File

@@ -15,8 +15,8 @@ export interface Event<K extends keyof ClientEvents> {
export type ItemEffect =
| { type: 'ADD_XP'; amount: number }
| { type: 'ADD_BALANCE'; amount: number }
| { type: 'XP_BOOST'; multiplier: number; durationSeconds: number }
| { type: 'TEMP_ROLE'; roleId: string; durationSeconds: number }
| { type: 'XP_BOOST'; multiplier: number; durationSeconds?: number; durationMinutes?: number; durationHours?: number }
| { type: 'TEMP_ROLE'; roleId: string; durationSeconds?: number; durationMinutes?: number; durationHours?: number }
| { type: 'REPLY_MESSAGE'; message: string };
export interface ItemUsageData {

View File

@@ -7,6 +7,13 @@ import { config } from "@/lib/config";
import { withTransaction } from "@/lib/db";
import type { Transaction, ItemUsageData } from "@/lib/types";
// Helper to extract duration in seconds
const getDuration = (effect: any): number => {
if (effect.durationHours) return effect.durationHours * 3600;
if (effect.durationMinutes) return effect.durationMinutes * 60;
return effect.durationSeconds || 60; // Default to 60s if nothing provided
};
export const inventoryService = {
addItem: async (userId: string, itemId: number, quantity: bigint = 1n, tx?: Transaction) => {
return await withTransaction(async (txFn) => {
@@ -171,7 +178,8 @@ export const inventoryService = {
results.push(effect.message);
break;
case 'XP_BOOST':
const expiresAt = new Date(Date.now() + effect.durationSeconds * 1000);
const boostDuration = getDuration(effect);
const expiresAt = new Date(Date.now() + boostDuration * 1000);
await txFn.insert(userTimers).values({
userId: BigInt(userId),
type: 'EFFECT',
@@ -182,10 +190,11 @@ export const inventoryService = {
target: [userTimers.userId, userTimers.type, userTimers.key],
set: { expiresAt: expiresAt, metadata: { multiplier: effect.multiplier } }
});
results.push(`XP Boost (${effect.multiplier}x) active for ${Math.floor(effect.durationSeconds / 60)}m`);
results.push(`XP Boost (${effect.multiplier}x) active for ${Math.floor(boostDuration / 60)}m`);
break;
case 'TEMP_ROLE':
const roleExpiresAt = new Date(Date.now() + effect.durationSeconds * 1000);
const roleDuration = getDuration(effect);
const roleExpiresAt = new Date(Date.now() + roleDuration * 1000);
await txFn.insert(userTimers).values({
userId: BigInt(userId),
type: 'ACCESS',
@@ -196,9 +205,8 @@ export const inventoryService = {
target: [userTimers.userId, userTimers.type, userTimers.key],
set: { expiresAt: roleExpiresAt }
});
// Actual role assignment happens in the Command layer (or here if we had client, but service shouldn't depend on client ideally)
// We return a flag to let the interaction handler know it needs to assign a role.
results.push(`Temporary Role granted for ${Math.floor(effect.durationSeconds / 60)}m`);
// Actual role assignment happens in the Command layer
results.push(`Temporary Role granted for ${Math.floor(roleDuration / 60)}m`);
break;
}
}