forked from syntaxbullet/AuroraBot-discord
feat: centralized constants and enums for project-wide use
This commit is contained in:
@@ -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 {
|
||||
|
||||
65
src/lib/constants.ts
Normal file
65
src/lib/constants.ts
Normal file
@@ -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',
|
||||
}
|
||||
@@ -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<K extends keyof ClientEvents> {
|
||||
}
|
||||
|
||||
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<Parameters<DbClient['transaction']>[0]>[0];
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 = () => [
|
||||
|
||||
@@ -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})`,
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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<any> };
|
||||
}) {
|
||||
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)],
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
});
|
||||
|
||||
@@ -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<string, TradeSession>();
|
||||
@@ -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}`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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 = {
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user