Compare commits
24 Commits
53a2f1ff0c
...
feat/dashb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39e405afde | ||
|
|
6763e3c543 | ||
|
|
11e07a0068 | ||
|
|
5d2d4bb0c6 | ||
|
|
19206b5cc7 | ||
|
|
0f6cce9b6e | ||
|
|
3f3a6c88e8 | ||
|
|
8253de9f73 | ||
|
|
1251df286e | ||
|
|
fff90804c0 | ||
|
|
8ebaf7b4ee | ||
|
|
17cb70ec00 | ||
|
|
a207d511be | ||
|
|
cf4f180124 | ||
|
|
5df1396b3f | ||
|
|
daad7be01c | ||
|
|
05f27ca604 | ||
|
|
d37059d50f | ||
|
|
caafe6b34d | ||
|
|
017f5ad818 | ||
|
|
f92415b89c | ||
|
|
3f028eb76a | ||
|
|
2b641c952d | ||
|
|
88b266f81b |
@@ -7,6 +7,7 @@ DISCORD_BOT_TOKEN=your-discord-bot-token
|
||||
DISCORD_CLIENT_ID=your-discord-client-id
|
||||
DISCORD_GUILD_ID=your-discord-guild-id
|
||||
DATABASE_URL=postgres://aurora:aurora@db:5432/aurora
|
||||
ADMIN_TOKEN=Ffeg4hgsdfvsnyms,kmeuy64sy5y
|
||||
|
||||
VPS_USER=your-vps-user
|
||||
VPS_HOST=your-vps-ip
|
||||
|
||||
8
.gitignore
vendored
@@ -1,7 +1,8 @@
|
||||
.env
|
||||
node_modules
|
||||
db-logs
|
||||
db-data
|
||||
shared/db-logs
|
||||
shared/db/data
|
||||
shared/db/loga
|
||||
.cursor
|
||||
# dependencies (bun install)
|
||||
|
||||
@@ -43,5 +44,4 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
src/db/data
|
||||
src/db/log
|
||||
scratchpad/
|
||||
|
||||
scratchpad/
|
||||
@@ -9,8 +9,8 @@ COPY package.json bun.lock ./
|
||||
RUN bun install --frozen-lockfile
|
||||
|
||||
# Install web project dependencies
|
||||
COPY src/web/package.json src/web/bun.lock ./src/web/
|
||||
RUN cd src/web && bun install --frozen-lockfile
|
||||
COPY web/package.json web/bun.lock ./web/
|
||||
RUN cd web && bun install --frozen-lockfile
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
@@ -1,6 +1,6 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags } from "discord.js";
|
||||
import { ModerationService } from "@/modules/moderation/moderation.service";
|
||||
import { ModerationService } from "@shared/modules/moderation/moderation.service";
|
||||
import { getCaseEmbed, getModerationErrorEmbed } from "@/modules/moderation/moderation.view";
|
||||
|
||||
export const moderationCase = createCommand({
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags } from "discord.js";
|
||||
import { ModerationService } from "@/modules/moderation/moderation.service";
|
||||
import { ModerationService } from "@shared/modules/moderation/moderation.service";
|
||||
import { getCasesListEmbed, getModerationErrorEmbed } from "@/modules/moderation/moderation.view";
|
||||
|
||||
export const cases = createCommand({
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags } from "discord.js";
|
||||
import { ModerationService } from "@/modules/moderation/moderation.service";
|
||||
import { ModerationService } from "@shared/modules/moderation/moderation.service";
|
||||
import { getClearSuccessEmbed, getModerationErrorEmbed } from "@/modules/moderation/moderation.view";
|
||||
|
||||
export const clearwarning = createCommand({
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createCommand } from "@lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, ModalBuilder, TextInputBuilder, TextInputStyle, ActionRowBuilder, ModalSubmitInteraction } from "discord.js";
|
||||
import { config, saveConfig } from "@lib/config";
|
||||
import type { GameConfigType } from "@lib/config";
|
||||
import { config, saveConfig } from "@shared/lib/config";
|
||||
import type { GameConfigType } from "@shared/lib/config";
|
||||
import { createSuccessEmbed, createErrorEmbed } from "@lib/embeds";
|
||||
|
||||
export const configCommand = createCommand({
|
||||
@@ -1,8 +1,8 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, AttachmentBuilder } from "discord.js";
|
||||
import { config, saveConfig } from "@/lib/config";
|
||||
import { DrizzleClient } from "@/lib/DrizzleClient";
|
||||
import { items } from "@/db/schema";
|
||||
import { config, saveConfig } from "@shared/lib/config";
|
||||
import { DrizzleClient } from "@shared/db/DrizzleClient";
|
||||
import { items } from "@db/schema";
|
||||
import { createSuccessEmbed, createErrorEmbed } from "@lib/embeds";
|
||||
|
||||
export const createColor = createCommand({
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags } from "discord.js";
|
||||
import { renderWizard } from "@/modules/admin/item_wizard";
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags } from "discord.js";
|
||||
import { createBaseEmbed } from "@lib/embeds";
|
||||
import { configManager } from "@/lib/configManager";
|
||||
import { config, reloadConfig } from "@/lib/config";
|
||||
import { config, reloadConfig, toggleCommand } from "@shared/lib/config";
|
||||
import { AuroraClient } from "@/lib/BotClient";
|
||||
|
||||
export const features = createCommand({
|
||||
@@ -79,11 +78,11 @@ export const features = createCommand({
|
||||
|
||||
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||
|
||||
configManager.toggleCommand(commandName, enabled);
|
||||
toggleCommand(commandName, enabled);
|
||||
|
||||
await interaction.editReply({ content: `✅ Command **${commandName}** has been ${enabled ? "enabled" : "disabled"}. Reloading configuration...` });
|
||||
|
||||
// Reload config from disk (which was updated by configManager)
|
||||
// Reload config from disk (which was updated by toggleCommand)
|
||||
reloadConfig();
|
||||
|
||||
await AuroraClient.loadCommands(true);
|
||||
@@ -5,7 +5,7 @@ import { AuroraClient } from "@/lib/BotClient";
|
||||
|
||||
// Mock DrizzleClient
|
||||
const executeMock = mock(() => Promise.resolve());
|
||||
mock.module("@/lib/DrizzleClient", () => ({
|
||||
mock.module("@shared/db/DrizzleClient", () => ({
|
||||
DrizzleClient: {
|
||||
execute: executeMock
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createCommand } from "@lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { AuroraClient } from "@/lib/BotClient";
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder, Colors } from "discord.js";
|
||||
import { DrizzleClient } from "@/lib/DrizzleClient";
|
||||
import { DrizzleClient } from "@shared/db/DrizzleClient";
|
||||
import { sql } from "drizzle-orm";
|
||||
import { createBaseEmbed } from "@lib/embeds";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import {
|
||||
SlashCommandBuilder,
|
||||
ActionRowBuilder,
|
||||
@@ -8,12 +8,12 @@ import {
|
||||
PermissionFlagsBits,
|
||||
MessageFlags
|
||||
} from "discord.js";
|
||||
import { inventoryService } from "@/modules/inventory/inventory.service";
|
||||
import { inventoryService } from "@shared/modules/inventory/inventory.service";
|
||||
import { createSuccessEmbed, createErrorEmbed, createBaseEmbed } from "@lib/embeds";
|
||||
import { UserError } from "@/lib/errors";
|
||||
import { items } from "@/db/schema";
|
||||
import { items } from "@db/schema";
|
||||
import { ilike, isNotNull, and } from "drizzle-orm";
|
||||
import { DrizzleClient } from "@/lib/DrizzleClient";
|
||||
import { DrizzleClient } from "@shared/db/DrizzleClient";
|
||||
import { getShopListingMessage } from "@/modules/economy/shop.view";
|
||||
|
||||
export const listing = createCommand({
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags } from "discord.js";
|
||||
import { ModerationService } from "@/modules/moderation/moderation.service";
|
||||
import { CaseType } from "@/lib/constants";
|
||||
import { ModerationService } from "@shared/modules/moderation/moderation.service";
|
||||
import { CaseType } from "@shared/lib/constants";
|
||||
import { getNoteSuccessEmbed, getModerationErrorEmbed } from "@/modules/moderation/moderation.view";
|
||||
|
||||
export const note = createCommand({
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags } from "discord.js";
|
||||
import { ModerationService } from "@/modules/moderation/moderation.service";
|
||||
import { ModerationService } from "@shared/modules/moderation/moderation.service";
|
||||
import { getCasesListEmbed, getModerationErrorEmbed } from "@/modules/moderation/moderation.view";
|
||||
|
||||
export const notes = createCommand({
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags, ComponentType } from "discord.js";
|
||||
import { config } from "@/lib/config";
|
||||
import { PruneService } from "@/modules/moderation/prune.service";
|
||||
import { config } from "@shared/lib/config";
|
||||
import { PruneService } from "@shared/modules/moderation/prune.service";
|
||||
import {
|
||||
getConfirmationMessage,
|
||||
getProgressEmbed,
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createCommand } from "@lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { AuroraClient } from "@/lib/BotClient";
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags } from "discord.js";
|
||||
import { createErrorEmbed, createSuccessEmbed } from "@lib/embeds";
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, ChannelType, TextChannel } from "discord.js";
|
||||
import { terminalService } from "@/modules/terminal/terminal.service";
|
||||
import { terminalService } from "@shared/modules/terminal/terminal.service";
|
||||
import { createBaseEmbed, createErrorEmbed } from "@/lib/embeds";
|
||||
|
||||
export const terminal = createCommand({
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createCommand } from "@lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags, ComponentType } from "discord.js";
|
||||
import { UpdateService } from "@/modules/admin/update.service";
|
||||
import { UpdateService } from "@shared/modules/admin/update.service";
|
||||
import {
|
||||
getCheckingEmbed,
|
||||
getNoUpdatesEmbed,
|
||||
@@ -1,12 +1,12 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags } from "discord.js";
|
||||
import { ModerationService } from "@/modules/moderation/moderation.service";
|
||||
import { ModerationService } from "@shared/modules/moderation/moderation.service";
|
||||
import {
|
||||
getWarnSuccessEmbed,
|
||||
getModerationErrorEmbed,
|
||||
getUserWarningEmbed
|
||||
} from "@/modules/moderation/moderation.view";
|
||||
import { config } from "@/lib/config";
|
||||
import { config } from "@shared/lib/config";
|
||||
|
||||
export const warn = createCommand({
|
||||
data: new SlashCommandBuilder()
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags } from "discord.js";
|
||||
import { ModerationService } from "@/modules/moderation/moderation.service";
|
||||
import { ModerationService } from "@shared/modules/moderation/moderation.service";
|
||||
import { getWarningsEmbed, getModerationErrorEmbed } from "@/modules/moderation/moderation.view";
|
||||
|
||||
export const warnings = createCommand({
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags } from "discord.js";
|
||||
import { createErrorEmbed } from "@/lib/embeds";
|
||||
import { sendWebhookMessage } from "@/lib/webhookUtils";
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder } from "discord.js";
|
||||
import { userService } from "@/modules/user/user.service";
|
||||
import { userService } from "@shared/modules/user/user.service";
|
||||
import { createBaseEmbed } from "@lib/embeds";
|
||||
|
||||
export const balance = createCommand({
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder } from "discord.js";
|
||||
import { economyService } from "@/modules/economy/economy.service";
|
||||
import { economyService } from "@shared/modules/economy/economy.service";
|
||||
import { createErrorEmbed, createSuccessEmbed } from "@lib/embeds";
|
||||
import { UserError } from "@/lib/errors";
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder } from "discord.js";
|
||||
import { userService } from "@/modules/user/user.service";
|
||||
import { userService } from "@shared/modules/user/user.service";
|
||||
import { createErrorEmbed, createSuccessEmbed } from "@lib/embeds";
|
||||
import { UserError } from "@/lib/errors";
|
||||
import { userTimers, users } from "@/db/schema";
|
||||
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";
|
||||
import { DrizzleClient } from "@shared/db/DrizzleClient";
|
||||
import { config } from "@shared/lib/config";
|
||||
import { TimerType } from "@shared/lib/constants";
|
||||
|
||||
const EXAM_TIMER_TYPE = TimerType.EXAM_SYSTEM;
|
||||
const EXAM_TIMER_KEY = 'default';
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, MessageFlags } from "discord.js";
|
||||
import { economyService } from "@/modules/economy/economy.service";
|
||||
import { userService } from "@/modules/user/user.service";
|
||||
import { config } from "@/lib/config";
|
||||
import { economyService } from "@shared/modules/economy/economy.service";
|
||||
import { userService } from "@shared/modules/user/user.service";
|
||||
import { config } from "@shared/lib/config";
|
||||
import { createErrorEmbed, createSuccessEmbed } from "@lib/embeds";
|
||||
import { UserError } from "@/lib/errors";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, ChannelType, ThreadAutoArchiveDuration, MessageFlags } from "discord.js";
|
||||
import { tradeService } from "@/modules/trade/trade.service";
|
||||
import { tradeService } from "@shared/modules/trade/trade.service";
|
||||
import { getTradeDashboard } from "@/modules/trade/trade.view";
|
||||
import { createErrorEmbed, createWarningEmbed } from "@lib/embeds";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder } from "discord.js";
|
||||
import { config } from "@/lib/config";
|
||||
import { config } from "@shared/lib/config";
|
||||
import { createErrorEmbed } from "@/lib/embeds";
|
||||
import { getFeedbackTypeMenu } from "@/modules/feedback/feedback.view";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder } from "discord.js";
|
||||
import { inventoryService } from "@/modules/inventory/inventory.service";
|
||||
import { userService } from "@/modules/user/user.service";
|
||||
import { inventoryService } from "@shared/modules/inventory/inventory.service";
|
||||
import { userService } from "@shared/modules/user/user.service";
|
||||
import { createWarningEmbed } from "@lib/embeds";
|
||||
import { getInventoryEmbed } from "@/modules/inventory/inventory.view";
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder } from "discord.js";
|
||||
import { inventoryService } from "@/modules/inventory/inventory.service";
|
||||
import { userService } from "@/modules/user/user.service";
|
||||
import { inventoryService } from "@shared/modules/inventory/inventory.service";
|
||||
import { userService } from "@shared/modules/user/user.service";
|
||||
import { createErrorEmbed } from "@lib/embeds";
|
||||
import { getItemUseResultEmbed } from "@/modules/inventory/inventory.view";
|
||||
import type { ItemUsageData } from "@/lib/types";
|
||||
import type { ItemUsageData } from "@shared/lib/types";
|
||||
import { UserError } from "@/lib/errors";
|
||||
import { config } from "@/lib/config";
|
||||
import { config } from "@shared/lib/config";
|
||||
|
||||
export const use = createCommand({
|
||||
data: new SlashCommandBuilder()
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder } from "discord.js";
|
||||
import { DrizzleClient } from "@/lib/DrizzleClient";
|
||||
import { users, items, inventory } from "@/db/schema";
|
||||
import { DrizzleClient } from "@shared/db/DrizzleClient";
|
||||
import { users, items, inventory } from "@db/schema";
|
||||
import { desc, sql, eq } from "drizzle-orm";
|
||||
import { createWarningEmbed } from "@lib/embeds";
|
||||
import { getLeaderboardEmbed } from "@/modules/leveling/leveling.view";
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, MessageFlags } from "discord.js";
|
||||
import { questService } from "@/modules/quest/quest.service";
|
||||
import { questService } from "@shared/modules/quest/quest.service";
|
||||
import { createWarningEmbed } from "@lib/embeds";
|
||||
import { getQuestListEmbed } from "@/modules/quest/quest.view";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createCommand } from "@/lib/utils";
|
||||
import { createCommand } from "@shared/lib/utils";
|
||||
import { SlashCommandBuilder, AttachmentBuilder } from "discord.js";
|
||||
import { userService } from "@/modules/user/user.service";
|
||||
import { userService } from "@shared/modules/user/user.service";
|
||||
import { generateStudentIdCard } from "@/graphics/studentID";
|
||||
import { createWarningEmbed } from "@/lib/embeds";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Events } from "discord.js";
|
||||
import type { Event } from "@lib/types";
|
||||
import { config } from "@lib/config";
|
||||
import { userService } from "@modules/user/user.service";
|
||||
import type { Event } from "@shared/lib/types";
|
||||
import { config } from "@shared/lib/config";
|
||||
import { userService } from "@shared/modules/user/user.service";
|
||||
|
||||
// Visitor role
|
||||
const event: Event<Events.GuildMemberAdd> = {
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Events } from "discord.js";
|
||||
import { ComponentInteractionHandler, AutocompleteHandler, CommandHandler } from "@/lib/handlers";
|
||||
import type { Event } from "@lib/types";
|
||||
import type { Event } from "@shared/lib/types";
|
||||
|
||||
const event: Event<Events.InteractionCreate> = {
|
||||
name: Events.InteractionCreate,
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Events } from "discord.js";
|
||||
import { userService } from "@/modules/user/user.service";
|
||||
import { levelingService } from "@/modules/leveling/leveling.service";
|
||||
import type { Event } from "@lib/types";
|
||||
import { userService } from "@shared/modules/user/user.service";
|
||||
import { levelingService } from "@shared/modules/leveling/leveling.service";
|
||||
import type { Event } from "@shared/lib/types";
|
||||
|
||||
const event: Event<Events.MessageCreate> = {
|
||||
name: Events.MessageCreate,
|
||||
@@ -15,7 +15,7 @@ const event: Event<Events.MessageCreate> = {
|
||||
levelingService.processChatXp(message.author.id);
|
||||
|
||||
// Activity Tracking for Lootdrops
|
||||
import("@/modules/economy/lootdrop.service").then(m => m.lootdropService.processMessage(message));
|
||||
import("@shared/modules/economy/lootdrop.service").then(m => m.lootdropService.processMessage(message));
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Events } from "discord.js";
|
||||
import { schedulerService } from "@/modules/system/scheduler";
|
||||
import type { Event } from "@lib/types";
|
||||
import type { Event } from "@shared/lib/types";
|
||||
|
||||
const event: Event<Events.ClientReady> = {
|
||||
name: Events.ClientReady,
|
||||
@@ -10,7 +10,7 @@ const event: Event<Events.ClientReady> = {
|
||||
schedulerService.start();
|
||||
|
||||
// Handle post-update tasks
|
||||
const { UpdateService } = await import("@/modules/admin/update.service");
|
||||
const { UpdateService } = await import("@shared/modules/admin/update.service");
|
||||
await UpdateService.handlePostRestart(c);
|
||||
},
|
||||
};
|
||||
@@ -2,12 +2,12 @@ import { GlobalFonts, createCanvas, loadImage } from '@napi-rs/canvas';
|
||||
import path from 'path';
|
||||
|
||||
// Register Fonts (same as studentID.ts)
|
||||
const fontDir = path.join(process.cwd(), 'src', 'assets', 'fonts');
|
||||
const fontDir = path.join(process.cwd(), 'bot', 'assets', 'fonts');
|
||||
GlobalFonts.registerFromPath(path.join(fontDir, 'IBMPlexSansCondensed-SemiBold.ttf'), 'IBMPlexSansCondensed-SemiBold');
|
||||
GlobalFonts.registerFromPath(path.join(fontDir, 'IBMPlexMono-Bold.ttf'), 'IBMPlexMono-Bold');
|
||||
|
||||
export async function generateLootdropCard(amount: number, currency: string): Promise<Buffer> {
|
||||
const templatePath = path.join(process.cwd(), 'src', 'assets', 'graphics', 'lootdrop', 'template.png');
|
||||
const templatePath = path.join(process.cwd(), 'bot', 'assets', 'graphics', 'lootdrop', 'template.png');
|
||||
const template = await loadImage(templatePath);
|
||||
|
||||
const canvas = createCanvas(template.width, template.height);
|
||||
@@ -50,7 +50,7 @@ export async function generateLootdropCard(amount: number, currency: string): Pr
|
||||
}
|
||||
|
||||
export async function generateClaimedLootdropCard(amount: number, currency: string, username: string, avatarUrl: string): Promise<Buffer> {
|
||||
const templatePath = path.join(process.cwd(), 'src', 'assets', 'graphics', 'lootdrop', 'template.png');
|
||||
const templatePath = path.join(process.cwd(), 'bot', 'assets', 'graphics', 'lootdrop', 'template.png');
|
||||
const template = await loadImage(templatePath);
|
||||
|
||||
const canvas = createCanvas(template.width, template.height);
|
||||
@@ -1,9 +1,9 @@
|
||||
import { GlobalFonts, createCanvas, loadImage } from '@napi-rs/canvas';
|
||||
import { levelingService } from '@/modules/leveling/leveling.service';
|
||||
import { levelingService } from '@shared/modules/leveling/leveling.service';
|
||||
import path from 'path';
|
||||
|
||||
// Register Fonts
|
||||
const fontDir = path.join(process.cwd(), 'src', 'assets', 'fonts');
|
||||
const fontDir = path.join(process.cwd(), 'bot', 'assets', 'fonts');
|
||||
GlobalFonts.registerFromPath(path.join(fontDir, 'IBMPlexSansCondensed-SemiBold.ttf'), 'IBMPlexSansCondensed-SemiBold');
|
||||
GlobalFonts.registerFromPath(path.join(fontDir, 'IBMPlexMono-Bold.ttf'), 'IBMPlexMono-Bold');
|
||||
|
||||
@@ -18,8 +18,8 @@ interface StudentCardData {
|
||||
}
|
||||
|
||||
export async function generateStudentIdCard(data: StudentCardData): Promise<Buffer> {
|
||||
const templatePath = path.join(process.cwd(), 'src', 'assets', 'graphics', 'studentID', 'template.png');
|
||||
const classTemplatePath = path.join(process.cwd(), 'src', 'assets', 'graphics', 'studentID', `Constellation-${data.className}.png`);
|
||||
const templatePath = path.join(process.cwd(), 'bot', 'assets', 'graphics', 'studentID', 'template.png');
|
||||
const classTemplatePath = path.join(process.cwd(), 'bot', 'assets', 'graphics', 'studentID', `Constellation-${data.className}.png`);
|
||||
|
||||
const template = await loadImage(templatePath);
|
||||
const classTemplate = await loadImage(classTemplatePath);
|
||||
@@ -1,19 +1,20 @@
|
||||
import { AuroraClient } from "@/lib/BotClient";
|
||||
import { env } from "@lib/env";
|
||||
import { env } from "@shared/lib/env";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { startWebServerFromRoot } from "./web/src/server";
|
||||
import { startWebServerFromRoot } from "../web/src/server";
|
||||
|
||||
// Load commands & events
|
||||
await AuroraClient.loadCommands();
|
||||
await AuroraClient.loadEvents();
|
||||
await AuroraClient.deployCommands();
|
||||
await AuroraClient.setupSystemEvents();
|
||||
|
||||
console.log("🌐 Starting web server...");
|
||||
|
||||
let shuttingDown = false;
|
||||
|
||||
const webProjectPath = join(import.meta.dir, "web");
|
||||
const webProjectPath = join(import.meta.dir, "../web");
|
||||
const webPort = Number(process.env.WEB_PORT) || 3000;
|
||||
const webHost = process.env.HOST || "0.0.0.0";
|
||||
|
||||
111
bot/lib/BotClient.test.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { describe, expect, test, mock, beforeEach, spyOn } from "bun:test";
|
||||
import { systemEvents, EVENTS } from "@shared/lib/events";
|
||||
|
||||
// Mock Discord.js Client and related classes
|
||||
mock.module("discord.js", () => ({
|
||||
Client: class {
|
||||
constructor() { }
|
||||
on() { }
|
||||
once() { }
|
||||
login() { }
|
||||
destroy() { }
|
||||
removeAllListeners() { }
|
||||
},
|
||||
Collection: Map,
|
||||
GatewayIntentBits: { Guilds: 1, MessageContent: 1, GuildMessages: 1, GuildMembers: 1 },
|
||||
REST: class {
|
||||
setToken() { return this; }
|
||||
put() { return Promise.resolve([]); }
|
||||
},
|
||||
Routes: {
|
||||
applicationGuildCommands: () => 'guild_route',
|
||||
applicationCommands: () => 'global_route'
|
||||
}
|
||||
}));
|
||||
|
||||
// Mock loaders to avoid filesystem access during client init
|
||||
mock.module("../lib/loaders/CommandLoader", () => ({
|
||||
CommandLoader: class {
|
||||
constructor() { }
|
||||
loadFromDirectory() { return Promise.resolve({ loaded: 0, skipped: 0, errors: [] }); }
|
||||
}
|
||||
}));
|
||||
mock.module("../lib/loaders/EventLoader", () => ({
|
||||
EventLoader: class {
|
||||
constructor() { }
|
||||
loadFromDirectory() { return Promise.resolve({ loaded: 0, skipped: 0, errors: [] }); }
|
||||
}
|
||||
}));
|
||||
|
||||
// Mock dashboard service to prevent network/db calls during event handling
|
||||
mock.module("@shared/modules/economy/lootdrop.service", () => ({
|
||||
lootdropService: { clearCaches: mock(async () => { }) }
|
||||
}));
|
||||
mock.module("@shared/modules/trade/trade.service", () => ({
|
||||
tradeService: { clearSessions: mock(() => { }) }
|
||||
}));
|
||||
mock.module("@/modules/admin/item_wizard", () => ({
|
||||
clearDraftSessions: mock(() => { })
|
||||
}));
|
||||
mock.module("@shared/modules/dashboard/dashboard.service", () => ({
|
||||
dashboardService: {
|
||||
recordEvent: mock(() => Promise.resolve())
|
||||
}
|
||||
}));
|
||||
|
||||
describe("AuroraClient System Events", () => {
|
||||
let AuroraClient: any;
|
||||
|
||||
beforeEach(async () => {
|
||||
systemEvents.removeAllListeners();
|
||||
const module = await import("./BotClient");
|
||||
AuroraClient = module.AuroraClient;
|
||||
AuroraClient.maintenanceMode = false;
|
||||
// MUST call explicitly now
|
||||
await AuroraClient.setupSystemEvents();
|
||||
});
|
||||
|
||||
/**
|
||||
* Test Case: Maintenance Mode Toggle
|
||||
* Requirement: Client state should update when event is received
|
||||
*/
|
||||
test("should toggle maintenanceMode when MAINTENANCE_MODE event is received", async () => {
|
||||
systemEvents.emit(EVENTS.ACTIONS.MAINTENANCE_MODE, { enabled: true, reason: "Testing" });
|
||||
await new Promise(resolve => setTimeout(resolve, 30));
|
||||
expect(AuroraClient.maintenanceMode).toBe(true);
|
||||
|
||||
systemEvents.emit(EVENTS.ACTIONS.MAINTENANCE_MODE, { enabled: false });
|
||||
await new Promise(resolve => setTimeout(resolve, 30));
|
||||
expect(AuroraClient.maintenanceMode).toBe(false);
|
||||
});
|
||||
|
||||
/**
|
||||
* Test Case: Command Reload
|
||||
* Requirement: loadCommands and deployCommands should be called
|
||||
*/
|
||||
test("should reload commands when RELOAD_COMMANDS event is received", async () => {
|
||||
const loadSpy = spyOn(AuroraClient, "loadCommands").mockImplementation(() => Promise.resolve());
|
||||
const deploySpy = spyOn(AuroraClient, "deployCommands").mockImplementation(() => Promise.resolve());
|
||||
|
||||
systemEvents.emit(EVENTS.ACTIONS.RELOAD_COMMANDS);
|
||||
await new Promise(resolve => setTimeout(resolve, 50));
|
||||
|
||||
expect(loadSpy).toHaveBeenCalled();
|
||||
expect(deploySpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
/**
|
||||
* Test Case: Cache Clearance
|
||||
* Requirement: Service clear methods should be triggered
|
||||
*/
|
||||
test("should trigger service cache clearance when CLEAR_CACHE is received", async () => {
|
||||
const { lootdropService } = await import("@shared/modules/economy/lootdrop.service");
|
||||
const { tradeService } = await import("@shared/modules/trade/trade.service");
|
||||
|
||||
systemEvents.emit(EVENTS.ACTIONS.CLEAR_CACHE);
|
||||
await new Promise(resolve => setTimeout(resolve, 50));
|
||||
|
||||
expect(lootdropService.clearCaches).toHaveBeenCalled();
|
||||
expect(tradeService.clearSessions).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Client as DiscordClient, Collection, GatewayIntentBits, REST, Routes } from "discord.js";
|
||||
import { join } from "node:path";
|
||||
import type { Command } from "@lib/types";
|
||||
import { env } from "@lib/env";
|
||||
import type { Command } from "@shared/lib/types";
|
||||
import { env } from "@shared/lib/env";
|
||||
import { CommandLoader } from "@lib/loaders/CommandLoader";
|
||||
import { EventLoader } from "@lib/loaders/EventLoader";
|
||||
|
||||
@@ -9,6 +9,7 @@ export class Client extends DiscordClient {
|
||||
|
||||
commands: Collection<string, Command>;
|
||||
lastCommandTimestamp: number | null = null;
|
||||
maintenanceMode: boolean = false;
|
||||
private commandLoader: CommandLoader;
|
||||
private eventLoader: EventLoader;
|
||||
|
||||
@@ -19,6 +20,60 @@ export class Client extends DiscordClient {
|
||||
this.eventLoader = new EventLoader(this);
|
||||
}
|
||||
|
||||
public async setupSystemEvents() {
|
||||
const { systemEvents, EVENTS } = await import("@shared/lib/events");
|
||||
|
||||
systemEvents.on(EVENTS.ACTIONS.RELOAD_COMMANDS, async () => {
|
||||
console.log("🔄 System Action: Reloading commands...");
|
||||
try {
|
||||
await this.loadCommands(true);
|
||||
await this.deployCommands();
|
||||
|
||||
const { dashboardService } = await import("@shared/modules/dashboard/dashboard.service");
|
||||
await dashboardService.recordEvent({
|
||||
type: "success",
|
||||
message: "Bot: Commands reloaded and redeployed",
|
||||
icon: "✅"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to reload commands:", error);
|
||||
}
|
||||
});
|
||||
|
||||
systemEvents.on(EVENTS.ACTIONS.CLEAR_CACHE, async () => {
|
||||
console.log("<22> System Action: Clearing all internal caches...");
|
||||
|
||||
try {
|
||||
// 1. Lootdrop Service
|
||||
const { lootdropService } = await import("@shared/modules/economy/lootdrop.service");
|
||||
await lootdropService.clearCaches();
|
||||
|
||||
// 2. Trade Service
|
||||
const { tradeService } = await import("@shared/modules/trade/trade.service");
|
||||
tradeService.clearSessions();
|
||||
|
||||
// 3. Item Wizard
|
||||
const { clearDraftSessions } = await import("@/modules/admin/item_wizard");
|
||||
clearDraftSessions();
|
||||
|
||||
const { dashboardService } = await import("@shared/modules/dashboard/dashboard.service");
|
||||
await dashboardService.recordEvent({
|
||||
type: "success",
|
||||
message: "Bot: All internal caches and sessions cleared",
|
||||
icon: "🧼"
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to clear caches:", error);
|
||||
}
|
||||
});
|
||||
|
||||
systemEvents.on(EVENTS.ACTIONS.MAINTENANCE_MODE, async (data: { enabled: boolean, reason?: string }) => {
|
||||
const { enabled, reason } = data;
|
||||
console.log(`🛠️ System Action: Maintenance mode ${enabled ? "ON" : "OFF"}${reason ? ` (${reason})` : ""}`);
|
||||
this.maintenanceMode = enabled;
|
||||
});
|
||||
}
|
||||
|
||||
async loadCommands(reload: boolean = false) {
|
||||
if (reload) {
|
||||
this.commands.clear();
|
||||
@@ -96,7 +151,7 @@ export class Client extends DiscordClient {
|
||||
|
||||
async shutdown() {
|
||||
const { setShuttingDown, waitForTransactions } = await import("./shutdown");
|
||||
const { closeDatabase } = await import("./DrizzleClient");
|
||||
const { closeDatabase } = await import("@shared/db/DrizzleClient");
|
||||
|
||||
console.log("🛑 Shutdown signal received. Starting graceful shutdown...");
|
||||
setShuttingDown(true);
|
||||
74
bot/lib/clientStats.test.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { describe, test, expect, beforeEach, mock, afterEach } from "bun:test";
|
||||
import { getClientStats, clearStatsCache } from "./clientStats";
|
||||
|
||||
// Mock AuroraClient
|
||||
mock.module("./BotClient", () => ({
|
||||
AuroraClient: {
|
||||
guilds: {
|
||||
cache: {
|
||||
size: 5,
|
||||
},
|
||||
},
|
||||
ws: {
|
||||
ping: 42,
|
||||
},
|
||||
users: {
|
||||
cache: {
|
||||
size: 100,
|
||||
},
|
||||
},
|
||||
commands: {
|
||||
size: 20,
|
||||
},
|
||||
lastCommandTimestamp: 1641481200000,
|
||||
},
|
||||
}));
|
||||
|
||||
describe("clientStats", () => {
|
||||
beforeEach(() => {
|
||||
clearStatsCache();
|
||||
});
|
||||
|
||||
test("should return client stats", () => {
|
||||
const stats = getClientStats();
|
||||
|
||||
expect(stats.guilds).toBe(5);
|
||||
expect(stats.ping).toBe(42);
|
||||
expect(stats.cachedUsers).toBe(100);
|
||||
expect(stats.commandsRegistered).toBe(20);
|
||||
expect(typeof stats.uptime).toBe("number"); // Can't mock process.uptime easily
|
||||
expect(stats.lastCommandTimestamp).toBe(1641481200000);
|
||||
});
|
||||
|
||||
test("should cache stats for 30 seconds", () => {
|
||||
const stats1 = getClientStats();
|
||||
const stats2 = getClientStats();
|
||||
|
||||
// Should return same object (cached)
|
||||
expect(stats1).toBe(stats2);
|
||||
});
|
||||
|
||||
test("should refresh cache after TTL expires", async () => {
|
||||
const stats1 = getClientStats();
|
||||
|
||||
// Wait for cache to expire (simulate by clearing and waiting)
|
||||
await new Promise(resolve => setTimeout(resolve, 35));
|
||||
clearStatsCache();
|
||||
|
||||
const stats2 = getClientStats();
|
||||
|
||||
// Should be different objects (new fetch)
|
||||
expect(stats1).not.toBe(stats2);
|
||||
// But values should be the same (mocked client)
|
||||
expect(stats1.guilds).toBe(stats2.guilds);
|
||||
});
|
||||
|
||||
test("clearStatsCache should invalidate cache", () => {
|
||||
const stats1 = getClientStats();
|
||||
clearStatsCache();
|
||||
const stats2 = getClientStats();
|
||||
|
||||
// Should be different objects
|
||||
expect(stats1).not.toBe(stats2);
|
||||
});
|
||||
});
|
||||
48
bot/lib/clientStats.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { AuroraClient } from "./BotClient";
|
||||
import type { ClientStats } from "@shared/modules/dashboard/dashboard.types";
|
||||
|
||||
// Cache for client stats (30 second TTL)
|
||||
let cachedStats: ClientStats | null = null;
|
||||
let lastFetchTime: number = 0;
|
||||
const CACHE_TTL_MS = 30 * 1000; // 30 seconds
|
||||
|
||||
/**
|
||||
* Get Discord client statistics with caching
|
||||
* Respects rate limits by caching for 30 seconds
|
||||
*/
|
||||
export function getClientStats(): ClientStats {
|
||||
const now = Date.now();
|
||||
|
||||
// Return cached stats if still valid
|
||||
if (cachedStats && (now - lastFetchTime) < CACHE_TTL_MS) {
|
||||
return cachedStats;
|
||||
}
|
||||
|
||||
// Fetch fresh stats
|
||||
const stats: ClientStats = {
|
||||
bot: {
|
||||
name: AuroraClient.user?.username || "Aurora",
|
||||
avatarUrl: AuroraClient.user?.displayAvatarURL() || null,
|
||||
},
|
||||
guilds: AuroraClient.guilds.cache.size,
|
||||
ping: AuroraClient.ws.ping,
|
||||
cachedUsers: AuroraClient.users.cache.size,
|
||||
commandsRegistered: AuroraClient.commands.size,
|
||||
uptime: process.uptime(),
|
||||
lastCommandTimestamp: AuroraClient.lastCommandTimestamp,
|
||||
};
|
||||
|
||||
// Update cache
|
||||
cachedStats = stats;
|
||||
lastFetchTime = now;
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the stats cache (useful for testing)
|
||||
*/
|
||||
export function clearStatsCache(): void {
|
||||
cachedStats = null;
|
||||
lastFetchTime = 0;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DrizzleClient } from "./DrizzleClient";
|
||||
import type { Transaction } from "./types";
|
||||
import { DrizzleClient } from "@shared/db/DrizzleClient";
|
||||
import type { Transaction } from "@shared/lib/types";
|
||||
import { isShuttingDown, incrementTransactions, decrementTransactions } from "./shutdown";
|
||||
|
||||
export const withTransaction = async <T>(
|
||||
@@ -4,7 +4,7 @@ import { AuroraClient } from "@/lib/BotClient";
|
||||
import { ChatInputCommandInteraction } from "discord.js";
|
||||
|
||||
// Mock UserService
|
||||
mock.module("@/modules/user/user.service", () => ({
|
||||
mock.module("@shared/modules/user/user.service", () => ({
|
||||
userService: {
|
||||
getOrCreateUser: mock(() => Promise.resolve())
|
||||
}
|
||||
@@ -56,4 +56,28 @@ describe("CommandHandler", () => {
|
||||
expect(executeError).toHaveBeenCalled();
|
||||
expect(AuroraClient.lastCommandTimestamp).toBeNull();
|
||||
});
|
||||
|
||||
test("should block execution when maintenance mode is active", async () => {
|
||||
AuroraClient.maintenanceMode = true;
|
||||
const executeSpy = mock(() => Promise.resolve());
|
||||
AuroraClient.commands.set("maint-test", {
|
||||
data: { name: "maint-test" } as any,
|
||||
execute: executeSpy
|
||||
} as any);
|
||||
|
||||
const interaction = {
|
||||
commandName: "maint-test",
|
||||
user: { id: "123", username: "testuser" },
|
||||
reply: mock(() => Promise.resolve())
|
||||
} as unknown as ChatInputCommandInteraction;
|
||||
|
||||
await CommandHandler.handle(interaction);
|
||||
|
||||
expect(executeSpy).not.toHaveBeenCalled();
|
||||
expect(interaction.reply).toHaveBeenCalledWith(expect.objectContaining({
|
||||
flags: expect.anything()
|
||||
}));
|
||||
|
||||
AuroraClient.maintenanceMode = false; // Reset for other tests
|
||||
});
|
||||
});
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ChatInputCommandInteraction, MessageFlags } from "discord.js";
|
||||
import { AuroraClient } from "@/lib/BotClient";
|
||||
import { userService } from "@/modules/user/user.service";
|
||||
import { userService } from "@shared/modules/user/user.service";
|
||||
import { createErrorEmbed } from "@lib/embeds";
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Handles slash command execution
|
||||
@@ -17,6 +17,13 @@ export class CommandHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check maintenance mode
|
||||
if (AuroraClient.maintenanceMode) {
|
||||
const errorEmbed = createErrorEmbed('The bot is currently undergoing maintenance. Please try again later.');
|
||||
await interaction.reply({ embeds: [errorEmbed], flags: MessageFlags.Ephemeral });
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure user exists in database
|
||||
try {
|
||||
await userService.getOrCreateUser(interaction.user.id, interaction.user.username);
|
||||
@@ -1,7 +1,7 @@
|
||||
import { readdir } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import type { Command } from "@lib/types";
|
||||
import { config } from "@lib/config";
|
||||
import type { Command } from "@shared/lib/types";
|
||||
import { config } from "@shared/lib/config";
|
||||
import type { LoadResult, LoadError } from "./types";
|
||||
import type { Client } from "../BotClient";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { readdir } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import type { Event } from "@lib/types";
|
||||
import type { Event } from "@shared/lib/types";
|
||||
import type { LoadResult } from "./types";
|
||||
import type { Client } from "../BotClient";
|
||||
|
||||
@@ -6,13 +6,13 @@ import { ButtonInteraction, ModalSubmitInteraction, StringSelectMenuInteraction
|
||||
const valuesMock = mock((_args: any) => Promise.resolve());
|
||||
const insertMock = mock(() => ({ values: valuesMock }));
|
||||
|
||||
mock.module("@/lib/DrizzleClient", () => ({
|
||||
mock.module("@shared/db/DrizzleClient", () => ({
|
||||
DrizzleClient: {
|
||||
insert: insertMock
|
||||
}
|
||||
}));
|
||||
|
||||
mock.module("@/db/schema", () => ({
|
||||
mock.module("@db/schema", () => ({
|
||||
items: "items_schema"
|
||||
}));
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { type Interaction } from "discord.js";
|
||||
import { items } from "@/db/schema";
|
||||
import { DrizzleClient } from "@/lib/DrizzleClient";
|
||||
import type { ItemUsageData, ItemEffect } from "@/lib/types";
|
||||
import { items } from "@db/schema";
|
||||
import { DrizzleClient } from "@shared/db/DrizzleClient";
|
||||
import type { ItemUsageData, ItemEffect } from "@shared/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";
|
||||
import { ItemType, EffectType } from "@shared/lib/constants";
|
||||
|
||||
// --- Types ---
|
||||
|
||||
@@ -241,3 +241,8 @@ export const handleItemWizardInteraction = async (interaction: Interaction) => {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export const clearDraftSessions = () => {
|
||||
draftSession.clear();
|
||||
console.log("[ItemWizard] All draft item creation sessions cleared.");
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ItemUsageData } from "@/lib/types";
|
||||
import type { ItemUsageData } from "@shared/lib/types";
|
||||
|
||||
export interface DraftItem {
|
||||
name: string;
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from "discord.js";
|
||||
import { createBaseEmbed } from "@lib/embeds";
|
||||
import type { DraftItem } from "./item_wizard.types";
|
||||
import { ItemType } from "@/lib/constants";
|
||||
import { ItemType } from "@shared/lib/constants";
|
||||
|
||||
const getItemTypeOptions = () => [
|
||||
{ label: "Material", value: ItemType.MATERIAL, description: "Used for crafting or trading" },
|
||||
33
bot/modules/admin/update.types.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
export interface RestartContext {
|
||||
channelId: string;
|
||||
userId: string;
|
||||
timestamp: number;
|
||||
runMigrations: boolean;
|
||||
installDependencies: boolean;
|
||||
previousCommit: string;
|
||||
newCommit: string;
|
||||
}
|
||||
|
||||
export interface UpdateCheckResult {
|
||||
needsRootInstall: boolean;
|
||||
needsWebInstall: boolean;
|
||||
needsMigrations: boolean;
|
||||
changedFiles: string[];
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
export interface UpdateInfo {
|
||||
hasUpdates: boolean;
|
||||
branch: string;
|
||||
currentCommit: string;
|
||||
latestCommit: string;
|
||||
commitCount: number;
|
||||
commits: CommitInfo[];
|
||||
}
|
||||
|
||||
export interface CommitInfo {
|
||||
hash: string;
|
||||
message: string;
|
||||
author: string;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js";
|
||||
import { createInfoEmbed, createSuccessEmbed, createWarningEmbed, createErrorEmbed } from "@lib/embeds";
|
||||
import type { UpdateInfo, UpdateCheckResult } from "./update.service";
|
||||
import type { UpdateInfo, UpdateCheckResult } from "./update.types";
|
||||
|
||||
// Constants for UI
|
||||
const LOG_TRUNCATE_LENGTH = 800;
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ButtonInteraction } from "discord.js";
|
||||
import { lootdropService } from "./lootdrop.service";
|
||||
import { lootdropService } from "@shared/modules/economy/lootdrop.service";
|
||||
import { UserError } from "@/lib/errors";
|
||||
import { getLootdropClaimedMessage } from "./lootdrop.view";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ButtonInteraction, MessageFlags } from "discord.js";
|
||||
import { inventoryService } from "@/modules/inventory/inventory.service";
|
||||
import { userService } from "@/modules/user/user.service";
|
||||
import { inventoryService } from "@shared/modules/inventory/inventory.service";
|
||||
import { userService } from "@shared/modules/user/user.service";
|
||||
import { UserError } from "@/lib/errors";
|
||||
|
||||
export async function handleShopInteraction(interaction: ButtonInteraction) {
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Interaction } from "discord.js";
|
||||
import { TextChannel, MessageFlags } from "discord.js";
|
||||
import { config } from "@/lib/config";
|
||||
import { config } from "@shared/lib/config";
|
||||
import { AuroraClient } from "@/lib/BotClient";
|
||||
import { buildFeedbackMessage, getFeedbackModal } from "./feedback.view";
|
||||
import { FEEDBACK_CUSTOM_IDS, type FeedbackType, type FeedbackData } from "./feedback.types";
|
||||
@@ -1,11 +1,11 @@
|
||||
import { levelingService } from "@/modules/leveling/leveling.service";
|
||||
import { economyService } from "@/modules/economy/economy.service";
|
||||
import { userTimers } from "@/db/schema";
|
||||
import { levelingService } from "@shared/modules/leveling/leveling.service";
|
||||
import { economyService } from "@shared/modules/economy/economy.service";
|
||||
import { userTimers } from "@db/schema";
|
||||
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";
|
||||
import type { LootTableItem } from "@shared/lib/types";
|
||||
import { inventoryService } from "@shared/modules/inventory/inventory.service";
|
||||
import { inventory, items } from "@db/schema";
|
||||
import { TimerType, TransactionType, LootType } from "@shared/lib/constants";
|
||||
|
||||
|
||||
// Helper to extract duration in seconds
|
||||
@@ -120,7 +120,7 @@ export const handleLootbox: EffectHandler = async (userId, effect, txFn) => {
|
||||
// Try to fetch item name for the message
|
||||
try {
|
||||
const item = await txFn.query.items.findFirst({
|
||||
where: (items, { eq }) => eq(items.id, winner.itemId!)
|
||||
where: (items: any, { eq }: any) => eq(items.id, winner.itemId!)
|
||||
});
|
||||
if (item) {
|
||||
return winner.message || `You found ${quantity > 1 ? quantity + 'x ' : ''}**${item.name}**!`;
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
import type { Transaction } from "@/lib/types";
|
||||
import type { Transaction } from "@shared/lib/types";
|
||||
|
||||
export type EffectHandler = (userId: string, effect: any, txFn: Transaction) => Promise<string>;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { EmbedBuilder } from "discord.js";
|
||||
import type { ItemUsageData } from "@/lib/types";
|
||||
import { EffectType } from "@/lib/constants";
|
||||
import type { ItemUsageData } from "@shared/lib/types";
|
||||
import { EffectType } from "@shared/lib/constants";
|
||||
|
||||
/**
|
||||
* Inventory entry with item details
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CaseType } from "@/lib/constants";
|
||||
import { CaseType } from "@shared/lib/constants";
|
||||
|
||||
export { CaseType };
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { temporaryRoleService } from "./temp-role.service";
|
||||
import { temporaryRoleService } from "@shared/modules/system/temp-role.service";
|
||||
|
||||
export const schedulerService = {
|
||||
start: () => {
|
||||
@@ -10,7 +10,7 @@ export const schedulerService = {
|
||||
}, 60 * 1000);
|
||||
|
||||
// 2. Terminal Update Loop (every 60s)
|
||||
const { terminalService } = require("@/modules/terminal/terminal.service");
|
||||
const { terminalService } = require("@shared/modules/terminal/terminal.service");
|
||||
setInterval(() => {
|
||||
terminalService.update();
|
||||
}, 60 * 1000);
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
TextChannel,
|
||||
EmbedBuilder
|
||||
} from "discord.js";
|
||||
import { tradeService } from "./trade.service";
|
||||
import { inventoryService } from "@/modules/inventory/inventory.service";
|
||||
import { tradeService } from "@shared/modules/trade/trade.service";
|
||||
import { inventoryService } from "@shared/modules/inventory/inventory.service";
|
||||
import { createErrorEmbed, createWarningEmbed, createSuccessEmbed, createInfoEmbed } from "@lib/embeds";
|
||||
import { UserError } from "@lib/errors";
|
||||
import { getTradeDashboard, getTradeMoneyModal, getItemSelectMenu, getTradeCompletedEmbed } from "./trade.view";
|
||||
@@ -101,7 +101,7 @@ async function handleAddItemClick(interaction: ButtonInteraction, threadId: stri
|
||||
}
|
||||
|
||||
// Slice top 25 for select menu
|
||||
const options = inventory.slice(0, 25).map(entry => ({
|
||||
const options = inventory.slice(0, 25).map((entry: any) => ({
|
||||
label: `${entry.item.name} (${entry.quantity})`,
|
||||
value: entry.item.id.toString(),
|
||||
description: `Rarity: ${entry.item.rarity} `
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ButtonInteraction, MessageFlags } from "discord.js";
|
||||
import { config } from "@/lib/config";
|
||||
import { config } from "@shared/lib/config";
|
||||
import { getEnrollmentSuccessMessage } from "./enrollment.view";
|
||||
import { classService } from "@modules/class/class.service";
|
||||
import { userService } from "@modules/user/user.service";
|
||||
import { classService } from "@shared/modules/class/class.service";
|
||||
import { userService } from "@shared/modules/user/user.service";
|
||||
import { UserError } from "@/lib/errors";
|
||||
import { sendWebhookMessage } from "@/lib/webhookUtils";
|
||||
|
||||
@@ -37,7 +37,7 @@ export async function handleEnrollmentInteraction(interaction: ButtonInteraction
|
||||
|
||||
// 2. Get available classes
|
||||
const allClasses = await classService.getAllClasses();
|
||||
const validClasses = allClasses.filter(c => c.roleId);
|
||||
const validClasses = allClasses.filter((c: any) => c.roleId);
|
||||
|
||||
if (validClasses.length === 0) {
|
||||
throw new UserError("No classes with specified roles found in database.");
|
||||
@@ -1,7 +1,7 @@
|
||||
import { userTimers } from "@/db/schema";
|
||||
import { userTimers } from "@db/schema";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
import { DrizzleClient } from "@/lib/DrizzleClient";
|
||||
import { TimerType } from "@/lib/constants";
|
||||
import { DrizzleClient } from "@shared/db/DrizzleClient";
|
||||
import { TimerType } from "@shared/lib/constants";
|
||||
|
||||
export { TimerType };
|
||||
|
||||
@@ -33,7 +33,7 @@ export const userTimerService = {
|
||||
if (tx) {
|
||||
return await execute(tx);
|
||||
} else {
|
||||
return await DrizzleClient.transaction(async (t) => {
|
||||
return await DrizzleClient.transaction(async (t: any) => {
|
||||
return await execute(t);
|
||||
});
|
||||
}
|
||||
@@ -89,7 +89,7 @@ export const userTimerService = {
|
||||
if (tx) {
|
||||
return await execute(tx);
|
||||
} else {
|
||||
return await DrizzleClient.transaction(async (t) => {
|
||||
return await DrizzleClient.transaction(async (t: any) => {
|
||||
return await execute(t);
|
||||
});
|
||||
}
|
||||
@@ -10,8 +10,8 @@ services:
|
||||
# ports:
|
||||
# - "127.0.0.1:${DB_PORT}:5432"
|
||||
volumes:
|
||||
- ./src/db/data:/var/lib/postgresql/data
|
||||
- ./src/db/log:/var/log/postgresql
|
||||
- ./shared/db/data:/var/lib/postgresql/data
|
||||
- ./shared/db/log:/var/log/postgresql
|
||||
networks:
|
||||
- internal
|
||||
healthcheck:
|
||||
@@ -33,7 +33,7 @@ services:
|
||||
volumes:
|
||||
- .:/app
|
||||
- /app/node_modules
|
||||
- /app/src/web/node_modules
|
||||
- /app/web/node_modules
|
||||
environment:
|
||||
- HOST=0.0.0.0
|
||||
- DB_USER=${DB_USER}
|
||||
@@ -71,7 +71,7 @@ services:
|
||||
volumes:
|
||||
- .:/app
|
||||
- /app/node_modules
|
||||
- /app/src/web/node_modules
|
||||
- /app/web/node_modules
|
||||
environment:
|
||||
- DB_USER=${DB_USER}
|
||||
- DB_PASSWORD=${DB_PASSWORD}
|
||||
@@ -84,7 +84,8 @@ services:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- internal
|
||||
command: bun run db:studio
|
||||
- web
|
||||
command: [ "bun", "x", "drizzle-kit", "studio", "--port", "4983", "--host", "0.0.0.0" ]
|
||||
|
||||
networks:
|
||||
internal:
|
||||
|
||||