diff --git a/src/commands/economy/exam.ts b/src/commands/economy/exam.ts index 5ec6f3e..c259678 100644 --- a/src/commands/economy/exam.ts +++ b/src/commands/economy/exam.ts @@ -7,8 +7,9 @@ import { userTimers, users } from "@/db/schema"; import { eq, and, sql } from "drizzle-orm"; import { DrizzleClient } from "@/lib/DrizzleClient"; import { config } from "@lib/config"; +import { TimerType } from "@/lib/constants"; -const EXAM_TIMER_TYPE = 'EXAM_SYSTEM'; +const EXAM_TIMER_TYPE = TimerType.EXAM_SYSTEM; const EXAM_TIMER_KEY = 'default'; interface ExamMetadata { diff --git a/src/lib/constants.ts b/src/lib/constants.ts new file mode 100644 index 0000000..d022988 --- /dev/null +++ b/src/lib/constants.ts @@ -0,0 +1,65 @@ +/** + * Global Constants and Enums + */ + +export enum TimerType { + COOLDOWN = 'COOLDOWN', + EFFECT = 'EFFECT', + ACCESS = 'ACCESS', + EXAM_SYSTEM = 'EXAM_SYSTEM', +} + +export enum EffectType { + ADD_XP = 'ADD_XP', + ADD_BALANCE = 'ADD_BALANCE', + REPLY_MESSAGE = 'REPLY_MESSAGE', + XP_BOOST = 'XP_BOOST', + TEMP_ROLE = 'TEMP_ROLE', + COLOR_ROLE = 'COLOR_ROLE', + LOOTBOX = 'LOOTBOX', +} + +export enum TransactionType { + TRANSFER_IN = 'TRANSFER_IN', + TRANSFER_OUT = 'TRANSFER_OUT', + DAILY_REWARD = 'DAILY_REWARD', + ITEM_USE = 'ITEM_USE', + LOOTBOX = 'LOOTBOX', + EXAM_REWARD = 'EXAM_REWARD', + PURCHASE = 'PURCHASE', + TRADE_IN = 'TRADE_IN', + TRADE_OUT = 'TRADE_OUT', + QUEST_REWARD = 'QUEST_REWARD', +} + +export enum ItemTransactionType { + TRADE_IN = 'TRADE_IN', + TRADE_OUT = 'TRADE_OUT', + SHOP_BUY = 'SHOP_BUY', + DROP = 'DROP', + GIVE = 'GIVE', + USE = 'USE', +} + +export enum ItemType { + MATERIAL = 'MATERIAL', + CONSUMABLE = 'CONSUMABLE', + EQUIPMENT = 'EQUIPMENT', + QUEST = 'QUEST', +} + +export enum CaseType { + WARN = 'warn', + TIMEOUT = 'timeout', + KICK = 'kick', + BAN = 'ban', + NOTE = 'note', + PRUNE = 'prune', +} + +export enum LootType { + NOTHING = 'NOTHING', + CURRENCY = 'CURRENCY', + XP = 'XP', + ITEM = 'ITEM', +} diff --git a/src/lib/types.ts b/src/lib/types.ts index 0b6d748..cc03368 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,4 +1,6 @@ import type { AutocompleteInteraction, ChatInputCommandInteraction, ClientEvents, SlashCommandBuilder, SlashCommandOptionsOnlyBuilder, SlashCommandSubcommandsOnlyBuilder } from "discord.js"; +import { LootType, EffectType } from "./constants"; +import { DrizzleClient } from "./DrizzleClient"; export interface Command { data: SlashCommandBuilder | SlashCommandOptionsOnlyBuilder | SlashCommandSubcommandsOnlyBuilder; @@ -14,16 +16,16 @@ export interface Event { } export type ItemEffect = - | { type: 'ADD_XP'; amount: number } - | { type: 'ADD_BALANCE'; amount: 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 } - | { type: 'COLOR_ROLE'; roleId: string } - | { type: 'LOOTBOX'; pool: LootTableItem[] }; + | { type: EffectType.ADD_XP; amount: number } + | { type: EffectType.ADD_BALANCE; amount: number } + | { type: EffectType.XP_BOOST; multiplier: number; durationSeconds?: number; durationMinutes?: number; durationHours?: number } + | { type: EffectType.TEMP_ROLE; roleId: string; durationSeconds?: number; durationMinutes?: number; durationHours?: number } + | { type: EffectType.REPLY_MESSAGE; message: string } + | { type: EffectType.COLOR_ROLE; roleId: string } + | { type: EffectType.LOOTBOX; pool: LootTableItem[] }; export interface LootTableItem { - type: 'CURRENCY' | 'ITEM' | 'XP' | 'NOTHING'; + type: LootType; weight: number; amount?: number; // For CURRENCY, XP itemId?: number; // For ITEM @@ -37,7 +39,5 @@ export interface ItemUsageData { effects: ItemEffect[]; } -import { DrizzleClient } from "./DrizzleClient"; - export type DbClient = typeof DrizzleClient; export type Transaction = Parameters[0]>[0]; diff --git a/src/modules/admin/item_wizard.ts b/src/modules/admin/item_wizard.ts index c55bfcf..19ef10f 100644 --- a/src/modules/admin/item_wizard.ts +++ b/src/modules/admin/item_wizard.ts @@ -4,6 +4,7 @@ import { DrizzleClient } from "@/lib/DrizzleClient"; import type { ItemUsageData, ItemEffect } from "@/lib/types"; import { getItemWizardEmbed, getItemTypeSelection, getEffectTypeSelection, getDetailsModal, getEconomyModal, getVisualsModal, getEffectConfigModal } from "./item_wizard.view"; import type { DraftItem } from "./item_wizard.types"; +import { ItemType, EffectType } from "@/lib/constants"; // --- Types --- @@ -23,7 +24,7 @@ export const renderWizard = (userId: string, isDraft = true) => { name: "New Item", description: "No description", rarity: "Common", - type: "MATERIAL", + type: ItemType.MATERIAL, price: null, iconUrl: "", imageUrl: "", @@ -176,26 +177,26 @@ export const handleItemWizardInteraction = async (interaction: Interaction) => { if (type) { let effect: ItemEffect | null = null; - if (type === "ADD_XP" || type === "ADD_BALANCE") { + if (type === EffectType.ADD_XP || type === EffectType.ADD_BALANCE) { const amount = parseInt(interaction.fields.getTextInputValue("amount")); if (!isNaN(amount)) effect = { type: type as any, amount }; } - else if (type === "REPLY_MESSAGE") { - effect = { type: "REPLY_MESSAGE", message: interaction.fields.getTextInputValue("message") }; + else if (type === EffectType.REPLY_MESSAGE) { + effect = { type: EffectType.REPLY_MESSAGE, message: interaction.fields.getTextInputValue("message") }; } - else if (type === "XP_BOOST") { + else if (type === EffectType.XP_BOOST) { const multiplier = parseFloat(interaction.fields.getTextInputValue("multiplier")); const duration = parseInt(interaction.fields.getTextInputValue("duration")); - if (!isNaN(multiplier) && !isNaN(duration)) effect = { type: "XP_BOOST", multiplier, durationSeconds: duration }; + if (!isNaN(multiplier) && !isNaN(duration)) effect = { type: EffectType.XP_BOOST, multiplier, durationSeconds: duration }; } - else if (type === "TEMP_ROLE") { + else if (type === EffectType.TEMP_ROLE) { const roleId = interaction.fields.getTextInputValue("role_id"); const duration = parseInt(interaction.fields.getTextInputValue("duration")); - if (roleId && !isNaN(duration)) effect = { type: "TEMP_ROLE", roleId: roleId, durationSeconds: duration }; + if (roleId && !isNaN(duration)) effect = { type: EffectType.TEMP_ROLE, roleId: roleId, durationSeconds: duration }; } - else if (type === "COLOR_ROLE") { + else if (type === EffectType.COLOR_ROLE) { const roleId = interaction.fields.getTextInputValue("role_id"); - if (roleId) effect = { type: "COLOR_ROLE", roleId: roleId }; + if (roleId) effect = { type: EffectType.COLOR_ROLE, roleId: roleId }; } if (effect) { diff --git a/src/modules/admin/item_wizard.view.ts b/src/modules/admin/item_wizard.view.ts index 8de5d19..66b75f7 100644 --- a/src/modules/admin/item_wizard.view.ts +++ b/src/modules/admin/item_wizard.view.ts @@ -10,12 +10,13 @@ import { } from "discord.js"; import { createBaseEmbed } from "@lib/embeds"; import type { DraftItem } from "./item_wizard.types"; +import { ItemType } from "@/lib/constants"; const getItemTypeOptions = () => [ - { label: "Material", value: "MATERIAL", description: "Used for crafting or trading" }, - { label: "Consumable", value: "CONSUMABLE", description: "Can be used to gain effects" }, - { label: "Equipment", value: "EQUIPMENT", description: "Can be equipped (Not yet implemented)" }, - { label: "Quest Item", value: "QUEST", description: "Required for quests" }, + { label: "Material", value: ItemType.MATERIAL, description: "Used for crafting or trading" }, + { label: "Consumable", value: ItemType.CONSUMABLE, description: "Can be used to gain effects" }, + { label: "Equipment", value: ItemType.EQUIPMENT, description: "Can be equipped (Not yet implemented)" }, + { label: "Quest Item", value: ItemType.QUEST, description: "Required for quests" }, ]; const getEffectTypeOptions = () => [ diff --git a/src/modules/economy/economy.service.ts b/src/modules/economy/economy.service.ts index 66ea32a..8848ef9 100644 --- a/src/modules/economy/economy.service.ts +++ b/src/modules/economy/economy.service.ts @@ -4,6 +4,7 @@ import { config } from "@/lib/config"; import { withTransaction } from "@/lib/db"; import type { Transaction } from "@/lib/types"; import { UserError } from "@/lib/errors"; +import { TimerType, TransactionType } from "@/lib/constants"; export const economyService = { transfer: async (fromUserId: string, toUserId: string, amount: bigint, tx?: Transaction) => { @@ -48,7 +49,7 @@ export const economyService = { await txFn.insert(transactions).values({ userId: BigInt(fromUserId), amount: -amount, - type: 'TRANSFER_OUT', + type: TransactionType.TRANSFER_OUT, description: `Transfer to ${toUserId}`, }); @@ -56,7 +57,7 @@ export const economyService = { await txFn.insert(transactions).values({ userId: BigInt(toUserId), amount: amount, - type: 'TRANSFER_IN', + type: TransactionType.TRANSFER_IN, description: `Transfer from ${fromUserId}`, }); @@ -74,7 +75,7 @@ export const economyService = { const cooldown = await txFn.query.userTimers.findFirst({ where: and( eq(userTimers.userId, BigInt(userId)), - eq(userTimers.type, 'COOLDOWN'), + eq(userTimers.type, TimerType.COOLDOWN), eq(userTimers.key, 'daily') ), }); @@ -127,7 +128,7 @@ export const economyService = { await txFn.insert(userTimers) .values({ userId: BigInt(userId), - type: 'COOLDOWN', + type: TimerType.COOLDOWN, key: 'daily', expiresAt: nextReadyAt, }) @@ -140,7 +141,7 @@ export const economyService = { await txFn.insert(transactions).values({ userId: BigInt(userId), amount: totalReward, - type: 'DAILY_REWARD', + type: TransactionType.DAILY_REWARD, description: `Daily reward (Streak: ${streak})`, }); diff --git a/src/modules/inventory/effects/handlers.ts b/src/modules/inventory/effects/handlers.ts index 846d378..a04fbdb 100644 --- a/src/modules/inventory/effects/handlers.ts +++ b/src/modules/inventory/effects/handlers.ts @@ -5,6 +5,7 @@ import type { EffectHandler } from "./types"; import type { LootTableItem } from "@/lib/types"; import { inventoryService } from "@/modules/inventory/inventory.service"; import { inventory, items } from "@/db/schema"; +import { TimerType, TransactionType, LootType } from "@/lib/constants"; // Helper to extract duration in seconds @@ -20,7 +21,7 @@ export const handleAddXp: EffectHandler = async (userId, effect, txFn) => { }; export const handleAddBalance: EffectHandler = async (userId, effect, txFn) => { - await economyService.modifyUserBalance(userId, BigInt(effect.amount), 'ITEM_USE', `Used Item`, null, txFn); + await economyService.modifyUserBalance(userId, BigInt(effect.amount), TransactionType.ITEM_USE, `Used Item`, null, txFn); return `Gained ${effect.amount} 🪙`; }; @@ -33,7 +34,7 @@ export const handleXpBoost: EffectHandler = async (userId, effect, txFn) => { const expiresAt = new Date(Date.now() + boostDuration * 1000); await txFn.insert(userTimers).values({ userId: BigInt(userId), - type: 'EFFECT', + type: TimerType.EFFECT, key: 'xp_boost', expiresAt: expiresAt, metadata: { multiplier: effect.multiplier } @@ -49,7 +50,7 @@ export const handleTempRole: EffectHandler = async (userId, effect, txFn) => { const roleExpiresAt = new Date(Date.now() + roleDuration * 1000); await txFn.insert(userTimers).values({ userId: BigInt(userId), - type: 'ACCESS', + type: TimerType.ACCESS, key: `role_${effect.roleId}`, expiresAt: roleExpiresAt, metadata: { roleId: effect.roleId } @@ -84,22 +85,22 @@ export const handleLootbox: EffectHandler = async (userId, effect, txFn) => { if (!winner) return "The box is empty..."; // Should not happen // Process Winner - if (winner.type === 'NOTHING') { + if (winner.type === LootType.NOTHING) { return winner.message || "You found nothing inside."; } - if (winner.type === 'CURRENCY') { + if (winner.type === LootType.CURRENCY) { let amount = winner.amount || 0; if (winner.minAmount && winner.maxAmount) { amount = Math.floor(Math.random() * (winner.maxAmount - winner.minAmount + 1)) + winner.minAmount; } if (amount > 0) { - await economyService.modifyUserBalance(userId, BigInt(amount), 'LOOTBOX', 'Lootbox Reward', null, txFn); + await economyService.modifyUserBalance(userId, BigInt(amount), TransactionType.LOOTBOX, 'Lootbox Reward', null, txFn); return winner.message || `You found ${amount} 🪙!`; } } - if (winner.type === 'XP') { + if (winner.type === LootType.XP) { let amount = winner.amount || 0; if (winner.minAmount && winner.maxAmount) { amount = Math.floor(Math.random() * (winner.maxAmount - winner.minAmount + 1)) + winner.minAmount; @@ -110,7 +111,7 @@ export const handleLootbox: EffectHandler = async (userId, effect, txFn) => { } } - if (winner.type === 'ITEM') { + if (winner.type === LootType.ITEM) { if (winner.itemId) { const quantity = BigInt(winner.amount || 1); diff --git a/src/modules/inventory/inventory.service.ts b/src/modules/inventory/inventory.service.ts index 9f4ad26..5964c58 100644 --- a/src/modules/inventory/inventory.service.ts +++ b/src/modules/inventory/inventory.service.ts @@ -7,6 +7,7 @@ import { config } from "@/lib/config"; import { UserError } from "@/lib/errors"; import { withTransaction } from "@/lib/db"; import type { Transaction, ItemUsageData } from "@/lib/types"; +import { TransactionType } from "@/lib/constants"; @@ -121,7 +122,7 @@ export const inventoryService = { const totalPrice = item.price * quantity; // Deduct Balance using economy service (passing tx ensures atomicity) - await economyService.modifyUserBalance(userId, -totalPrice, 'PURCHASE', `Bought ${quantity}x ${item.name}`, null, txFn); + await economyService.modifyUserBalance(userId, -totalPrice, TransactionType.PURCHASE, `Bought ${quantity}x ${item.name}`, null, txFn); await inventoryService.addItem(userId, itemId, quantity, txFn); diff --git a/src/modules/inventory/inventory.view.ts b/src/modules/inventory/inventory.view.ts index 5569b20..2c796b7 100644 --- a/src/modules/inventory/inventory.view.ts +++ b/src/modules/inventory/inventory.view.ts @@ -1,5 +1,6 @@ import { EmbedBuilder } from "discord.js"; import type { ItemUsageData } from "@/lib/types"; +import { EffectType } from "@/lib/constants"; /** * Inventory entry with item details @@ -34,7 +35,7 @@ export function getItemUseResultEmbed(results: string[], item?: { name: string, const description = results.map(r => `• ${r}`).join("\n"); // Check if it was a lootbox - const isLootbox = item?.usageData?.effects?.some((e: any) => e.type === 'LOOTBOX'); + const isLootbox = item?.usageData?.effects?.some((e: any) => e.type === EffectType.LOOTBOX); const embed = new EmbedBuilder() .setDescription(description) diff --git a/src/modules/leveling/leveling.service.ts b/src/modules/leveling/leveling.service.ts index 9e8d570..06ebbda 100644 --- a/src/modules/leveling/leveling.service.ts +++ b/src/modules/leveling/leveling.service.ts @@ -3,6 +3,7 @@ import { eq, sql, and } from "drizzle-orm"; import { withTransaction } from "@/lib/db"; import { config } from "@/lib/config"; import type { Transaction } from "@/lib/types"; +import { TimerType } from "@/lib/constants"; export const levelingService = { // Calculate total XP required to REACH a specific level (Cumulative) @@ -78,7 +79,7 @@ export const levelingService = { const cooldown = await txFn.query.userTimers.findFirst({ where: and( eq(userTimers.userId, BigInt(id)), - eq(userTimers.type, 'COOLDOWN'), + eq(userTimers.type, TimerType.COOLDOWN), eq(userTimers.key, 'chat_xp') ), }); @@ -95,7 +96,7 @@ export const levelingService = { const xpBoost = await txFn.query.userTimers.findFirst({ where: and( eq(userTimers.userId, BigInt(id)), - eq(userTimers.type, 'EFFECT'), + eq(userTimers.type, TimerType.EFFECT), eq(userTimers.key, 'xp_boost') ) }); @@ -114,7 +115,7 @@ export const levelingService = { await txFn.insert(userTimers) .values({ userId: BigInt(id), - type: 'COOLDOWN', + type: TimerType.COOLDOWN, key: 'chat_xp', expiresAt: nextReadyAt, }) diff --git a/src/modules/moderation/moderation.service.test.ts b/src/modules/moderation/moderation.service.test.ts index 500fa86..ad23ada 100644 --- a/src/modules/moderation/moderation.service.test.ts +++ b/src/modules/moderation/moderation.service.test.ts @@ -2,6 +2,7 @@ import { describe, it, expect, mock, beforeEach } from "bun:test"; import { ModerationService } from "./moderation.service"; import { moderationCases } from "@/db/schema"; +import { CaseType } from "@/lib/constants"; // Mock Drizzle Functions const mockFindFirst = mock(); @@ -83,7 +84,7 @@ describe("ModerationService", () => { it("should issue a warning and attempt to DM the user", async () => { mockFindFirst.mockResolvedValue({ caseId: "CASE-0001" }); mockReturning.mockResolvedValue([{ caseId: "CASE-0002" }]); - mockFindMany.mockResolvedValue([{ type: 'warn', active: true }]); // 1 warning total + mockFindMany.mockResolvedValue([{ type: CaseType.WARN, active: true }]); // 1 warning total const mockDmTarget = { send: mock() }; @@ -178,7 +179,7 @@ describe("ModerationService", () => { mockFindFirst.mockResolvedValue({ caseId: "CASE-0001" }); const mockNewCase = { caseId: "CASE-0002", - type: 'warn', + type: CaseType.WARN, userId: 123456789n, username: "testuser", moderatorId: 987654321n, @@ -190,7 +191,7 @@ describe("ModerationService", () => { mockReturning.mockResolvedValue([mockNewCase]); const result = await ModerationService.createCase({ - type: 'warn', + type: CaseType.WARN, userId: "123456789", username: "testuser", moderatorId: "987654321", @@ -202,7 +203,7 @@ describe("ModerationService", () => { expect(mockInsert).toHaveBeenCalled(); expect(mockValues).toHaveBeenCalledWith(expect.objectContaining({ caseId: "CASE-0002", - type: 'warn', + type: CaseType.WARN, userId: 123456789n, reason: "test reason" })); @@ -213,7 +214,7 @@ describe("ModerationService", () => { mockReturning.mockImplementation((values) => [values]); // Simplified mock const result = await ModerationService.createCase({ - type: 'ban', + type: CaseType.BAN, userId: "123456789", username: "testuser", moderatorId: "987654321", @@ -273,8 +274,8 @@ describe("ModerationService", () => { describe("getActiveWarningCount", () => { it("should return the number of active warnings", async () => { mockFindMany.mockResolvedValue([ - { id: 1n, type: 'warn', active: true }, - { id: 2n, type: 'warn', active: true } + { id: 1n, type: CaseType.WARN, active: true }, + { id: 2n, type: CaseType.WARN, active: true } ]); const count = await ModerationService.getActiveWarningCount("123456789"); diff --git a/src/modules/moderation/moderation.service.ts b/src/modules/moderation/moderation.service.ts index 5937cd6..1b577c8 100644 --- a/src/modules/moderation/moderation.service.ts +++ b/src/modules/moderation/moderation.service.ts @@ -4,6 +4,7 @@ import { DrizzleClient } from "@/lib/DrizzleClient"; import type { CreateCaseOptions, ClearCaseOptions, SearchCasesFilter } from "./moderation.types"; import { config } from "@/lib/config"; import { getUserWarningEmbed } from "./moderation.view"; +import { CaseType } from "@/lib/constants"; export class ModerationService { /** @@ -43,7 +44,7 @@ export class ModerationService { moderatorName: options.moderatorName, reason: options.reason, metadata: options.metadata || {}, - active: options.type === 'warn' ? true : false, // Only warnings are "active" by default + active: options.type === CaseType.WARN ? true : false, // Only warnings are "active" by default }).returning(); return newCase; @@ -63,7 +64,7 @@ export class ModerationService { timeoutTarget?: { timeout: (duration: number, reason: string) => Promise }; }) { const moderationCase = await this.createCase({ - type: 'warn', + type: CaseType.WARN, userId: options.userId, username: options.username, moderatorId: options.moderatorId, @@ -105,7 +106,7 @@ export class ModerationService { // Create a timeout case await this.createCase({ - type: 'timeout', + type: CaseType.TIMEOUT, userId: options.userId, username: options.username, moderatorId: "0", // System/Bot @@ -154,7 +155,7 @@ export class ModerationService { return await DrizzleClient.query.moderationCases.findMany({ where: and( eq(moderationCases.userId, BigInt(userId)), - eq(moderationCases.type, 'warn'), + eq(moderationCases.type, CaseType.WARN), eq(moderationCases.active, true) ), orderBy: [desc(moderationCases.createdAt)], @@ -168,7 +169,7 @@ export class ModerationService { return await DrizzleClient.query.moderationCases.findMany({ where: and( eq(moderationCases.userId, BigInt(userId)), - eq(moderationCases.type, 'note') + eq(moderationCases.type, CaseType.NOTE) ), orderBy: [desc(moderationCases.createdAt)], }); diff --git a/src/modules/moderation/moderation.types.ts b/src/modules/moderation/moderation.types.ts index 98f3805..5fa78c7 100644 --- a/src/modules/moderation/moderation.types.ts +++ b/src/modules/moderation/moderation.types.ts @@ -1,4 +1,6 @@ -export type CaseType = 'warn' | 'timeout' | 'kick' | 'ban' | 'note' | 'prune'; +import { CaseType } from "@/lib/constants"; + +export { CaseType }; export interface CreateCaseOptions { type: CaseType; diff --git a/src/modules/quest/quest.service.ts b/src/modules/quest/quest.service.ts index 490ed15..ec1d9fb 100644 --- a/src/modules/quest/quest.service.ts +++ b/src/modules/quest/quest.service.ts @@ -6,6 +6,7 @@ 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"; +import { TransactionType } from "@/lib/constants"; export const questService = { assignQuest: async (userId: string, questId: number, tx?: Transaction) => { @@ -62,7 +63,7 @@ export const questService = { if (rewards?.balance) { const bal = BigInt(rewards.balance); - await economyService.modifyUserBalance(userId, bal, 'QUEST_REWARD', `Reward for quest ${questId}`, null, txFn); + await economyService.modifyUserBalance(userId, bal, TransactionType.QUEST_REWARD, `Reward for quest ${questId}`, null, txFn); results.balance = bal; } diff --git a/src/modules/system/cleanup.service.ts b/src/modules/system/cleanup.service.ts index cbbc2ea..43d8496 100644 --- a/src/modules/system/cleanup.service.ts +++ b/src/modules/system/cleanup.service.ts @@ -4,6 +4,7 @@ import { DrizzleClient } from "@/lib/DrizzleClient"; import { AuroraClient } from "@/lib/BotClient"; import { env } from "@/lib/env"; import { config } from "@/lib/config"; +import { TimerType } from "@/lib/constants"; export const cleanupService = { /** @@ -57,7 +58,7 @@ export const cleanupService = { // This is migrated from scheduler.ts const expiredAccess = await DrizzleClient.query.userTimers.findMany({ where: and( - eq(userTimers.type, 'ACCESS'), + eq(userTimers.type, TimerType.ACCESS), lt(userTimers.expiresAt, now) ) }); diff --git a/src/modules/trade/trade.service.ts b/src/modules/trade/trade.service.ts index 9df41cb..c3b4ee4 100644 --- a/src/modules/trade/trade.service.ts +++ b/src/modules/trade/trade.service.ts @@ -4,6 +4,7 @@ import { inventoryService } from "@/modules/inventory/inventory.service"; import { itemTransactions } from "@/db/schema"; import { withTransaction } from "@/lib/db"; import type { Transaction } from "@/lib/types"; +import { TransactionType, ItemTransactionType } from "@/lib/constants"; // Module-level session storage const sessions = new Map(); @@ -25,7 +26,7 @@ const processTransfer = async (tx: Transaction, from: TradeParticipant, to: Trad await economyService.modifyUserBalance( from.id, -from.offer.money, - 'TRADE_OUT', + TransactionType.TRADE_OUT, `Trade with ${to.username} (Thread: ${threadId})`, to.id, tx @@ -33,7 +34,7 @@ const processTransfer = async (tx: Transaction, from: TradeParticipant, to: Trad await economyService.modifyUserBalance( to.id, from.offer.money, - 'TRADE_IN', + TransactionType.TRADE_IN, `Trade with ${from.username} (Thread: ${threadId})`, from.id, tx @@ -54,7 +55,7 @@ const processTransfer = async (tx: Transaction, from: TradeParticipant, to: Trad relatedUserId: BigInt(to.id), itemId: item.id, quantity: -item.quantity, - type: 'TRADE_OUT', + type: ItemTransactionType.TRADE_OUT, description: `Traded to ${to.username}`, }); @@ -64,7 +65,7 @@ const processTransfer = async (tx: Transaction, from: TradeParticipant, to: Trad relatedUserId: BigInt(from.id), itemId: item.id, quantity: item.quantity, - type: 'TRADE_IN', + type: ItemTransactionType.TRADE_IN, description: `Received from ${from.username}`, }); } diff --git a/src/modules/user/user.timers.ts b/src/modules/user/user.timers.ts index e016fbe..e2e41d4 100644 --- a/src/modules/user/user.timers.ts +++ b/src/modules/user/user.timers.ts @@ -1,8 +1,9 @@ import { userTimers } from "@/db/schema"; import { eq, and } from "drizzle-orm"; import { DrizzleClient } from "@/lib/DrizzleClient"; +import { TimerType } from "@/lib/constants"; -export type TimerType = 'COOLDOWN' | 'EFFECT' | 'ACCESS'; +export { TimerType }; export const userTimerService = { /**