Skip to content
This repository was archived by the owner on May 12, 2026. It is now read-only.

Commit f256151

Browse files
author
arsabutispik
committed
refactor(bot): restructure logging, utils, and event architecture
- **Directory Structure**: Reorganized `src/utils` and `src/events` into domain-specific subfolders (logging, jobs, moderation, guilds, channels, etc.) to reduce clutter. - **Logging Refactor**: Split monolithic event handlers (ChannelUpdate, GuildUpdate) into the "Logic/Embeds/Utils" 3-file pattern for better readability and maintainability. - **TypeScript Config**: Fixed path aliases to correctly resolve `@utils`, `@lib`, etc., in WebStorm and build tools. - **Tooling**: Fixed `nodemon` watch scripts and resolved Prisma/Monorepo database connection issues. - **Events**: grouped flat event files into resource-based folders (channels/, members/, etc.) and updated loader logic.
1 parent 47d40a6 commit f256151

88 files changed

Lines changed: 2499 additions & 1957 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/bot/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
},
1212
"scripts": {
1313
"deploy-commands": "tsc && tsc-alias && node --enable-source-maps dist/utils/deploy-commands.js",
14-
"db:deploy": "pnpm -F @repo/database exec prisma migrate deploy",
14+
"db:deploy": "pnpm -F @repo/database exec -- dotenv -e ../../.env -- prisma migrate deploy",
1515
"build": "pnpm run db:deploy && tsc && tsc-alias",
1616
"start": "node --enable-source-maps .",
1717
"validate-translations": "node scripts/ValidateI18nKeys.js",
@@ -50,6 +50,7 @@
5050
"eslint-config-prettier": "^10.1.8",
5151
"eslint-plugin-jsonc": "^2.21.0",
5252
"globals": "^15.15.0",
53+
"nodemon": "^3.1.11",
5354
"prettier": "^3.6.2",
5455
"ts-node": "^10.9.2",
5556
"tsc-alias": "^1.8.16",

apps/bot/src/config-functions/miscConfig.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
} from "discord.js";
77
import { GuildWithLogs, updateGuildConfig } from "@repo/database";
88
import { dynamicChannel, dynamicMessage, waitForMessageComponent } from "./utils.js";
9-
import { localeFlags } from "src/constants/index.js";
9+
import { localeFlags } from "@constants";
1010
import { TFunction } from "i18next";
1111

1212
export async function miscConfig(interaction: ChatInputCommandInteraction<"cached">, guildData: GuildWithLogs) {

apps/bot/src/config-functions/moderationConfig.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from "discord.js";
1111
import { GuildWithLogs, updateGuildConfig } from "@repo/database";
1212
import { waitForMessageComponent, dynamicChannel, dynamicRole } from "./utils.js";
13-
import { logger } from "src/lib/Logger.js";
13+
import { logger } from "@lib";
1414
import { TFunction } from "i18next";
1515

1616
export async function moderationConfig(interaction: ChatInputCommandInteraction<"cached">, guildData: GuildWithLogs) {

apps/bot/src/config-functions/roleConfig.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ActionRowBuilder, ChatInputCommandInteraction, StringSelectMenuBuilder } from "discord.js";
22
import { dynamicRole, waitForMessageComponent } from "./utils.js";
3-
import { RoleType } from "src/types/index.js";
3+
import { RoleType } from "@types";
44
import { GuildWithLogs } from "@repo/database";
55

66
export async function roleConfig(interaction: ChatInputCommandInteraction<"cached">, guildData: GuildWithLogs) {

apps/bot/src/config-functions/utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ import {
1515
TextInputBuilder,
1616
TextInputStyle,
1717
} from "discord.js";
18-
import { logger } from "src/lib/index.js";
18+
import { logger } from "@lib";
1919
import { GuildWithLogs, updateGuildConfig } from "@repo/database";
20-
import { trimString, getCurrentValue, getUpdatePayload } from "src/utils/index.js";
20+
import { trimString, getCurrentValue, getUpdatePayload } from "@utils";
2121
import { TFunction } from "i18next";
22-
import { DbConfigKey } from "src/constants/index.js";
22+
import { DbConfigKey } from "@constants";
2323
export async function waitForMessageComponent(
2424
interaction: ChatInputCommandInteraction<"cached"> | StringSelectMenuInteraction<"cached">,
2525
actionRow: ActionRowBuilder<StringSelectMenuBuilder>,

apps/bot/src/events/autoModerationActionExecution.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import type { EventBase } from "src/types/index.js";
1+
import type { EventBase } from "@types";
22
import { AutoModerationActionType, Events, User } from "discord.js";
3-
import { modLog } from "src/utils/index.js";
3+
import { modLog } from "@utils";
44
import dayjs from "dayjs";
55

66
export default {
Lines changed: 3 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1-
import { EventBase } from "src/types/index.js";
1+
import { EventBase } from "@types";
22
import {
3-
AuditLogEvent,
4-
ChannelType,
5-
EmbedBuilder,
63
Events,
7-
NonThreadGuildBasedChannel,
8-
time,
9-
TimestampStyles,
104
} from "discord.js";
115
import { getOrCreateGuild, GuildWithLogs } from "@repo/database";
12-
import { returnWebhook, sleep, WebhookType } from "src/utils/index.js";
13-
import { logger } from "src/lib/index.js";
6+
import { logChannelCreate } from "@utils";
147

158
export default {
169
name: Events.ChannelCreate,
@@ -20,58 +13,4 @@ export default {
2013
if (!guildConfig) return;
2114
await logChannelCreate(channel, guildConfig);
2215
},
23-
} satisfies EventBase<Events.ChannelCreate>;
24-
25-
async function logChannelCreate(channel: NonThreadGuildBasedChannel, guildConfig: GuildWithLogs) {
26-
if (!guildConfig.logConfig?.channelLogsChannelId) return;
27-
const t = channel.client.i18next.getFixedT(guildConfig.language, "events", "channelCreate");
28-
const logChannel = channel.guild.channels.cache.get(guildConfig.logConfig?.channelLogsChannelId);
29-
if (logChannel?.type !== ChannelType.GuildText) return;
30-
31-
await sleep(2000); // Wait for 2 seconds to ensure audit logs are updated
32-
33-
const auditLogs = await channel.guild
34-
.fetchAuditLogs({
35-
limit: 1,
36-
type: AuditLogEvent.ChannelCreate,
37-
})
38-
.catch(() => null);
39-
40-
const logEntry = auditLogs?.entries.first();
41-
const executor = logEntry && logEntry.target?.id === channel.id ? logEntry.executor : null;
42-
43-
const embed = new EmbedBuilder()
44-
.setColor("Green")
45-
.setTitle(t("embed.title"))
46-
.setDescription(
47-
t("embed.description", {
48-
channel: channel.toString(), // Explicitly stringify to <#ID>
49-
channel_type: t(`channel_types.${channel.type}`),
50-
timestamp: time(channel.createdAt, TimestampStyles.LongDateShortTime),
51-
}),
52-
)
53-
.setThumbnail(channel.guild.iconURL() ?? null)
54-
.setTimestamp()
55-
.setFooter({
56-
text: executor?.tag ?? t("unknown_executor"),
57-
iconURL: executor?.displayAvatarURL() ?? undefined,
58-
});
59-
60-
const webhook = await returnWebhook(channel.client, logChannel, channel.guildId, guildConfig, {
61-
id: guildConfig.logConfig.channelLogsWebhookId,
62-
type: WebhookType.CHANNEL_LOGS,
63-
});
64-
if (!webhook) return;
65-
await webhook
66-
.send({
67-
embeds: [embed],
68-
})
69-
.catch((error) => {
70-
logger.log({
71-
level: "error",
72-
error,
73-
message: `Failed to send channelCreate embed in ${channel.guild.name} (${channel.guild.id})`,
74-
channel: logChannel.id,
75-
});
76-
});
77-
}
16+
} satisfies EventBase<Events.ChannelCreate>;
Lines changed: 4 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1-
import { EventBase } from "src/types/index.js";
1+
import { EventBase } from "@types";
22
import {
3-
AuditLogEvent,
4-
ChannelType,
5-
EmbedBuilder,
63
Events,
7-
NonThreadGuildBasedChannel,
8-
time,
9-
TimestampStyles,
104
} from "discord.js";
11-
import { getOrCreateGuild, GuildWithLogs } from "@repo/database";
12-
import { returnWebhook, sleep, WebhookType } from "src/utils/index.js";
13-
import { logger } from "src/lib/index.js";
5+
import { getOrCreateGuild } from "@repo/database";
6+
import { logsChannelDelete } from "@utils";
147

158
export default {
169
name: Events.ChannelDelete,
@@ -21,57 +14,4 @@ export default {
2114
if (!guildConfig) return;
2215
await logsChannelDelete(channel, guildConfig);
2316
},
24-
} satisfies EventBase<Events.ChannelDelete>;
25-
26-
async function logsChannelDelete(channel: NonThreadGuildBasedChannel, guildConfig: GuildWithLogs) {
27-
if (!guildConfig.logConfig?.channelLogsChannelId) return;
28-
const t = channel.client.i18next.getFixedT(guildConfig.language, "events", "channelDelete");
29-
const logChannel = channel.guild.channels.cache.get(guildConfig.logConfig.channelLogsChannelId);
30-
if (logChannel?.type !== ChannelType.GuildText) return;
31-
32-
await sleep(2000); // Wait for 2 seconds to ensure audit logs are updated
33-
34-
const auditLogs = await channel.guild
35-
.fetchAuditLogs({
36-
limit: 1,
37-
type: AuditLogEvent.ChannelDelete,
38-
})
39-
.catch(() => null);
40-
const logEntry = auditLogs?.entries.first();
41-
const executor = logEntry && logEntry.target?.id === channel.id ? logEntry.executor : null;
42-
43-
const embed = new EmbedBuilder()
44-
.setColor("Red")
45-
.setTitle(t("embed.title"))
46-
.setDescription(
47-
t("embed.description", {
48-
channel: channel,
49-
channel_type: t(`channel_types.${channel.type}`),
50-
timestamp: time(channel.createdAt, TimestampStyles.LongDateShortTime),
51-
}),
52-
)
53-
.setThumbnail(channel.guild.iconURL() ?? null)
54-
.setTimestamp()
55-
.setFooter({
56-
text: executor?.tag ?? t("unknown_executor"),
57-
iconURL: executor?.displayAvatarURL() ?? undefined,
58-
});
59-
60-
const webhook = await returnWebhook(channel.client, logChannel, channel.guildId, guildConfig, {
61-
id: guildConfig.logConfig.channelLogsWebhookId,
62-
type: WebhookType.CHANNEL_LOGS,
63-
});
64-
if (!webhook) return;
65-
await webhook
66-
.send({
67-
embeds: [embed],
68-
})
69-
.catch((error) => {
70-
logger.log({
71-
level: "error",
72-
error,
73-
message: `Failed to send channelDelete embed in ${channel.guild.name} (${channel.guild.id})`,
74-
channelId: logChannel.id,
75-
});
76-
});
77-
}
17+
} satisfies EventBase<Events.ChannelDelete>;

0 commit comments

Comments
 (0)